PHP Tricks
Last updated
Last updated
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Uzyskaj perspektywę hakera na swoje aplikacje webowe, sieć i chmurę
Znajdź i zgłoś krytyczne, wykorzystywalne luki w zabezpieczeniach, które mają rzeczywisty wpływ na biznes. Użyj naszych 20+ niestandardowych narzędzi, aby zmapować powierzchnię ataku, znaleźć problemy z bezpieczeństwem, które pozwalają na eskalację uprawnień, i użyj zautomatyzowanych exploitów, aby zebrać niezbędne dowody, przekształcając swoją ciężką pracę w przekonujące raporty.
To również dotyczy ciasteczek phpMyAdmin.
Ciasteczka:
Lokalizacje:
Jeśli ==
jest używane w PHP, istnieją nieoczekiwane przypadki, w których porównanie nie zachowuje się zgodnie z oczekiwaniami. Dzieje się tak, ponieważ "==" porównuje tylko wartości przekształcone do tego samego typu, jeśli chcesz również porównać, że typ porównywanych danych jest taki sam, musisz użyć ===
.
Tabele porównań PHP: https://www.php.net/manual/en/types.comparisons.php
"string" == 0 -> True
Ciąg znaków, który nie zaczyna się od liczby, jest równy liczbie
"0xAAAA" == "43690" -> True
Ciągi składające się z liczb w formacie dziesiętnym lub szesnastkowym mogą być porównywane z innymi liczbami/ciągami z wynikiem True, jeśli liczby były takie same (liczby w ciągu są interpretowane jako liczby)
"0e3264578" == 0 --> True
Ciąg zaczynający się od "0e" i następnie cokolwiek będzie równy 0
"0X3264578" == 0X --> True
Ciąg zaczynający się od "0" i następnie dowolna litera (X może być dowolną literą) i następnie cokolwiek będzie równy 0
"0e12334" == "0" --> True
To jest bardzo interesujące, ponieważ w niektórych przypadkach możesz kontrolować wejście ciągu "0" i niektóre treści, które są haszowane i porównywane z nim. Dlatego, jeśli możesz dostarczyć wartość, która stworzy hash zaczynający się od "0e" i bez żadnej litery, możesz obejść porównanie. Możesz znaleźć już haszowane ciągi w tym formacie tutaj: https://github.com/spaze/hashes
"X" == 0 --> True
Dowolna litera w ciągu jest równa int 0
Więcej informacji w https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09
Typ Juggling również wpływa na funkcję in_array()
domyślnie (musisz ustawić trzeci argument na true, aby dokonać ścisłego porównania):
Jeśli ta funkcja jest używana do jakiejkolwiek weryfikacji uwierzytelnienia (jak sprawdzanie hasła) i użytkownik kontroluje jedną stronę porównania, może wysłać pustą tablicę zamiast ciągu jako wartość hasła (https://example.com/login.php/?username=admin&password[]=
) i obejść tę weryfikację:
Ten sam błąd występuje z strcasecmp()
Nawet jeśli ===
jest używane, mogą wystąpić błędy, które sprawiają, że porównanie jest podatne na mieszanie typów. Na przykład, jeśli porównanie konwertuje dane na inny typ obiektu przed porównaniem:
preg_match()
może być użyty do walidacji danych wejściowych użytkownika (sprawdza, czy jakiekolwiek słowo/regex z czarnej listy jest obecne w danych wejściowych użytkownika, a jeśli nie, kod może kontynuować swoje wykonanie).
Jednakże, przy delimitacji początku regexp preg_match()
sprawdza tylko pierwszą linię danych wejściowych użytkownika, więc jeśli w jakiś sposób możesz wysłać dane wejściowe w kilku liniach, możesz być w stanie obejść tę kontrolę. Przykład:
Aby obejść tę kontrolę, możesz wysłać wartość z nowymi liniami zakodowanymi w URL (%0A
) lub jeśli możesz wysłać dane JSON, wyślij je w kilku liniach:
Znajdź przykład tutaj: https://ramadistra.dev/fbctf-2019-rceservice
(To obejście było podobno testowane na PHP 5.2.5 i nie mogłem go uruchomić na PHP 7.3.15)
Jeśli możesz wysłać do preg_match()
ważny bardzo duży input, nie będzie w stanie go przetworzyć i będziesz mógł obejść kontrolę. Na przykład, jeśli czarna lista dotyczy JSON-a, możesz wysłać:
From: https://medium.com/bugbountywriteup/solving-each-and-every-fb-ctf-challenge-part-1-4bce03e2ecb0
Sztuczka z: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223 i https://mizu.re/post/pong
Krótko mówiąc, problem występuje, ponieważ funkcje preg_*
w PHP opierają się na bibliotece PCRE. W PCRE niektóre wyrażenia regularne są dopasowywane przy użyciu wielu wywołań rekurencyjnych, co zużywa dużo miejsca na stosie. Można ustawić limit na liczbę dozwolonych rekurencji, ale w PHP ten limit domyślnie wynosi 100.000, co jest więcej niż mieści się na stosie.
Ten wątek na Stackoverflow również został podlinkowany w poście, w którym bardziej szczegółowo omawiano ten problem. Nasze zadanie było teraz jasne:
Wysłać dane wejściowe, które spowodują, że regex wykona 100_000+ rekurencji, powodując SIGSEGV, co sprawi, że funkcja preg_match()
zwróci false
, a aplikacja pomyśli, że nasze dane wejściowe nie są złośliwe, zaskakując na końcu ładunku czymś w stylu {system(<verybadcommand>)}
w celu uzyskania SSTI --> RCE --> flagi :).
Cóż, w terminach regex nie wykonujemy faktycznie 100k "rekurencji", ale zamiast tego liczymy "kroki cofania", które, jak stwierdza dokumentacja PHP, domyślnie wynoszą 1_000_000 (1M) w zmiennej pcre.backtrack_limit
.\ Aby to osiągnąć, 'X'*500_001
spowoduje 1 milion kroków cofania (500k do przodu i 500k do tyłu):
Jeśli PHP przekierowuje na inną stronę, ale żadna funkcja die
lub exit
nie jest wywoływana po ustawieniu nagłówka Location
, PHP kontynuuje wykonywanie i dodaje dane do treści:
Sprawdź:
File Inclusion/Path traversalregister_globals: W PHP < 4.1.1.1 lub w przypadku błędnej konfiguracji, register_globals może być aktywne (lub ich zachowanie jest naśladowane). Oznacza to, że w zmiennych globalnych takich jak $_GET, jeśli mają wartość np. $_GET["param"]="1234", możesz uzyskać do nich dostęp przez $param. Dlatego, wysyłając parametry HTTP, możesz nadpisać zmienne używane w kodzie.
Ciasteczka PHPSESSION tej samej domeny są przechowywane w tym samym miejscu, dlatego jeśli w obrębie domeny różne ciasteczka są używane w różnych ścieżkach, możesz sprawić, że jedna ścieżka uzyska dostęp do ciasteczka innej ścieżki, ustawiając wartość ciasteczka innej ścieżki. W ten sposób, jeśli obie ścieżki uzyskują dostęp do zmiennej o tej samej nazwie, możesz sprawić, że wartość tej zmiennej w path1 będzie miała zastosowanie w path2. A następnie path2 uzna za ważne zmienne z path1 (nadając ciasteczku nazwę, która odpowiada jej w path2).
Kiedy masz nazwy użytkowników użytkowników maszyny. Sprawdź adres: /~<USERNAME>, aby zobaczyć, czy katalogi php są aktywowane.
Funkcje te są zazwyczaj używane w PHP do generowania hashy z haseł oraz do sprawdzania, czy hasło jest poprawne w porównaniu z hashem.
Obsługiwane algorytmy to: PASSWORD_DEFAULT
i PASSWORD_BCRYPT
(zaczyna się od $2y$
). Zauważ, że PASSWORD_DEFAULT często jest tym samym co PASSWORD_BCRYPT. A obecnie, PASSWORD_BCRYPT ma ograniczenie rozmiaru wejścia do 72 bajtów. Dlatego, gdy próbujesz zhashować coś większego niż 72 bajty za pomocą tego algorytmu, tylko pierwsze 72B zostanie użyte:
Z tego wątku na Twitterze można zobaczyć, że wysyłając więcej niż 1000 parametrów GET lub 1000 parametrów POST lub 20 plików, PHP nie ustawi nagłówków w odpowiedzi.
Pozwala to na obejście na przykład nagłówków CSP ustawianych w kodach takich jak:
Jeśli strona PHP wyświetla błędy i zwraca niektóre dane wprowadzone przez użytkownika, użytkownik może sprawić, że serwer PHP zwróci treść wystarczająco długą, aby podczas próby dodania nagłówków do odpowiedzi serwer zgłosił błąd. W następującym scenariuszu atakujący spowodował, że serwer zgłosił kilka dużych błędów, a jak widać na ekranie, gdy PHP próbowało zmodyfikować informacje o nagłówkach, nie mogło (na przykład nagłówek CSP nie został wysłany do użytkownika):
Sprawdź stronę:
PHP SSRFsystem("ls"); `ls`; shell_exec("ls");
Sprawdź to dla bardziej przydatnych funkcji PHP
Aby wykonać kod w argumencie "replace", potrzebne jest przynajmniej jedno dopasowanie. Ta opcja preg_replace została wycofana od PHP 5.5.0.
Ta funkcja w php pozwala na wykonywanie kodu zapisanego w ciągu w celu zwrócenia true lub false (a w zależności od tego zmienić wykonanie). Zazwyczaj zmienna użytkownika będzie wstawiana w środek ciągu. Na przykład:
assert("strpos($_GET['page']),'..') === false")
--> W tym przypadku, aby uzyskać RCE, możesz zrobić:
Będziesz musiał złamać składnię kodu, dodać swój ładunek, a następnie naprawić to z powrotem. Możesz użyć operacji logicznych takich jak "and" lub "%26%26" lub "|". Zauważ, że "or", "||" nie działa, ponieważ jeśli pierwszy warunek jest prawdziwy, nasz ładunek nie zostanie wykonany. W ten sam sposób ";" nie działa, ponieważ nasz ładunek nie zostanie wykonany.
Inną opcją jest dodanie do ciągu wykonania polecenia: '.highlight_file('.passwd').'
Inną opcją (jeśli masz wewnętrzny kod) jest modyfikacja niektórej zmiennej, aby zmienić wykonanie: $file = "hola"
Funkcja ta jest używana do sortowania tablicy elementów za pomocą określonej funkcji. Aby nadużyć tej funkcji:
Możesz również użyć // do komentowania reszty kodu.
Aby odkryć liczbę nawiasów, które musisz zamknąć:
?order=id;}//
: otrzymujemy komunikat o błędzie (Parse error: syntax error, unexpected ';'
). Prawdopodobnie brakuje nam jednego lub więcej nawiasów.
?order=id);}//
: otrzymujemy ostrzeżenie. To wydaje się w porządku.
?order=id));}//
: otrzymujemy komunikat o błędzie (Parse error: syntax error, unexpected ')' i
). Prawdopodobnie mamy za dużo nawiasów zamykających.
Jeśli możesz przesłać .htaccess, to możesz skonfigurować kilka rzeczy, a nawet wykonać kod (konfigurując, że pliki z rozszerzeniem .htaccess mogą być wykonywane).
Różne powłoki .htaccess można znaleźć tutaj
Jeśli znajdziesz lukę, która pozwala ci modyfikować zmienne środowiskowe w PHP (i inną, aby przesyłać pliki, chociaż z większym badaniem może to być możliwe do obejścia), możesz nadużyć tego zachowania, aby uzyskać RCE.
LD_PRELOAD
: Ta zmienna środowiskowa pozwala na ładowanie dowolnych bibliotek podczas wykonywania innych binarnych (chociaż w tym przypadku może to nie działać).
PHPRC
: Instrukcja dla PHP, gdzie znaleźć plik konfiguracyjny, zazwyczaj nazywany php.ini
. Jeśli możesz przesłać własny plik konfiguracyjny, użyj PHPRC
, aby wskazać PHP na niego. Dodaj wpis auto_prepend_file
, określający drugi przesłany plik. Ten drugi plik zawiera normalny kod PHP, który jest następnie wykonywany przez środowisko PHP przed jakimkolwiek innym kodem.
Prześlij plik PHP zawierający nasz shellcode
Prześlij drugi plik, zawierający dyrektywę auto_prepend_file
, instruującą preprocesor PHP do wykonania pliku, który przesłaliśmy w kroku 1
Ustaw zmienną PHPRC
na plik, który przesłaliśmy w kroku 2.
Uzyskaj więcej informacji na temat wykonania tego łańcucha z oryginalnego raportu.
PHPRC - inna opcja
Jeśli nie możesz przesyłać plików, możesz użyć w FreeBSD "pliku" /dev/fd/0
, który zawiera stdin
, będąc treścią żądania wysłanego do stdin
:
curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'
Lub aby uzyskać RCE, włącz allow_url_include
i dodaj plik z kodem PHP w base64:
curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary $'allow_url_include=1\nauto_prepend_file="data://text/plain;base64,PD8KICAgcGhwaW5mbygpOwo/Pg=="'
Technika z tego raportu.
Serwer WWW analizuje żądania HTTP i przekazuje je do skryptu PHP wykonującego żądanie takie jak http://host/cgi.php?foo=bar
jako php.exe cgi.php foo=bar
, co pozwala na wstrzyknięcie parametrów. To pozwoli na wstrzyknięcie następujących parametrów, aby załadować kod PHP z treści:
Ponadto, możliwe jest wstrzyknięcie parametru "-" za pomocą znaku 0xAD z powodu późniejszej normalizacji PHP. Sprawdź przykład exploita z tego posta:
W tym poście można znaleźć świetne pomysły na generowanie kodu PHP w stylu brain fuck z bardzo ograniczoną liczbą dozwolonych znaków. Ponadto zaproponowano również interesujący sposób na wykonywanie funkcji, które pozwoliły im obejść kilka kontroli:
Sprawdź, czy możesz wstawić kod w wywołania tych funkcji (z tutaj):
Jeśli debugujesz aplikację PHP, możesz globalnie włączyć drukowanie błędów w /etc/php5/apache2/php.ini
, dodając display_errors = On
i zrestartować apache: sudo systemctl restart apache2
Możesz użyć web www.unphp.net do deobfuskacji kodu php.
Wrappery PHP i protokoły mogą pozwolić ci na obejście ochrony zapisu i odczytu w systemie i jego kompromitację. Aby uzyskać więcej informacji, sprawdź tę stronę.
Jeśli widzisz, że Xdebug jest włączony w wyjściu phpconfig()
, powinieneś spróbować uzyskać RCE za pomocą https://github.com/nqxcode/xdebug-exploit
Jeśli na stronie możesz utworzyć nowy obiekt dowolnej klasy, możesz uzyskać RCE, sprawdź następującą stronę, aby się dowiedzieć:
PHP - RCE abusing object creation: new $_GET["a"]($_GET["b"])https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/
Zgodnie z tym opisem możliwe jest wygenerowanie łatwego kodu powłoki w ten sposób:
Więc, jeśli możesz wykonać dowolny PHP bez cyfr i liter możesz wysłać żądanie takie jak poniższe, wykorzystując ten ładunek do wykonania dowolnego PHP:
Aby uzyskać bardziej szczegółowe wyjaśnienie, sprawdź https://ctf-wiki.org/web/php/php/#preg_match
Uzyskaj perspektywę hakera na swoje aplikacje webowe, sieć i chmurę
Znajdź i zgłoś krytyczne, wykorzystywalne luki z rzeczywistym wpływem na biznes. Użyj naszych 20+ niestandardowych narzędzi, aby zmapować powierzchnię ataku, znaleźć problemy z bezpieczeństwem, które pozwalają na eskalację uprawnień, oraz użyj zautomatyzowanych exploitów do zbierania niezbędnych dowodów, przekształcając swoją ciężką pracę w przekonujące raporty.
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)