Talos Vulnerability Report

TALOS-2017-0317

LibOFX Tag Parsing Code Execution Vulnerability

September 13, 2017
CVE Number

CVE-2017-2816

Summary

An exploitable buffer overflow vulnerability exists in the tag parsing functionality of LibOFX 0.9.11. A specially crafted OFX file can cause a write out of bounds resulting in a buffer overflow on the stack. An attacker can construct a malicious OFX file to trigger this vulnerability.

Tested Versions

LibOFX 0.9.11

Product URLs

https://github.com/libofx/libofx

CVSSv3 Score

8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

CWE

CWE-120 - Buffer Copy without Checking Size of Input (‘Classic Buffer Overflow’)

Details

OFX is the Open Financial Exchange format used by financial institutions to share financial data with clients. GnuCash is an open source financial-accounting software that has the capability to import OFX records using libOFX, an open source implementation of OFX.

While parsing the tags of the given OFX record, libOFX attempts to strip any present OFX proprietary tags. After checking that the file begins with the correct OFX tag of <OFX>, the remaining tag is sent over to be sanitized by sanitize_proprietary_tags. In this function, the tag is copied into a local stack variable called tagname as long as the parser is still within the opening tag [0].

lib/ofx_preproc.cpp:75
const unsigned int READ_BUFFER_SIZE = 1024;

lib/ofx_preproc.cpp:417
string sanitize_proprietary_tags(string input_string)
{
unsigned int i;
size_t input_string_size;
bool strip = false;
bool tag_open = false;
int tag_open_idx = 0; //Are we within < > ?
bool closing_tag_open = false; //Are we within </ > ?
int orig_tag_open_idx = 0;
bool proprietary_tag = false; //Are we within a proprietary element?
bool proprietary_closing_tag = false;
int crop_end_idx = 0;
char buffer[READ_BUFFER_SIZE] = "";
char tagname[READ_BUFFER_SIZE] = "";
int tagname_idx = 0;
char close_tagname[READ_BUFFER_SIZE] = "";


for (i = 0; i < input_string_size; i++)

    if (input_string.c_str()[i] == '<')
    {
    tag_open = true;
    tag_open_idx = i;
    if (proprietary_tag == true && input_string.c_str()[i+1] == '/')
    {
        ...
    }
    else if (proprietary_tag == true)
    {
        //It is the start of a new tag, following a proprietary tag
        crop_end_idx = i - 1;
        strip = true;
    }
    }
    else if (input_string.c_str()[i] == '>')
    {
        ...
    }
    else if (tag_open == true && closing_tag_open == false)
    {
    if (input_string.c_str()[i] == '.')
    {
        if (proprietary_tag != true)
        {
        orig_tag_open_idx = tag_open_idx;
        proprietary_tag = true;
        }
    }
    tagname[tagname_idx] = input_string.c_str()[i]; [0]
    tagname_idx++;

Because the loop occurs over the size of the input_string, if the input_string is larger than READ_BUFFER_SIZE, then the stack variable is overflown and can potentially lead to code execution.

Crash Information

==6542==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffffbb4260 at pc 0x7fab9d30ccc1 bp 0x7fffffbb39b0 sp 0x7fffffbb39a8
WRITE of size 1 at 0x7fffffbb4260 thread T0
	#0 0x7fab9d30ccc0  (/home/vagrant/fuzzing/libofx-asan/libofx-0.9.11/lib/.libs/libofx.so.7+0x30cc0)
	#1 0x7fab9d30aba0  (/home/vagrant/fuzzing/libofx-asan/libofx-0.9.11/lib/.libs/libofx.so.7+0x2eba0)
	#2 0x7fab9d3057cb  (/home/vagrant/fuzzing/libofx-asan/libofx-0.9.11/lib/.libs/libofx.so.7+0x297cb)
	#3 0x4f8ba2  (/home/vagrant/fuzzing/libofx-asan/libofx-0.9.11/ofxdump/.libs/lt-ofxdump+0x4f8ba2)
	#4 0x7fab9c06982f  (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
	#5 0x419618  (/home/vagrant/fuzzing/libofx-asan/libofx-0.9.11/ofxdump/.libs/lt-ofxdump+0x419618)

Address 0x7fffffbb4260 is located in stack of thread T0 at offset 2208 in frame
	#0 0x7fab9d30c38f  (/home/vagrant/fuzzing/libofx-asan/libofx-0.9.11/lib/.libs/libofx.so.7+0x3038f)

This frame has 9 object(s):
	[32, 1056) 'buffer'
	[1184, 2208) 'tagname' <== Memory access at offset 2208 overflows this variable
	[2336, 3360) 'close_tagname'
	[3488, 3520) ''
	[3552, 3584) ''
	[3616, 3617) ''
	[3632, 3664) ''
	[3696, 3728) ''
	[3760, 3761) ''
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
	(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/vagrant/fuzzing/libofx-asan/libofx-
    0.9.11/lib/.libs/libofx.so.7+0x30cc0)
Shadow bytes around the buggy address:
0x10007ff6e7f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007ff6e800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007ff6e810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007ff6e820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007ff6e830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10007ff6e840: 00 00 00 00 00 00 00 00 00 00 00 00[f2]f2 f2 f2
0x10007ff6e850: f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 00 00 00 00
0x10007ff6e860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007ff6e870: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007ff6e880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007ff6e890: 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
Left alloca redzone:     ca
Right alloca redzone:    cb
==6542==ABORTING

Timeline

2017-04-14 - Vendor Disclosure
2017-09-13 - Public Release

Credit

Discovered by Cory Duplantis of Cisco Talos.