# PHP - Deserialización + Clases de Autoload {{#include ../../banners/hacktricks-training.md}} Primero, debes verificar qué son [**Clases de Autoload**](https://www.php.net/manual/en/language.oop5.autoload.php). ## Deserialización de PHP + spl_autoload_register + LFI/Gadget Estamos en una situación donde encontramos una **deserialización de PHP en una webapp** sin **ninguna** biblioteca vulnerable a gadgets dentro de **`phpggc`**. Sin embargo, en el mismo contenedor había una **webapp de composer diferente con bibliotecas 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 biblioteca con un gadget** de la webapp vulnerable a deserialización. Pasos: - Has encontrado una **deserialización** y **no hay ningún gadget** en el código de la aplicación actual - Puedes abusar de una función **`spl_autoload_register`** como la siguiente para **cargar cualquier archivo local con extensión `.php`** - Para eso, usas 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:{}`** ```php 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; } }); ``` > [!TIP] > Si tienes una **carga de archivos** y puedes subir un archivo con **extensión `.php`**, podrías **abusar de esta funcionalidad directamente** y obtener RCE. En mi caso, no tenía nada como eso, pero había dentro del **mismo contenedor** otra página web de composer con una **biblioteca vulnerable a un gadget `phpggc`**. - Para cargar esta otra biblioteca, primero necesitas **cargar el cargador de composer de esa otra aplicación web** (porque el de la aplicación actual no accederá a las bibliotecas 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 app**, así que es hora de **`generar la carga`** **phpgcc** para usar. En mi caso, utilicé **`Guzzle/FW1`**, que me permitió **escribir cualquier archivo dentro del sistema de archivos**. - NOTA: La **carga generada no funcionaba**, para que funcionara **modifiqué** esa carga **`chain.php`** de phpggc y establecí **todos los atributos** de las clases **de privado a público**. Si no, después de deserializar la cadena, los atributos de los objetos creados no tenían ningún valor. - Ahora tenemos la forma de **cargar el cargador de composer de la otra app** y tener una **carga phpggc que funciona**, pero necesitamos **hacer esto en la MISMA SOLICITUD para que el cargador se cargue cuando se use el gadget**. Para eso, envié un array serializado con ambos objetos como: - Puedes ver **primero el cargador siendo cargado y luego la carga**. ```php 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:"";}}}s:10:"strictMode";N;s:8:"filename";s:10:"/tmp/a.php";s:19:"storeSessionCookies";b:1;}} ``` - Ahora, podemos **crear y escribir un archivo**, sin embargo, el usuario **no pudo escribir en ninguna carpeta dentro del servidor web**. Así que, como puedes ver en la carga útil, PHP llama a **`system`** con algún **base64** que se crea en **`/tmp/a.php`**. Luego, podemos **reutilizar el primer tipo de carga útil** que usamos como LFI para cargar el cargador de composer de la otra aplicación web **para cargar el archivo generado `/tmp/a.php`**. Simplemente agrégalo al gadget de deserialización: ```php 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:"";}}}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 de la carga útil** - **Cargar el autoload de composer** de una webapp diferente en el mismo contenedor - **Cargar un gadget phpggc** para abusar de una biblioteca de la otra webapp (la webapp inicial vulnerable a la deserialización no tenía ningún gadget en sus bibliotecas) - El gadget **creará un archivo con una carga útil PHP** en /tmp/a.php con comandos maliciosos (el usuario de la webapp no puede escribir en ninguna carpeta de ninguna webapp) - La parte final de nuestra carga útil utilizará **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. {{#include ../../banners/hacktricks-training.md}}