Out of bound when verify signature of zip phar in phar_parse_zipfile
| Sec Bug #72928 | Out of bound when verify signature of zip phar in phar_parse_zipfile | ||||
|---|---|---|---|---|---|
| Submitted: | 2016-08-23 07:53 UTC | Modified: | 2016-09-16 13:39 UTC | ||
| From: | nguyenvuhoang199321 at gmail dot com | Assigned: | stas (profile) | ||
| Status: | Closed | Package: | PHAR related | ||
| PHP Version: | 5.6.25 | OS: | ALL | ||
| Private report: | No | CVE-ID: | 2016-7414 | ||
[2016-08-23 07:53 UTC] nguyenvuhoang199321 at gmail dot com
Description:
------------
There was a security code in phar_parse_zipfile
```
sig = (char *) emalloc(entry.uncompressed_filesize);
read = php_stream_read(fp, sig, entry.uncompressed_filesize);
if (read != entry.uncompressed_filesize) {
php_stream_close(sigfile);
efree(sig);
PHAR_ZIP_FAIL("signature cannot be read");
}
mydata->sig_flags = PHAR_GET_32(sig);
if (FAILURE == phar_verify_signature(sigfile,
php_stream_tell(sigfile),
mydata->sig_flags,
sig + 8,
entry.uncompressed_filesize - 8,
fname,
&mydata->signature,
&mydata->sig_len,
error)
) {
```
There are no checking *entry.uncompressed_filesize* attacker can create a signature.bin with size less than 8 and then this value is passed to *phar_verify_signature* as sig_len as you can see `entry.uncompressed_filesize - 8` as result sig_len is overflow.
And the third param is sig buffer as you can see `sig + 8`, because *entry.uncompressed_filesize* is less than 8 by default emalloc will return 16 bytes this result may lead to heap out of bound.
phar zip file : https://drive.google.com/file/d/0B0D1DYQpkA9Ud3I2OFlfeFRqbEU/view?usp=sharing
Test script:
---------------
<?php
$phar = new PharData('phars/signature.zip');
var_dump($phar);
?>
Actual result:
--------------
This debug step i will show the malicious parse phar zip.
Set breakpoint at line 419 in zip.c
[----------------------------------registers-----------------------------------]
RAX: 0x4
RBX: 0x13
RCX: 0x0
RDX: 0x1a3
RSI: 0xf2cc38 ("/home/vagrant/Sources_Ext/audit/src/php7.0-7.0.8/ext/phar/zip.c")
RDI: 0x4
RBP: 0x7fffffffa870 --> 0x7fffffffadb0 --> 0x7fffffffae50 --> 0x7fffffffaec0 --> 0x7fffffffaf50 --> 0x7fffffffb040 --> 0x7fffffffb090 --> 0x7fffffffb0c0 --> 0x7fffffffb100 --> 0x7fffffffb210 --> 0x7fffffffd510 --> 0x7fffffffe890 --> 0x7fffffffe9e0 --> 0xa1a0a0 (<__libc_csu_init>: push r15)
RSP: 0x7ffffffea600 --> 0x7fffffffafa0 --> 0x0
RIP: 0x6ed786 (<phar_parse_zipfile+7788>: call 0x911568 <_emalloc>)
R8 : 0x0
R9 : 0xf2cc38 ("/home/vagrant/Sources_Ext/audit/src/php7.0-7.0.8/ext/phar/zip.c")
R10: 0x5
R11: 0x206
R12: 0x42c280 (<_start>: xor ebp,ebp)
R13: 0x7fffffffeac0 --> 0x2
R14: 0x7ffff3e14030 --> 0x7ffff3e7e040 --> 0x9a66aa (<ZEND_DO_FCALL_SPEC_HANDLER>: push rbp)
R15: 0x7ffff3e7e040 --> 0x9a66aa (<ZEND_DO_FCALL_SPEC_HANDLER>: push rbp)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x6ed777 <phar_parse_zipfile+7773>: mov edx,0x1a3
0x6ed77c <phar_parse_zipfile+7778>: lea rsi,[rip+0x83f4b5] # 0xf2cc38
0x6ed783 <phar_parse_zipfile+7785>: mov rdi,rax
=> 0x6ed786 <phar_parse_zipfile+7788>: call 0x911568 <_emalloc>
0x6ed78b <phar_parse_zipfile+7793>: mov QWORD PTR [rbp-0x101d0],rax
0x6ed792 <phar_parse_zipfile+7800>: mov eax,DWORD PTR [rbp-0x10170]
0x6ed798 <phar_parse_zipfile+7806>: mov edx,eax
0x6ed79a <phar_parse_zipfile+7808>: mov rcx,QWORD PTR [rbp-0x101d0]
Guessed arguments:
arg[0]: 0x4
arg[1]: 0xf2cc38 ("/home/vagrant/Sources_Ext/audit/src/php7.0-7.0.8/ext/phar/zip.c")
arg[2]: 0x1a3
arg[3]: 0x0
arg[4]: 0x0
[------------------------------------stack-------------------------------------]
0000| 0x7ffffffea600 --> 0x7fffffffafa0 --> 0x0
0008| 0x7ffffffea608 --> 0x7fffffffaeb0 --> 0x0
0016| 0x7ffffffea610 --> 0x0
0024| 0x7ffffffea618 --> 0x2900000000 ('')
0032| 0x7ffffffea620 --> 0x7ffff3e70088 ("/vagrant_extend/audit/phars/signature.zip")
0040| 0x7ffffffea628 --> 0x7ffff3e5fa00 --> 0x12bf720 --> 0x8d6fc6 (<php_stdiop_write>: push rbp)
0048| 0x7ffffffea630 --> 0x0
0056| 0x7ffffffea638 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00000000006ed786 419 sig = (char *) emalloc(entry.uncompressed_filesize);
gdb-peda$ p entry.uncompressed_filesize
$20 = 0x4
I create signature.bin with size is 4, after that i will step to phar_verify_signature and you will see param is passed to phar_verify_signature.
[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffafa0 --> 0x0
RBX: 0x10
RCX: 0x7ffff3e710a8 --> 0x4
RDX: 0x10
RSI: 0x0
RDI: 0x7ffff3e5fb40 --> 0x12bf720 --> 0x8d6fc6 (<php_stdiop_write>: push rbp)
RBP: 0x7ffffffea5d0 --> 0x7fffffffa870 --> 0x7fffffffadb0 --> 0x7fffffffae50 --> 0x7fffffffaec0 --> 0x7fffffffaf50 --> 0x7fffffffb040 --> 0x7fffffffb090 --> 0x7fffffffb0c0 --> 0x7fffffffb100 --> 0x7fffffffb210 --> 0x7fffffffd510 --> 0x7fffffffe890 --> 0x7fffffffe9e0 --> 0xa1a0a0 (<__libc_csu_init>: push r15)
RSP: 0x7ffffffea000 --> 0x7fffffffafa0 --> 0x0
RIP: 0x6e3c77 (<phar_verify_signature+85>: mov rax,QWORD PTR fs:0x28)
R8 : 0xfffffffc
R9 : 0x7ffff3e70088 ("/vagrant_extend/audit/phars/signature.zip")
R10: 0x4
R11: 0x246
R12: 0x7ffff3e710a8 --> 0x4
R13: 0x7ffff3e79120 --> 0x0
R14: 0x7ffff3e79118 --> 0x0
R15: 0xfffffffc
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x6e3c65 <phar_verify_signature+67>: mov QWORD PTR [rbp-0x5c8],rax
0x6e3c6c <phar_verify_signature+74>: mov rax,QWORD PTR [rbp+0x20]
0x6e3c70 <phar_verify_signature+78>: mov QWORD PTR [rbp-0x5d0],rax
=> 0x6e3c77 <phar_verify_signature+85>: mov rax,QWORD PTR fs:0x28
0x6e3c80 <phar_verify_signature+94>: mov QWORD PTR [rbp-0x8],rax
0x6e3c84 <phar_verify_signature+98>: xor eax,eax
0x6e3c86 <phar_verify_signature+100>: mov rax,QWORD PTR [rbp-0x598]
0x6e3c8d <phar_verify_signature+107>: mov edx,0x0
[------------------------------------stack-------------------------------------]
0000| 0x7ffffffea000 --> 0x7fffffffafa0 --> 0x0
0008| 0x7ffffffea008 --> 0x7ffff3e79118 --> 0x0
0016| 0x7ffffffea010 --> 0x7ffff3e79120 --> 0x0
0024| 0x7ffffffea018 --> 0x7ffff3e70088 ("/vagrant_extend/audit/phars/signature.zip")
0032| 0x7ffffffea020 --> 0x7ffff3e710a8 --> 0x4
0040| 0x7ffffffea028 --> 0x10fffffffc
0048| 0x7ffffffea030 --> 0x0
0056| 0x7ffffffea038 --> 0x7ffff3e5fb40 --> 0x12bf720 --> 0x8d6fc6 (<php_stdiop_write>: push rbp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 11, phar_verify_signature (fp=0x7ffff3e5fb40, end_of_phar=0x0,
sig_type=0x10, sig=0x7ffff3e710a8 "\004", sig_len=0xfffffffc,
fname=0x7ffff3e70088 "/vagrant_extend/audit/phars/signature.zip",
signature=0x7ffff3e79120, signature_len=0x7ffff3e79118, error=0x7fffffffafa0)
at /home/vagrant/Sources_Ext/audit/src/php7.0-7.0.8/ext/phar/util.c:1484
1484 {
As you can see both value sig and sig_len may lead to heap out of bound
Patches
Pull Requests
History
AllCommentsChangesGit/SVN commits
[2016-09-05 05:11 UTC] stas@php.net
-Assigned To: +Assigned To: stas
[2016-09-05 06:50 UTC] nguyenvuhoang199321 at gmail dot com
[2016-09-12 07:48 UTC] stas@php.net
-PHP Version: 7.0Git-2016-08-23 (Git) +PHP Version: 5.6.25
[2016-09-13 04:04 UTC] stas@php.net
-Status: Assigned +Status: Closed
[2016-09-16 13:39 UTC] kaplan@php.net
-CVE-ID: needed +CVE-ID: 2016-7414