Bug #79385 [NEW]: Global Out-of-Bounds Read

From: Date: Sun, 15 Mar 2020 14:44:55 +0000
Subject: Bug #79385 [NEW]: Global Out-of-Bounds Read
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-226118@lists.php.net to get a copy of this message
From:             dev dot davidmatosse at outlook dot com
Operating system: Linux
PHP version:      Irrelevant
Package:          GD related
Bug Type:         Bug
Bug description:Global Out-of-Bounds Read

Description:
------------
While analysing the source code of libgd (located at
github.com/libgd/libgd [1] and bundled 
version of php 7.4.3 [2]) was found that there is a global out-of-bounds
read error in function dynamicGetbuf (gd_io_dp.c [3]), called by
gdImageCreateFromWebpPtr or gdImageCreateFromJpegPtr 
functions.

Both arguments of presented functions (gdImageCreateFromWebpPtr(int
size, void *data) and
gdImageCreateFromJpegPtr(int size, void *data)) are user supplied data
with no prior sanitize. 

Whatever the call context in which we have size > that the length of the
data, we trigger the
global out-of-bounds read.


> Study Case

1 - gdImageCreateFromWebpPtr [4] is a function used to load truecolor or
create images from WebP
data already in memory. The function have two argumets, a size of the
data in bytes and a pointer
to data (WebP data). 
In attempt to create the Webp image from data already in memory, we call
the function gdImageCreateFromWebpPtr 
and pass to then 2 arguments (user controlled data), this information is
used to create the gdIOCtx [5]
and we pass to gdImageCreateFromWebpCtx [6] to create the image. 

2 - on function gdImageCreateFromWebpCtx, in attempt to get the number
of bytes read from gdIOCtx
we call gdGetBuf [7] which is resolved dynamically to dynamicGetbuf [8]

3 - at this point we have the following
	rlen     = size
	dp->data = data
    if we call the memcpy [9] with rlen set to length greater than the
source (dp->data), it results
    in global out-of-bounds read.


> conclusion
After digging into the code a little further was found that to reach the
vulnarable function [8],
we need a following call stack

@whatever_function@ --> gdGetBuf(dest, size, src) -->
dynamicGetbuf(src_pkd, dest, size) --> memcpy(dest, src, size); [where
'size' and 'data' are user controllable] 

As example we have: gdImageCreateFromWebpPtr and
gdImageCreateFromJpegPtr


> references
[1] - https://github.com/libgd/libgd
[2] - https://www.php.net/downloads.php#v7.4.3
[3] - https://github.com/libgd/libgd/blob/master/src/gd_io_dp.c
[4] - https://github.com/libgd/libgd/blob/master/src/gd_webp.c#L86
[5] - https://github.com/libgd/libgd/blob/master/src/gd_webp.c#L89
[6] - https://github.com/libgd/libgd/blob/master/src/gd_webp.c#L102
[7] - https://github.com/libgd/libgd/blob/master/src/gd_io.c#L211
[8] - https://github.com/libgd/libgd/blob/master/src/gd_io_dp.c#L270
[9] - https://github.com/libgd/libgd/blob/master/src/gd_io_dp.c#L302

Test script:
---------------
/*
 file:     poc.c
 to build: clang-9 poc.c -o poc -lgd -fsanitize=address
*/

#include <gd.h>

int main(){

	int size 	= 16;
	void *data 	= "AAAAAAAAAAAA";
	gdImageCreateFromWebpPtr (size, data);
	return 0;
}

Expected result:
----------------
Webp image generated.

Actual result:
--------------
==1611==ERROR: AddressSanitizer: global-buffer-overflow on address
0x0000004d4c1c at pc 0x000000431bb7 bp 0x7ffe7ebd0ef0 sp 0x7ffe7ebd06b0
READ of size 48 at 0x0000004d4c1c thread T0
    #0 0x431bb6 in memcpy (~/poc/poc+0x431bb6)
    #1 0x7f57a1f5a368 in dynamicGetbuf ~/libgd/src/gd_io_dp.c:302:2
    #2 0x7f57a1f59fb7 in gdGetBuf ~/libgd/src/gd_io.c:213:9
    #3 0x7f57a1f68dd2 in gdImageCreateFromWebpCtx
~/libgd/src/gd_webp.c:126:7
    #4 0x7f57a1f69061 in gdImageCreateFromWebpPtr
~/libgd/src/gd_webp.c:92:7
    #5 0x4c222f in main (~/poc/poc+0x4c222f)
    #6 0x7f57a103882f in __libc_start_main
/build/glibc-LK5gWL/glibc-2.23/csu/../csu/libc-start.c:291
    #7 0x41ac48 in _start (~/poc/poc+0x41ac48)

0x0000004d4c1c is located 0 bytes to the right of global variable
'<string literal>' defined in 'poc.c:10:16' (0x4d4c00) of size 28
  '<string literal>' is ascii string 'AAAAAAAAAAAAAAAAAAAAAAAAAAA'
SUMMARY: AddressSanitizer: global-buffer-overflow (~/poc/poc+0x431bb6)
in memcpy
Shadow bytes around the buggy address:
  0x000080092930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x000080092940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x000080092950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x000080092960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x000080092970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x000080092980: 00 00 00[04]f9 f9 f9 f9 00 00 00 00 00 00 00 00
  0x000080092990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800929a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800929b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800929c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800929d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  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
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==1611==ABORTING


-- 
Edit bug report at https://bugs.php.net/bug.php?id=79385&edit=1
-- 
Fix committed:                    https://bugs.php.net/fix.php?id=79385&r=fixed
Fixed in release:                 https://bugs.php.net/fix.php?id=79385&r=alreadyfixed
Need backtrace:                   https://bugs.php.net/fix.php?id=79385&r=needtrace
Need Reproduce Script:            https://bugs.php.net/fix.php?id=79385&r=needscript
Try newer version:                https://bugs.php.net/fix.php?id=79385&r=oldversion
Not developer issue:              https://bugs.php.net/fix.php?id=79385&r=support
Expected behavior:                https://bugs.php.net/fix.php?id=79385&r=notwrong
Not enough info:                  https://bugs.php.net/fix.php?id=79385&r=notenoughinfo
Submitted twice:                  https://bugs.php.net/fix.php?id=79385&r=submittedtwice
register_globals:                 https://bugs.php.net/fix.php?id=79385&r=globals
PHP version support discontinued: https://bugs.php.net/fix.php?id=79385&r=phptooold
Daylight Savings:                 https://bugs.php.net/fix.php?id=79385&r=dst
IIS Stability:                    https://bugs.php.net/fix.php?id=79385&r=isapi
Install GNU Sed:                  https://bugs.php.net/fix.php?id=79385&r=gnused
Floating point limitations:       https://bugs.php.net/fix.php?id=79385&r=float
No Zend Extensions:               https://bugs.php.net/fix.php?id=79385&r=nozend
MySQL Configuration Error:        https://bugs.php.net/fix.php?id=79385&r=mysqlcfg


Thread (6 messages)

« previous php.bugs (#226118) next »