CVE-2018-16140: Buffer under write vulnerability in fig2dev 3.2.7a

Buffer under write vulnerability in fig2dev 3.2.7a

Loginsoft-2018-16140

August 25, 2018

CVE Number

CVE-2018-16140

CWE

CWE-124: Buffer Underwrite (‘Buffer Underflow’)

Product Details

Xfig is a free and open-source vector graphics editor which runs under the X Window System on most UNIX-compatible platforms. fig2dev is a library used by Xfig package to translate fig code to other graphical languages (tikz, shape, jpeg, png etc.)

URL: https://sourceforge.net/projects/mcj/

Vulnerable Versions

fig2dev 3.2.7a (Xfig package)

Vulnerability Details

A Buffer under write vulnerability was discovered in fig2dev 3.2.7a version.

SYNOPSIS

Pre-research, on having a closer look at CVE’s specific to fig2dev binary, we came across a buffer under write vulnerability which was discovered in fig2dev 3.2.6a-6, reported by jwilk.

Ref: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=882022

As per the report, a local variable `len` in get_line() function of fig2dev/read.c, when received with a values less than 2 (0 or 1), would end up writing outside the buffer, creating a buffer underwrite issue. Being precise, get_line() functions is responsible for doing few checks, some of them being Skip empty lines, trailing newline, trailing CR’s etc. The problem exists while trailing any CR’s (Carriage returns) from the fig document.

According to the code, on occurrence of an `\r`, a NULL termination is written thereby stripping off the CR. The issues get introduced when the value of len is manipulated with a value, which when computed with 2, would result in writing outside buffer, throwing a Segmentation fault (SIGSEGV).

As per Debian report log, the vulnerability looks to be fixed in the 3.2.7 release. Upon searching for the fix commit in the 3.2.7 release, we figured out there were no fix made to the get_line() function, instead in the read_objects() which was using the same block of code. This looked quite strange. Upon fuzzing, we were still able to reproduce the same issue. The value of len inside get_len() manage to be zero, leading to buffer underwrite.

Suggested mitigation: Applying the same bound check, which was employed earlier in 3.2.7 against read_objects() (checking len>0).

Analysis

Breakpoint 1 at 0x812ad: file read.c, line 1470.
The program is not being run.
Starting program: /home/ethan/Desktop/fig2dev-3.2.7a_debian/fig2dev/fig2dev -L tikz ~/Desktop/overflow_218
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, get_line (fp=0x616000000080) at read.c:1470
1470                if (buf[len-2] == '\r')
len = 1
rax            0x555555995ac0   93824996694720
rbx            0x7fffffffde70   140737488346736
rcx            0x0  0
rdx            0x0  0
rsi            0x616000000100   107064944754944
rdi            0x0  0
rbp            0x7fffffffddd0   0x7fffffffddd0
rsp            0x7fffffffddb0   0x7fffffffddb0
r8             0x0  0
r9             0x0  0
r10            0x7ffff7fdb780   140737353987968
r11            0x246    582
r12            0xffffffffbce    17592186043342
r13            0x7fffffffdf10   140737488346896
r14            0x7fffffffde70   140737488346736
r15            0x0  0
rip            0x5555555d52ad   0x5555555d52ad <get_line+204>
eflags         0x246    [ PF ZF IF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0
1465                if (save_comment() < 0)
1466                    return -1;
1467            } else if (*buf != '\n') {      /* Skip empty lines */
1468                len = strlen(buf);
1469                buf[len-1] = '\0';      /* strip trailing newline */
1470                if (buf[len-2] == '\r')
1471                    buf[len-2] = '\0';  /* strip trailing CRs */
1472                return 1;
1473            }
1474        }
Continuing.
[Inferior 1 (process 5867) exited with code 01]

ASAN Report


==4867==ERROR: AddressSanitizer: global-buffer-overflow on address 0x0000007cd03f at pc 0x0000004232a1 bp 0x7fffffffdb40 sp 0x7fffffffdb30 

READ of size 1 at 0x0000007cd03f thread T0 

    #0 0x4232a0 in get_line /home/woot/Desktop/fig2dev-3.2.7a/fig2dev/read.c:1470 

    #1 0x41a27f in read_objects /home/woot/Desktop/fig2dev-3.2.7a/fig2dev/read.c:340 

    #2 0x418f20 in readfp_fig /home/woot/Desktop/fig2dev-3.2.7a/fig2dev/read.c:172 

    #3 0x418e09 in read_fig /home/woot/Desktop/fig2dev-3.2.7a/fig2dev/read.c:142 

    #4 0x411583 in main /home/woot/Desktop/fig2dev-3.2.7a/fig2dev/fig2dev.c:424 

    #5 0x7ffff659282f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) 

    #6 0x4030e8 in _start (/home/woot/Desktop/fig2dev-3.2.7a/fig2dev/fig2dev+0x4030e8) 

  

0x0000007cd03f is located 59 bytes to the right of global variable 'gif_colnum' defined in 'read.c:80:13' (0x7cd000) of size 4 

0x0000007cd03f is located 1 bytes to the left of global variable 'buf' defined in 'read.c:81:14' (0x7cd040) of size 8192 

SUMMARY: AddressSanitizer: global-buffer-overflow /home/woot/Desktop/fig2dev-3.2.7a/fig2dev/read.c:1470 get_line 

Shadow bytes around the buggy address: 

  0x0000800f19b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

  0x0000800f19c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

  0x0000800f19d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

  0x0000800f19e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

  0x0000800f19f0: 00 00 00 00 00 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 

=>0x0000800f1a00: 04 f9 f9 f9 f9 f9 f9[f9]00 00 00 00 00 00 00 00 

  0x0000800f1a10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

  0x0000800f1a20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

  0x0000800f1a30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

  0x0000800f1a40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

  0x0000800f1a50: 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 

  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 

==4867==ABORTING 

[Inferior 1 (process 4867) exited with code 01


Proof of Concept

fig2dev -L tikz $POC

The switch -L specifies the graphical language to which we want to convert our figure file to, followed by our input figure file. The issue is exploitable when supplied a crafted fig file via the above given command.

Timeline

Vendor Disclosure: 2018-08-22

Patch Release: 2018-08-23

Public Disclosure: 2018-08-25

Credit

Discovered by ACE Team – Loginsoft