libxls xls_preparseWorkSheet MULBLANK Code Execution Vulnerability(CVE-2017-12108)

ID SSV:96900
Type seebug
Reporter Root
Modified 2017-11-29T00:00:00



An exploitable integer overflow vulnerability exists in the xls_preparseWorkSheet function of libxls 1.4 when handling a MULBLANK record. 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.

Tested Versions

libxls 1.4 readxl package 1.0.0 for R (tested using Microsoft R 4.3.1)

Product URLs

CVSSv3 Score

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


CWE-680: Integer Overflow to Buffer Overflow


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.

The integer overflow appears in the xls_preparseWorkSheet function, so let's take a look at the vulnerable code: Line 970 void xls_preparseWorkSheet(xlsWorkSheet* pWS) Line 971 { (...) Line 985 read = ole2_read(buf, 1,tmp.size,pWS->workbook->olestr); Line 986 assert(read == tmp.size); Line 987 // xls_showBOF(&tmp); Line 988 switch ( Line 989 { (...) Line 1012 case 0x00BE: //MULBLANK Line 1013 if (pWS->rows.lastcol<xlsShortVal(((MULBLANK*)buf)->col) + (tmp.size - 6)/2 - 1) Line 1014 pWS->rows.lastcol=xlsShortVal(((MULBLANK*)buf)->col) + (tmp.size - 6)/2 - 1; Line 1015 if (pWS->rows.lastrow<xlsShortVal(((MULBLANK*)buf)->row)) Line 1016 pWS->rows.lastrow=xlsShortVal(((MULBLANK*)buf)->row); Line 1017 break;

The general purpose of the xls_preparseWorkSheet function is to obtain the maximal size of col and row value from records present in the worksheet and update the lastcol and lastrow fields with that value. As we can see in lines 1013-1014 an integer overflow can occur. This can have two potential impacts. In one case, the maximum value stored in lastcol will not be updated even if the MULBLANK col field is greater than the value in lastcol. The other case will result in the lastcol value being updated to the overflowed value.

The malformed MULBLANK record is located at offset : 0xCB1F and looks as follows: 0xCB1F BE 00 20 00 AA AA FF FF

Setting breakpoint at line 1007 we can obtain the following information: (gdb) p/x tmp $1 = {id = 0xbe, size = 0x20} (gdb) p/x *((MULBLANK*)buf) $3 = {row = 0xaaaa, col = 0xffff, rk = 0x60f3e4} (gdb) p/x pWS->rows.lastcol $4 = 0x0

stepping further after line 1014: (gdb) p/x pWS->rows.lastcol $5 = 0x2

