CVE-2017-12111
An exploitable out-of-bounds vulnerability exists in the xls_addCell function of libxls 1.4. A specially crafted XLS file with a formula record can cause memory corruption resulting in remote code execution. An attacker can send a malicious XLS file to trigger this vulnerability.
libxls 1.4
http://libxls.sourceforge.net/
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H CVSSv3 Calculator: https://www.first.org/cvss/calculator/3.0
CWE-787: Out-of-bounds Write
libxls is a C library supported on windows, mac, cygwin which can read Microsoft Excel File Format ( XLS ) files. The library is used by the readxl package that can be installed in the R programming language. However this particular vulnerability does not impact the readxl package.
To analyze this vulnerability, we need to start at the xls_preparseWorkSheet
function.
Line 970 void xls_preparseWorkSheet(xlsWorkSheet* pWS)
Line 971 {
(...)
Line 1018 case 0x0203: //NUMBER
Line 1019 case 0x027e: //RK
Line 1020 case 0x00FD: //LABELSST
Line 1021 case 0x0201: //BLANK
Line 1022 case 0x0204: //LABEL
Line 1023 case 0x0006: //FORMULA
Line 1024 if (pWS->rows.lastcol<xlsShortVal(((COL*)buf)->col))
Line 1025 pWS->rows.lastcol=xlsShortVal(((COL*)buf)->col);
Line 1026 if (pWS->rows.lastrow<xlsShortVal(((COL*)buf)->row))
Line 1027 pWS->rows.lastrow=xlsShortVal(((COL*)buf)->row);
Line 1028 break;
Line 1029 }
The xls_preparseWorkSheet
function iterates over all existing records and tries to find a maximum value for the col
and row
fields, these values are stored in pWS->rows.lastcol
and pWS->rows.lastcol
, respectively.
These values are later used for buffers allocations inside the xls_makeTable
function:
Line 409 void xls_makeTable(xlsWorkSheet* pWS)
Line 410 {
(...)
Line 415 pWS->rows.row=(struct st_row_data *)calloc((pWS->rows.lastrow+1),sizeof(struct st_row_data));
(...)
Line 426 tmp->cells.cell=(struct st_cell_data *)calloc(tmp->cells.count,sizeof(struct st_cell_data));
Later in the xls_parseWorkSheet
function, the xls_addCell
function is called for nearly the same set of records that we saw in the xls_preparseWorkSheet
but with one exception, FORMULA (Apple Numbers Bug)
:
Line 1063 void xls_parseWorkSheet(xlsWorkSheet* pWS)
Line 1064 {
(...)
Line 1141 case 0x00BD: //MULRK
Line 1142 case 0x00BE: //MULBLANK
Line 1143 case 0x0203: //NUMBER
Line 1144 case 0x027e: //RK
Line 1145 case 0x00FD: //LABELSST
Line 1146 case 0x0201: //BLANK
Line 1147 case 0x0204: //LABEL
Line 1148 case 0x0006: //FORMULA
Line 1149 case 0x0406: //FORMULA (Apple Numbers Bug)
Line 1150 cell = xls_addCell(pWS,&tmp,buf);
We did not see FORMULA (Apple Numbers Bug)
with id 0x0406
in thexls_preparseWorkSheet
function. This means that for this record, the fields col
and row
have not been checked whether
their value exceed the current maximum values stored in pWS->rows.lastcol
, pWS->rows.lastrow
.
That lack of check for this type of record leads to an out of bound write in the xls_addCell
function. Let’s take a look at the vulnerable code:
Line 446 struct st_cell_data *xls_addCell(xlsWorkSheet* pWS,BOF* bof,BYTE* buf)
Line 447 {
Line 448 struct st_cell_data* cell;
Line 449 struct st_row_data* row;
Line 450 int i;
Line 451
Line 452 verbose ("xls_addCell");
Line 453
Line 454 // printf("ROW: %u COL: %u\n", xlsShortVal(((COL*)buf)->row), xlsShortVal(((COL*)buf)->col));
Line 455 row=&pWS->rows.row[xlsShortVal(((COL*)buf)->row)];
Line 456 //cell=&row->cells.cell[((COL*)buf)->col - row->fcell]; DFH - inconsistent
Line 457 cell=&row->cells.cell[xlsShortVal(((COL*)buf)->col)];
Line 458 cell->id=bof->id;
Line 459 cell->xf=xlsShortVal(((COL*)buf)->xf);
The col
value coming from the record is used as an index for the row->cells.cell
array at line 457
.
The malformed Formula
record is located at offset : 0xCB1F
CB1Fh: 06 04 20 00 00 01 FF FF .. ...ÿÿ
`col` field is set to 0xFFFF
Because this value has not been checked, it can easily exceed array’s range. Write operations at lines 458-459
can subsequently cause an out-of-bounds write which subsequently leads to memory corruption.
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x27fff9
RBX: 0x21 ('!')
RCX: 0x7fffffffdb40 --> 0x200406
RDX: 0x406
RSI: 0x7fffffffdb40 --> 0x200406
RDI: 0xffffffff
RBP: 0x7fffffffdb20 --> 0x7fffffffdbc0 --> 0x7fffffffdc30 --> 0x401610 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffdac0 --> 0x60f3a0 --> 0x8000000c90b
RIP: 0x7ffff7bce936 (<xls_addCell+144>: mov WORD PTR [rax],dx)
R8 : 0x60f360 --> 0x0
R9 : 0x800000003000000
R10: 0x6f ('o')
R11: 0x7ffff7bce8a6 (<xls_addCell>: push rbp)
R12: 0x400b60 (<_start>: xor ebp,ebp)
R13: 0x7fffffffdd10 --> 0x2
R14: 0x0
R15: 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x7ffff7bce92b <xls_addCell+133>: mov rax,QWORD PTR [rbp-0x50]
0x7ffff7bce92f <xls_addCell+137>: movzx edx,WORD PTR [rax]
0x7ffff7bce932 <xls_addCell+140>: mov rax,QWORD PTR [rbp-0x28]
=> 0x7ffff7bce936 <xls_addCell+144>: mov WORD PTR [rax],dx
0x7ffff7bce939 <xls_addCell+147>: mov rax,QWORD PTR [rbp-0x58]
0x7ffff7bce93d <xls_addCell+151>: movzx eax,WORD PTR [rax+0x4]
0x7ffff7bce941 <xls_addCell+155>: cwde
0x7ffff7bce942 <xls_addCell+156>: mov edi,eax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdac0 --> 0x60f3a0 --> 0x8000000c90b
0008| 0x7fffffffdac8 --> 0x6077d0 --> 0xbbbbbbbbffff0100
0016| 0x7fffffffdad0 --> 0x7fffffffdb40 --> 0x200406
0024| 0x7fffffffdad8 --> 0x60f3a0 --> 0x8000000c90b
0032| 0x7fffffffdae0 --> 0x60f3a0 --> 0x8000000c90b
0040| 0x7fffffffdae8 --> 0x60f360 --> 0x0
0048| 0x7fffffffdaf0 --> 0x800000003000000
0056| 0x7fffffffdaf8 --> 0x27fff9
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007ffff7bce936 in xls_addCell (pWS=0x60f3a0, bof=0x7fffffffdb40, buf=0x6077d0 "") at xls.c:458
458 cell->id=bof->id;
gdb-peda$ bt
#0 0x00007ffff7bce936 in xls_addCell (pWS=0x60f3a0, bof=0x7fffffffdb40, buf=0x6077d0 "") at xls.c:458
#1 0x00007ffff7bd0b1a in xls_parseWorkSheet (pWS=0x60f3a0) at xls.c:1150
#2 0x0000000000400ff8 in main (argc=0x2, argv=0x7fffffffdd18) at xls2csv.c:149
#3 0x00007ffff781c830 in __libc_start_main (main=0x400d78 <main>, argc=0x2, argv=0x7fffffffdd18, init=<optimized out>, fini=
<optimized
out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdd08) at ../csu/libc-start.c:291
#4 0x0000000000400b89 in _start ()
gdb-peda$ source ~/tools/triage_tools/exploitable/exploitable/exploitable.py
gdb-peda$ exploitable -m
__main__:102: UserWarning: GDB v7.11 may not support required Python API
Warning: machine string printing is deprecated and may be removed in a future release.
EXCEPTION_FAULTING_ADDRESS:0x0000000027fff9
EXCEPTION_CODE:0xb
FAULTING_INSTRUCTION:mov WORD PTR [rax],dx
MAJOR_HASH:34754c1fb7e7f36e18e374f783e4c876
MINOR_HASH:34754c1fb7e7f36e18e374f783e4c876
STACK_DEPTH:3
STACK_FRAME:/home/icewall/bugs/libxls-1.4.0/build/lib/libxlsreader.so.1.2.1!xls_addCell+0x0
STACK_FRAME:/home/icewall/bugs/libxls-1.4.0/build/lib/libxlsreader.so.1.2.1!xls_parseWorkSheet+0x0
STACK_FRAME:/home/icewall/bugs/libxls-1.4.0/build/bin/xls2csv!main+0x0
INSTRUCTION_ADDRESS:0x007ffff7bce936
INVOKING_STACK_FRAME:0
DESCRIPTION:Access violation on destination operand
SHORT_DESCRIPTION:DestAv (9/29)
OTHER_RULES:AccessViolation (28/29)
CLASSIFICATION:EXPLOITABLE
EXPLANATION:The target crashed on an access violation at an address matching the destination operand of the instruction. This
likely indicates a write access violation, which means the attacker may control the write address and/or value.
Description: Access violation on destination operand
Short description: DestAv (9/29)
Hash: 34754c1fb7e7f36e18e374f783e4c876.34754c1fb7e7f36e18e374f783e4c876
Exploitability Classification: EXPLOITABLE
Explanation: The target crashed on an access violation at an address matching the destination operand of the instruction. This
likely indicates a write access violation, which means the attacker may control the write address and/or value.
Other tags: AccessViolation (28/29)
2017-10-25 - Vendor Disclosure
2017-11-15 - Public Release
Discovered by Marcin 'Icewall' Noga of Cisco Talos.