CVE-2017-2924
An exploitable heap-based buffer overflow vulnerability exists in the read_legacy_biff function of FreeXL 1.0.3. A specially crafted XLS file can cause a memory corruption resulting in remote code execution. An attacker can send malicious XLS file to trigger this vulnerability.
freexl 1.0.3
https://www.gaia-gis.it/fossil/freexl/index
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-122: Heap-based Buffer Overflow
FreeXL is a C library which can read Microsoft Excel File Format ( XLS ) files. The library is used by the SpatiaLite open source library. The heap-based buffer overflow appears in read_legacy_biff function during parsing DIMENSION record. To reach the vulnerable code, the XLS file needs be in the old BIFF format.
Line 4046 /*
Line 4047 * the XLS file is internally structured as a FAT-like
Line 4048 * file-system (Compound File Binary Format, CFBF)
Line 4049 * so we'll start by parsing the FAT
Line 4050 */
Line 4051 chain = read_cfbf_header (workbook, swap, &errcode);
Line 4052 if (!chain)
Line 4053 {
Line 4054 /* it's not a CFBF file: testing older BIFF-(2,3,4) formats */
Line 4055 if (read_legacy_biff (workbook, swap))
At line 2275 record_size
is read directly from the file:
Line 2268 while (1)
Line 2269 {
Line 2270 /* looping on BIFF records */
Line 2271
Line 2272 if (fread (&buf, 1, 4, workbook->xls) != 4)
Line 2273 return 0;
Line 2274 memcpy (record_type.bytes, buf, 2);
Line 2275 memcpy (record_size.bytes, buf + 2, 2);
The vulnerability occurs in the read_legacy_biff
function:
Line 2439 if ((record_type.value == 0x0000
Line 2440 && workbook->biff_version == FREEXL_BIFF_VER_2)
Line 2441 || (record_type.value == BIFF_DIMENSION
Line 2442 && (workbook->biff_version == FREEXL_BIFF_VER_3
Line 2443 || workbook->biff_version == FREEXL_BIFF_VER_4)))
Line 2444 {
Line 2445 /* DIMENSION marker found */
Line 2446 biff_word16 word16;
Line 2447 unsigned int rows;
Line 2448 unsigned short columns;
Line 2449 char *utf8_name;
Line 2450 if (fread
Line 2451 (workbook->record, 1, record_size.value,
Line 2452 workbook->xls) != record_size.value)
Line 2453 return 0;
At line 2450
, workbook->record is read from the file, based on the size in the file that was read at line 2275. There are no checks to ensure that this value is smaller than the maximum size that a record can contain:
Line 278 unsigned char record[8224]; /* current record */
This allows an attacker to fully control the heap overflow since both the content and the size are read directly from the file.
The workbook object just after executing line 2450
:
(gdb) p/x *workbook
$6 = {magic1 = 0x63dd0d77, xls = 0x614100, fat = 0x0, cfbf_version = 0x0, cfbf_sector_size = 0x0, start_sector =
0x0, size = 0x0, current_sector = 0x0, bytes_read = 0x0, current_offset = 0x0,
sector_buf = {0x0 <repeats 8192 times>}, p_in = 0x604040, sector_end = 0x0, sector_ready = 0x0, ok_bof = 0x1,
biff_version = 0x2, biff_max_record_size = 0x0, biff_content_type = 0x0, biff_code_page = 0x0,
biff_book_code_page = 0x0, biff_date_mode = 0x0, biff_obfuscated = 0x0, utf8_converter = 0x0, utf16_converter =
0x0, record = {0x41 <repeats 8224 times>}, record_type = 0x4141, prev_record_type = 0x4141,
record_size = 0x41414141, shared_strings = {string_count = 0x41414141, utf8_strings = 0x4141414141414141,
current_index = 0x41414141, current_utf16_buf = 0x4141414141414141, current_utf16_len = 0x41414141,
current_utf16_off = 0x41414141, current_utf16_skip = 0x41414141, next_utf16_skip = 0x41414141}, first_sheet =
0x4141414141414141, last_sheet = 0x4141414141414141, active_sheet = 0x4141414141414141, second_pass = 0x41414141,
format_array = {{format_index = 0x41414141, is_date = 0x41414141, is_datetime = 0x41414141, is_time =
0x41414141} <repeats 43 times>, {format_index = 0x41414141, is_date = 0x0, is_datetime = 0x0, is_time = 0x0},
{format_index = 0x0, is_date = 0x0, is_datetime = 0x0, is_time = 0x0} <repeats 2004 times>}, max_format_index =
0x0, biff_xf_array = {0x0 <repeats 8192 times>}, biff_xf_next_index = 0x0, magic2 = 0xa9f5250}
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7bcc34b in add_sheet_to_workbook (workbook=0x604010, offset=0, visible=0 '\000', type=0 '\000',
name=0x615340 "Worksheet") at freexl.c:1142
1142 workbook->last_sheet->next = sheet;
(gdb) bt
#0 0x00007ffff7bcc34b in add_sheet_to_workbook (workbook=0x604010, offset=0, visible=0 '\000', type=0 '\000',
name=0x615340 "Worksheet") at freexl.c:1142
#1 0x00007ffff7bcf5ca in read_legacy_biff (workbook=0x604010, swap=0) at freexl.c:2465
#2 0x00007ffff7bd37a7 in common_open (path=0x7fffffffe19c "./crashes/82c4a4b19c5ef5b72d11dcf34b9b936a",
xls_handle=0x7fffffffdcf8, magic=1675431287) at freexl.c:4055
#3 0x00007ffff7bd3b3a in freexl_open (path=0x7fffffffe19c "./crashes/82c4a4b19c5ef5b72d11dcf34b9b936a",
xls_handle=0x7fffffffdcf8) at freexl.c:4202
#4 0x0000000000400c3c in main (argc=2, argv=0x7fffffffddf8) at test_xl.c:84
(gdb) p workbook->last_sheet
$7 = (biff_sheet *) 0x4141414141414141
2017-09-06 - Vendor Disclosure
2017-09-11 - Public Release
Discovered by Marcin 'Icewall' Noga of Cisco Talos.