We see that lastcol field has been updated with overflowed value 0x2. It has further consequences in function xls_makeTable where based on the lastcol field an array for cells is allocated: Line 409 void xls_makeTable(xlsWorkSheet* pWS) Line 410 { Line 412 struct st_row_data* tmp; Line 418 for (t=0;t<=pWS->rows.lastrow;t++) { Line 420 tmp=&pWS->rows.row[t]; (...) Line 425 tmp->cells.count = pWS->rows.lastcol+1; Line 426 tmp->cells.cell=(struct st_cell_data *)calloc(tmp->cells.count,sizeof(struct st_cell_data));

next during the final parsing of the malformed MULBLANK record in xls_addCell, an out of bound write occurs because the col value being used as index for the cell array is greater than amount of allocated elements for that array. Line 446 struct st_cell_data *xls_addCell(xlsWorkSheet* pWS,BOF* bof,BYTE* buf) Line 447 { Line 448 struct st_cell_data* cell; (...) Line 457 cell=&row->cells.cell[xlsShortVal(((COL*)buf)->col)]; Line 458 cell->id=bof->id; Line 459 cell->xf=xlsShortVal(((COL*)buf)->xf);

At line 457 the cell element points to a value outside of the array range which leads to an out-of-bounds write at lines 458,459... Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7bce936 in xls_addCell (pWS=0x60f3a0, bof=0x7fffffffdb80, buf=0x607c50 "\252\252\377\377\273\273\273\273\314\314\314\314\335\335\335\335", <incomplete sequence \345>) at xls.c:458 458 cell->id=bof->id; gdb-peda$ p cell $8 = (struct st_cell_data *) 0xdea898 gdb-peda$ vmmap 0xdea898 Warning: not found or cannot access procfs

Crash Information

``` Microsoft R crash

(94c.c74): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Users\Icewall\Documents\R\win- library\3.4\readxl\libs\x64\readxl.dll - readxl!xls_addCell+0x61: 000000006534b191 668903 mov word ptr [rbx],ax ds:00000000312f5f58=???? 0:000> kb 10 # RetAddr : Args to Child : Call Site 00 000000006534c8e2 : 0000000048332fd0 0000000031135ff0 0000000000000006 0000000000000000 : readxl!xls_addCell+0x61 01 0000000065382714 : 00000000044079c0 0000000000000000 0000000000000000 0000000000000000 : readxl!xls_parseWorkSheet+0x302 02 000000006534572b : 0000000004407750 00000000044076b0 0000000000000000 00007ffe179295c9 : readxl! ZN12XlsWorkSheetC1E11XlsWorkBookiN4Rcpp6VectorILi13ENS1_15PreserveStorageEEEb+0x2b4 03 0000000065343ddb : 00000000044079b0 00007ffe02922b4f 0000000000000000 0000000000000000 : readxl! Z9read_xls_SsiN4Rcpp6VectorILi13ENS_15PreserveStorageEEEbNS_12RObject_ImplIS1_EES4_St6vectorISsSaISsEEbi+0x31b ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Microsoft\R Open\R- 3.4.1\bin\x64\R.dll - 04 000000006c7977cf : 0000000025cecec8 0000000010cfdf98 000000006cbfdbd0 0000000004407cd0 : readxl!readxl_read_xls_+0x1db 05 000000006c797f04 : 0000000000000272 0000000004407cc8 0000000000000139 0000000004407f00 : R!Rf_NewFrameConfirm+0x7b6f 06 000000006c7ee8c8 : 0000000023747fa0 000000002607c7c0 0000000023747fa0 000000006cbfdcb8 : R!Rf_NewFrameConfirm+0x82a4 07 000000006c7f11dd : 000000006cbfdc08 000000006cbfdd20 000000006cbfdc90 000000006c7c070c : R!Rf_eval+0x6f8 08 000000006c7ee6ba : 0000000023748010 0000000014595aa8 0000000023748010 0000000025e0b818 : R!R_execMethod+0x8bd 09 000000006c7f0550 : 0000000031f17618 00000000145dbd58 0000000025f56dc0 00000000264e3c08 : R!Rf_eval+0x4ea 0a 000000006c7f08d2 : 00000000237b8cb8 00000000261e92b0 00000000237b8cb8 00000000264e4270 : R!R_cmpfun1+0xf50 0b 000000006c7e28ba : 000000006cbfdbd0 000000002607f130 0000000014590600 000000006c7f08d2 : R!Rf_applyClosure+0x192 0c 000000006c7ee341 : 0000000031efd850 00000000264e4270 000000002607cf08 000000006c7eebd5 : R!R_initAssignSymbols+0xf27a 0d 000000006c7eeb9c : 0000000031dfaad8 0000002e2375f578 0000000000000000 000000002607c4e8 : R!Rf_eval+0x171 0e 000000006c7ee3dc : 0000000000000000 0000000014590670 000000002607cf08 00000000324006f8 : R!Rf_eval+0x9cc 0f 000000006c82251b : 000000002fa88528 0000000014590788 0000000023992b28 000000006c7f08d2 : R!Rf_eval+0x20c 0:000> lmv m readxl Browse full module list start end module name 0000000065340000 000000006543f000 readxl (export symbols) C:\Users\Icewall\Documents\R\win- library\3.4\readxl\libs\x64\readxl.dll Loaded symbol image file: C:\Users\Icewall\Documents\R\win-library\3.4\readxl\libs\x64\readxl.dll Image path: C:\Users\Icewall\Documents\R\win-library\3.4\readxl\libs\x64\readxl.dll Image name: readxl.dll Browse all global symbols functions data Timestamp: Wed Aug 30 18:38:23 2017 (59A6E9FF) CheckSum: 001018F6 ImageSize: 000FF000 Translations: 0000.04b0 0000.04e4 0409.04b0 0409.04e4

Linux version

[----------------------------------registers-----------------------------------] RAX: 0xdea898 RBX: 0xb6a8c0 --> 0xaaaa0201 RCX: 0x7fffffffdb80 --> 0x2000bd RDX: 0xbe RSI: 0x7fffffffdb80 --> 0x2000bd RDI: 0xffffffff RBP: 0x7fffffffdb60 --> 0x7fffffffdc00 --> 0x7fffffffdc70 --> 0x401610 (<__libc_csu_init>: push r15) RSP: 0x7fffffffdb00 --> 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: 0x7fffffffdd50 --> 0x2 R14: 0x0 R15: 0x0 EFLAGS: 0x10202 (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| 0x7fffffffdb00 --> 0x60f3a0 --> 0x8000000c90b 0008| 0x7fffffffdb08 --> 0x607c50 --> 0xbbbbbbbbffffaaaa 0016| 0x7fffffffdb10 --> 0x7fffffffdb80 --> 0x2000bd 0024| 0x7fffffffdb18 --> 0x60f3a0 --> 0x8000000c90b 0032| 0x7fffffffdb20 --> 0x60f3a0 --> 0x8000000c90b 0040| 0x7fffffffdb28 --> 0x60f360 --> 0x0 0048| 0x7fffffffdb30 --> 0x800000003000000 0056| 0x7fffffffdb38 --> 0xdea898 [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x00007ffff7bce936 in xls_addCell (pWS=0x60f3a0, bof=0x7fffffffdb80, buf=0x607c50 "\252\252\377\377\273\273\273\273\314\314\314\314\335\335\335\335", <incomplete sequence \345>) at xls.c:458 458 cell->id=bof->id; gdb-peda$ bt

0 0x00007ffff7bce936 in xls_addCell (pWS=0x60f3a0, bof=0x7fffffffdb80, buf=0x607c50

"\252\252\377\377\273\273\273\273\314\314\314\314\335\335\335\335", <incomplete sequence \345>) at xls.c:458

1 0x00007ffff7bd0b1a in xls_parseWorkSheet (pWS=0x60f3a0) at xls.c:1150

2 0x0000000000400ff8 in main (argc=0x2, argv=0x7fffffffdd58) at xls2csv.c:149

3 0x00007ffff781c830 in __libc_start_main (main=0x400d78 <main>, argc=0x2, argv=0x7fffffffdd58, init=<optimized out>, fini=

<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdd48) at ../csu/libc- start.c:291

4 0x0000000000400b89 in _start ()

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:0x00000000dea898 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/!xls_addCell+0x0 STACK_FRAME:/home/icewall/bugs/libxls-1.4.0/build/lib/!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