Out of bound when verify signature of tar phar in phar_parse_tarfile
| Sec Bug #73035 | Out of bound when verify signature of tar phar in phar_parse_tarfile | ||||
|---|---|---|---|---|---|
| Submitted: | 2016-09-06 21:02 UTC | Modified: | 2019-02-10 01:53 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-09-06 21:02 UTC] nguyenvuhoang199321 at gmail dot com
Description:
------------
There was a security code in phar_parse_tarfile
```
if (FAILURE == phar_verify_signature(fp, php_stream_tell(fp) - size - 512, myphar->sig_flags, buf + 8, size - 8, fname, &myphar->signature, &myphar->sig_len, error)) {
if (error) {
char *save = *error;
spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be verified: %s", fname, save);
efree(save);
}
goto bail;
}
```
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.
tarfile : https://drive.google.com/open?id=0B0D1DYQpkA9UVkV1Q0tTeGpVYk0
Test script:
---------------
<?php
$phar = new PharData('phars/signature.tar');
var_dump($phar);
?>
Actual result:
--------------
This debug steps I will show the malicious parse phar tarfile.
Set breakpoint at line 266 in tar.c
[----------------------------------registers-----------------------------------]
RAX: 0x4
RBX: 0x0
RCX: 0x0
RDX: 0xb ('\x0b')
RSI: 0xc ('\x0c')
RDI: 0x7fffffffa6ec ("00000000004")
RBP: 0x7fffffffa8b0 --> 0x7fffffffae00 --> 0x7fffffffaea0 --> 0x7fffffffaf10 --> 0x7fffffffafa0 --> 0x7fffffffb090 --> 0x7fffffffb0e0 --> 0x7fffffffb110 --> 0x7fffffffb150 --> 0x7fffffffb260 --> 0x7fffffffd560 --> 0x7fffffffe8e0 --> 0x7fffffffea30 --> 0xa1a0a0 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffa400 --> 0x7fffffffaff0 --> 0x0
RIP: 0x6e67b5 (<phar_parse_tarfile+1101>: cmp DWORD PTR [rbp-0x468],0x0)
R8 : 0xf2b910 ("/home/vagrant/Sources_Ext/audit/src/php7.0-7.0.8/ext/phar/tar.c")
R9 : 0xf4
R10: 0x180
R11: 0x7ffff67e2730 --> 0xfffda400fffda12f
R12: 0x42c280 (<_start>: xor ebp,ebp)
R13: 0x7fffffffeb10 --> 0x2
R14: 0x7ffff3e14030 --> 0x7ffff3e7d040 --> 0x9a66aa (<ZEND_DO_FCALL_SPEC_HANDLER>: push rbp)
R15: 0x7ffff3e7d040 --> 0x9a66aa (<ZEND_DO_FCALL_SPEC_HANDLER>: push rbp)
EFLAGS: 0x297 (CARRY PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x6e67a3 <phar_parse_tarfile+1083>: mov DWORD PTR [rbp-0x3f0],eax
0x6e67a9 <phar_parse_tarfile+1089>: mov eax,DWORD PTR [rbp-0x3f0]
0x6e67af <phar_parse_tarfile+1095>: mov DWORD PTR [rbp-0x47c],eax
=> 0x6e67b5 <phar_parse_tarfile+1101>: cmp DWORD PTR [rbp-0x468],0x0
0x6e67bc <phar_parse_tarfile+1108>: jne 0x6e67fd <phar_parse_tarfile+1173>
0x6e67be <phar_parse_tarfile+1110>: mov rax,QWORD PTR [rbp-0x438]
0x6e67c5 <phar_parse_tarfile+1117>: movzx eax,BYTE PTR [rax+0x9c]
0x6e67cc <phar_parse_tarfile+1124>: cmp al,0x67
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa400 --> 0x7fffffffaff0 --> 0x0
0008| 0x7fffffffa408 --> 0x7fffffffaf00 --> 0x0
0016| 0x7fffffffa410 --> 0x0
0024| 0x7fffffffa418 --> 0x2900000000 ('')
0032| 0x7fffffffa420 --> 0x7ffff3e75018 ("/vagrant_extend/audit/phars/signature.tar")
0040| 0x7fffffffa428 --> 0x7ffff3e5fa00 --> 0x12bf720 --> 0x8d6fc6 (<php_stdiop_write>: push rbp)
0048| 0x7fffffffa430 --> 0x4f665eff8
0056| 0x7fffffffa438 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
270 if (!old && (hdr->typeflag == TAR_GLOBAL_HDR || hdr->typeflag == TAR_FILE_HDR)) {
gdb-peda$ p size
$3 = 0x4
You see here size is only 4 bytes.
Set breakpoint before phar_verify_signature at line 304, and then continue
[----------------------------------registers-----------------------------------]
RAX: 0x7ffff3e5fa00 --> 0x12bf720 --> 0x8d6fc6 (<php_stdiop_write>: push rbp)
RBX: 0x7fffffffa678 ("gnature.bin")
RCX: 0x7fffffffa678 ("gnature.bin")
RDX: 0x10
RSI: 0x0
RDI: 0x7ffff3e5fa00 --> 0x12bf720 --> 0x8d6fc6 (<php_stdiop_write>: push rbp)
RBP: 0x7fffffffa8b0 --> 0x7fffffffae00 --> 0x7fffffffaea0 --> 0x7fffffffaf10 --> 0x7fffffffafa0 --> 0x7fffffffb090 --> 0x7fffffffb0e0 --> 0x7fffffffb110 --> 0x7fffffffb150 --> 0x7fffffffb260 --> 0x7fffffffd560 --> 0x7fffffffe8e0 --> 0x7fffffffea30 --> 0xa1a0a0 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffa3e0 --> 0x7ffff3e79120 --> 0x0
RIP: 0x6e6a0e (<phar_parse_tarfile+1702>: call 0x6e3c22 <phar_verify_signature>)
R8 : 0xfffffffc
R9 : 0x7ffff3e75018 ("/vagrant_extend/audit/phars/signature.tar")
R10: 0x7ffff6794150 (<__strncmp_sse42+3728>: pslldq xmm2,0x1)
R11: 0x0
R12: 0x10
R13: 0x7ffff3e79120 --> 0x0
R14: 0x7ffff3e79118 --> 0x0
R15: 0xfffffffc
EFLAGS: 0x212 (carry parity ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x6e6a05 <phar_parse_tarfile+1693>: mov rcx,rbx
0x6e6a08 <phar_parse_tarfile+1696>: mov edx,r12d
0x6e6a0b <phar_parse_tarfile+1699>: mov rdi,rax
=> 0x6e6a0e <phar_parse_tarfile+1702>: call 0x6e3c22 <phar_verify_signature>
0x6e6a13 <phar_parse_tarfile+1707>: add rsp,0x20
0x6e6a17 <phar_parse_tarfile+1711>: cmp eax,0xffffffff
0x6e6a1a <phar_parse_tarfile+1714>: jne 0x6e6a9a <phar_parse_tarfile+1842>
0x6e6a1c <phar_parse_tarfile+1716>: cmp QWORD PTR [rbp-0x4b0],0x0
Guessed arguments:
arg[0]: 0x7ffff3e5fa00 --> 0x12bf720 --> 0x8d6fc6 (<php_stdiop_write>: push rbp)
arg[1]: 0x0
arg[2]: 0x10
arg[3]: 0x7fffffffa678 ("gnature.bin")
arg[4]: 0xfffffffc
arg[5]: 0x7ffff3e75018 ("/vagrant_extend/audit/phars/signature.tar")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa3e0 --> 0x7ffff3e79120 --> 0x0
0008| 0x7fffffffa3e8 --> 0x7ffff3e79118 --> 0x0
0016| 0x7fffffffa3f0 --> 0x7fffffffaff0 --> 0x0
0024| 0x7fffffffa3f8 --> 0x6e69cb (<phar_parse_tarfile+1635>: mov rdx,rax)
0032| 0x7fffffffa400 --> 0x7fffffffaff0 --> 0x0
0040| 0x7fffffffa408 --> 0x7fffffffaf00 --> 0x0
0048| 0x7fffffffa410 --> 0x0
0056| 0x7fffffffa418 --> 0x2900000000 ('')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00000000006e6a0e 305 if (FAILURE == phar_verify_signature(fp, php_stream_tell(fp) - size - 512, myphar->sig_flags, buf + 8, size - 8, fname, &myphar->signature, &myphar->sig_len, error)) {
As you can see because siglen is calculated by subtract size by 8 so as result this value is negative number in signed int or large int number in unsigned int. This mistake may lead to heap out of bound
Patches
Pull Requests
History
AllCommentsChangesGit/SVN commits
[2016-09-12 04:27 UTC] stas@php.net
[2016-09-12 04:30 UTC] nguyenvuhoang199321 at gmail dot com
[2016-09-12 04:38 UTC] stas@php.net
-PHP Version: 7.0.10 +PHP Version: 5.6.25 -Assigned To: +Assigned To: stas
[2016-09-12 06:30 UTC] nguyenvuhoang199321 at gmail dot com
[2016-09-13 04:04 UTC] stas@php.net
-Status: Assigned +Status: Closed