ZDI-CAN-27517: FontForge GUtils BMP File Parsing Heap-based Buffer Overflow Remote Code Execution Vulnerability
-- CVSS -----------------------------------------
7.8: AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
-- ABSTRACT -------------------------------------
Trend Micro's Zero Day Initiative has identified a vulnerability affecting the following products:
FontForge - FontForge
-- VULNERABILITY DETAILS ------------------------
* Version tested:387146a241b36bcdf6ce229c5a3fe367ed3854a1
* Installer file:-
* Platform tested:ubuntu 24.04 desktop edition
heap overflow exists within the FontForge BMP parser.
It didn't validate the length of the pixels when reading from BMP within the readpixels() function.
It overflowed around 0x100 bytes with arbitrary content.
The overflow will be triggered when victim open the malicious svg file with the fontforge application.
static int readpixels(FILE *file,struct bmpheader *head) {
int i,ii,j,ll,excess;
fseek(file,head->offset,0);
ll = head->width;
if ( head->bitsperpixel==8 && head->compression==0 ) {
excess = ((ll+3)/4)*4 -ll;
for ( i=0; i<head->height; ++i ) {
fread(head->byte_pixels+i*ll,1,ll,file);
for ( j=0; j<excess; ++j )
(void) getc(file);
}
} else if ( head->bitsperpixel==8 ) {
/* 8 bit RLE */
int ii = 0;
while ( ii<head->height*head->width ) {
int cnt = getc(file); // (1) `cnt` is extracted from the file without validation
// it should be in bounds of (height*head->width)-ii
if ( cnt!=0 ) {
int ch = getc(file);
while ( --cnt>=0 )
head->byte_pixels[ii++] = ch; // (2) overflow here
} else {
cnt = getc(file); // (3) same pattern repeats
if ( cnt>= 3 ) {
int odd = cnt&1;
while ( --cnt>=0 )
head->byte_pixels[ii++] = getc(file); // (3) overflow here
if ( odd )
getc(file);
} else if ( cnt==0 ) { /* end of scan line */
ii = ((ii+head->width-1)/head->width) * head->width;
} else if ( cnt==1 ) {
break;
} else if ( cnt==2 ) {
int x=getc(file);
int y = getc(file);
y += ii/head->width;
x += ii%head->width;
ii = y*head->width + x;
}
}
}
reproduce steps
mkdir build ; cd build
cmake -GNinja .. -DCMAKE_C_FLAGS="-fsanitize=address -g" -DCMAKE_CXX_FLAGS="-fsanitize=address -g" -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address"
ninja
./bin/fontforge [poc.svg]
ASAN report
=================================================================
==8294==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50b0000a3ed0 at pc 0x73e28866c2b7 bp 0x7fffe4cd1e10 sp 0x7fffe4cd1e00
WRITE of size 1 at 0x50b0000a3ed0 thread T0
#0 0x73e28866c2b6 in readpixels /root/fontforge/gutils/gimagereadbmp.c:193
#1 0x73e28866c2b6 in GImageRead_Bmp /root/fontforge/gutils/gimagereadbmp.c:362
#2 0x73e288c7a6e6 in GImageFromDataURI /root/fontforge/fontforge/svg.c:2399
#3 0x73e288c7a6e6 in SVGParseImage /root/fontforge/fontforge/svg.c:2443
#4 0x73e288c7a6e6 in _SVGParseSVG /root/fontforge/fontforge/svg.c:2762
#5 0x73e288c77849 in _SVGParseSVG /root/fontforge/fontforge/svg.c:2709
#6 0x73e288c7b203 in SVGParseSVG /root/fontforge/fontforge/svg.c:2868
#7 0x73e288c7b60d in SVGParseGlyphBody /root/fontforge/fontforge/svg.c:2880
#8 0x73e288c7f626 in SVGParseGlyph /root/fontforge/fontforge/svg.c:2984
#9 0x73e288c7f626 in SVGParseFont /root/fontforge/fontforge/svg.c:3430
#10 0x73e288c7f626 in _SFReadSVG /root/fontforge/fontforge/svg.c:3617
#11 0x73e288b282cc in _ReadSplineFont /root/fontforge/fontforge/splinefont.c:1218
#12 0x73e288b28fb0 in LoadSplineFont /root/fontforge/fontforge/splinefont.c:1420
#13 0x73e28878466f in ViewPostScriptFont /root/fontforge/fontforge/fontviewbase.c:1387
#14 0x5e33865e7396 in fontforge_main /root/fontforge/fontforgeexe/startui.c:1017
#15 0x73e285a2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#16 0x73e285a2a28a in __libc_start_main_impl ../csu/libc-start.c:360
#17 0x5e3386145f74 in _start (/usr/local/bin/fontforge+0x172f74) (BuildId: 68c80c361f3adf7aea14524aeb7eb3e62357bdcb)
0x50b0000a3ed0 is located 0 bytes after 112-byte region [0x50b0000a3e60,0x50b0000a3ed0)
allocated by thread T0 here:
#0 0x73e2894fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x73e288669e94 in GImageRead_Bmp /root/fontforge/gutils/gimagereadbmp.c:357
SUMMARY: AddressSanitizer: heap-buffer-overflow /root/fontforge/gutils/gimagereadbmp.c:193 in readpixels
Shadow bytes around the buggy address:
0x50b0000a3c00: 00 fa fa fa fa fa fa fa fa fa fd fd fd fd fd fd
0x50b0000a3c80: fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa fa
0x50b0000a3d00: fd fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa
0x50b0000a3d80: fa fa fa fa fa fa fd fd fd fd fd fd fd fd fd fd
0x50b0000a3e00: fd fd fd fd fa fa fa fa fa fa fa fa 00 00 00 00
=>0x50b0000a3e80: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa
0x50b0000a3f00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x50b0000a3f80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x50b0000a4000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x50b0000a4080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x50b0000a4100: 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
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
==8294==ABORTING
-- CREDIT ---------------------------------------
This vulnerability was discovered by:
volticks (@movx64 on twitter) working with Trend Micro Zero Day Initiative
-- FURTHER DETAILS ------------------------------
Supporting files:
If supporting files were contained with this report they are provided within a password protected ZIP file. The password is the ZDI candidate number in the form: ZDI-CAN-XXXX where XXXX is the ID number.
Please confirm receipt of this report. We expect all vendors to remediate ZDI vulnerabilities within 120 days of the reported date. If you are ready to release a patch at any point leading up to the deadline, please coordinate with us so that we may release our advisory detailing the issue. If the 120-day deadline is reached and no patch has been made available we will release a limited public advisory with our own mitigations, so that the public can protect themselves in the absence of a patch. Please keep us updated regarding the status of this issue and feel free to contact us at any time:
Zero Day Initiative
zdi-disclosures@trendmicro.com
The PGP key used for all ZDI vendor communications is available from:
http://www.zerodayinitiative.com/documents/disclosures-pgp-key.asc
-- INFORMATION ABOUT THE ZDI --------------------
Established by TippingPoint and acquired by Trend Micro, the Zero Day Initiative (ZDI) neither re-sells vulnerability details nor exploit code. Instead, upon notifying the affected product vendor, the ZDI provides its Trend Micro TippingPoint customers with zero day protection through its intrusion prevention technology. Explicit details regarding the specifics of the vulnerability are not exposed to any parties until an official vendor patch is publicly available.
Please contact us for further details or refer to:
http://www.zerodayinitiative.com
-- DISCLOSURE POLICY ----------------------------
Our vulnerability disclosure policy is available online at:
http://www.zerodayinitiative.com/advisories/disclosure_policy/