|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
[2016-02-17 16:48 UTC] manhluat at vnsecurity dot net
[2016-02-22 00:53 UTC] stas@php.net
[2016-02-22 00:53 UTC] stas@php.net
-Assigned To:
+Assigned To: stas
[2016-02-22 00:57 UTC] stas@php.net
-PHP Version: 5.6.17
+PHP Version: 5.5.32
[2016-03-02 06:39 UTC] stas@php.net
[2016-03-02 06:39 UTC] stas@php.net
-Status: Assigned
+Status: Closed
[2016-03-02 06:56 UTC] stas@php.net
[2016-03-02 07:12 UTC] stas@php.net
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Fri Oct 24 23:00:01 2025 UTC |
Description: ------------ There is a flaw was found in PHAR's phar_parse_zipfile() function. -------------------------------- ext/phar/zip.c: int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error) /* {{{ */ { phar_zip_dir_end locator; char buf[sizeof(locator) + 65536]; ... while ((p=(char *) memchr(p + 1, 'P', (size_t) (size - (p + 1 - buf)))) != NULL) { if (!memcmp(p + 1, "K\5\6", 3)) { memcpy((void *)&locator, (void *) p, sizeof(locator)); if (PHAR_GET_16(locator.centraldisk) != 0 || PHAR_GET_16(locator.disknumber) != 0) { /* split archives not handled */ php_stream_close(fp); if (error) { spprintf(error, 4096, "phar error: split archives spanning multiple zips cannot be processed in zip-based phar \"%s\"", fname); } return FAILURE; } ... -------------------------------- Sizeof(buf) equals 0x10016 (0x16 (sizeof[locator]) plus 65536) The above code block tries to determine where is "PK\x05\x06" in buf. it is actually "End of central directory record" structure of zip file (https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html). Then it copies 0x16 bytes from there to `phar_zip_dir_end locator`. What if "PK\x05\x06" signature is located at end of `buf` variable, it will read out-of-bound `buf` variable to copy to `locator`. You can see details of debugging below. * Note: - PHP5 and PHP7 latest version are vulnerable. - Works on Linux, Mac, Windows as well. I think - It is possible to memory leak, crash, etc. `poc.zip` can be downloaded here: https://drive.google.com/file/d/0B4qNn6UEDAR-V2ptcGlUR01DTjA/view?usp=sharing Test script: --------------- <?php $p = new PharData($argv[1]); ?> Actual result: -------------- You can see that I print out `p`,`locator`,`buf`,.. to let you imagine the stack layout. Finally, `locator` contains some pointer, memory, blah blah. gdb-peda$ r Starting program: /root/test/php-7.0.2/sapi/cli/php ./php-test/zip.php ./php-test/reproduce/poc.zip [----------------------------------registers-----------------------------------] RAX: 0x0 RBX: 0x7ffffffe9440 ('b' <repeats 200 times>...) RCX: 0x0 RDX: 0x0 RSI: 0x19 RDI: 0x7b ('{') RBP: 0x7fffffff9452 --> 0x3100000006054b50 RSP: 0x7ffffffe8230 --> 0x0 RIP: 0x50d28f (<phar_parse_zipfile+1167>: movsx ax,BYTE PTR [rbp+0x7]) R8 : 0x46 ('F') R9 : 0x8e R10: 0x0 R11: 0x0 R12: 0x10016 R13: 0x7ffff6474300 --> 0xdd8e80 --> 0x606090 (<php_stdiop_write>: mov rax,QWORD PTR [rdi+0x8]) R14: 0x0 R15: 0x7ffff6474300 --> 0xdd8e80 --> 0x606090 (<php_stdiop_write>: mov rax,QWORD PTR [rdi+0x8]) EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x50d283 <phar_parse_zipfile+1155>: mov BYTE PTR [rsp+0x60],al 0x50d287 <phar_parse_zipfile+1159>: movzx eax,BYTE PTR [rbp+0x15] 0x50d28b <phar_parse_zipfile+1163>: mov BYTE PTR [rsp+0x50],al => 0x50d28f <phar_parse_zipfile+1167>: movsx ax,BYTE PTR [rbp+0x7] 0x50d294 <phar_parse_zipfile+1172>: shl eax,0x8 0x50d297 <phar_parse_zipfile+1175>: or ax,r11w 0x50d29b <phar_parse_zipfile+1179>: jne 0x50d3c0 <phar_parse_zipfile+1472> 0x50d2a1 <phar_parse_zipfile+1185>: shl ecx,0x8 [------------------------------------stack-------------------------------------] 0000| 0x7ffffffe8230 --> 0x0 0008| 0x7ffffffe8238 --> 0x0 0016| 0x7ffffffe8240 --> 0x0 0024| 0x7ffffffe8248 --> 0x0 0032| 0x7ffffffe8250 --> 0x7fffffffacf0 --> 0x0 0040| 0x7ffffffe8258 --> 0x7ffff6471098 ("/root/test/php-test/reproduce/poc.zip") 0048| 0x7ffffffe8260 --> 0x0 0056| 0x7ffffffe8268 --> 0x25 ('%') [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 2, phar_parse_zipfile (fp=fp@entry=0x7ffff6474300, fname=fname@entry=0x7ffff6471098 "/root/test/php-test/reproduce/poc.zip", fname_len=fname_len@entry=0x25, alias=alias@entry=0x0, alias_len=alias_len@entry=0x0, pphar=pphar@entry=0x7fffffffabe8, error=error@entry=0x7fffffffacf0) at /root/test/php-7.0.2/ext/phar/zip.c:204 204 if (PHAR_GET_16(locator.centraldisk) != 0 || PHAR_GET_16(locator.disknumber) != 0) { gdb-peda$ print p $12 = 0x7fffffff9452 "PK\005\006" gdb-peda$ print p+0x16 $13 = 0x7fffffff9468 "\350\253\377\377\377\177" gdb-peda$ print buf+0x10016 $14 = 0x7fffffff9456 "" gdb-peda$ x/20gx $rbp+0x4 <--- locator 0x7fffffff9456: 0x7b198e4631000000 0x7ffff6474300a65d 0x7fffffff9466: 0x7fffffffabe80000 0x7fffffff96c00000 0x7fffffff9476: 0x7ffff64710980000 0x0000000004000000 0x7fffffff9486: 0x0000000000010000 0x7ffff64743000000 0x7fffffff9496: 0x00000051b0e20000 0x7fffffffacf00000 0x7fffffff94a6: 0x0000000000000000 0x0000000000000000 0x7fffffff94b6: 0x7fffffff96d20000 0x0000000000000000 0x7fffffff94c6: 0x0000000000000000 0x7fffffffacf00000 0x7fffffff94d6: 0x0000000000000000 0x7fffffff96a00000 0x7fffffff94e6: 0x7ffff64710980000 0x7fffffff96500000 gdb-peda$