CVE-2017-2910
An exploitable Out-of-bounds Write vulnerability exists in the xls_addCell function of libxls 1.4. 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.
libxls 1.4 readxl package 1.0.0 for R (tested using Microsoft R 4.3.1)
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
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 of the `Microsoft R` programming language. An out-of-bounds write appears in the `xls_addCell` function during parsing of the MULBLANK record. Let's take a look at the vulnerable code:
Line 399 void xls_addCell(xlsWorkSheet* pWS,BOF* bof,BYTE* buf)
Line 400 {
Line 401 (...)
Line 402 row=&pWS->rows.row[((COL*)buf)->row];
Line 403 cell=&row->cells.cell[((COL*)buf)->col-row->fcell];
Line 404
Line 405 cell->id=bof->id;
Line 406 cell->xf=((COL*)buf)->xf;
Line 407
Line 408 switch (bof->id)
Line 409 {
Line 410 case 0x0BE: //MULBLANK
Line 411 for (i=0;i<=*(WORD *)(buf+(bof->size-2))-((COL*)buf)->col;i++)
Line 412 {
Line 413 cell=&row->cells.cell[((COL*)buf)->col-row->fcell+i];
Line 414 // col=row->cols[i];
Line 415 cell->id=bof->id;
Line 416 cell->xf=*((WORD *)(buf+(4+i*2)));
Line 417 cell->str=xls_getfcell(pWS->workbook,cell);
Line 418 }
According to the Microsoft MS-XLS document:
2.4.174 MulBlank
is
The MulBlank record specifies a series of blank cells in a sheet row. This record can store up to 256 IXFCell structures.
At line 411
the amount of IXFCells
is calculated as follows:
*(WORD *)(buf+(bof->size-2))-((COL*)buf)->col where
*(WORD *)(buf+(bof->size-2)) == colLast
and ((COL*)buf)->col == colFirst
In our PoC the MulBlank
record is located at offset 0x1bcc.
Next at line 413
using the loop index i
, further cells are pulled out from a particular row
.
There is no check to ensure that the calculated index :
((COL*)buf)->col-row->fcell+i
does not exceed the available amount of cells in that particular row
.
Also lines
Line 402 row=&pWS->rows.row[((COL*)buf)->row];
Line 403 cell=&row->cells.cell[((COL*)buf)->col-row->fcell];
contain the same vulnerability because :
((COL*)buf)->row
is not checked and its value comes directly from the file. That situation leads to out of bounds writes and finally heap corruption.
(gdb) p/x *bof
$5 = {id = 0xbe, size = 0x52}
(gdb) p *row
$6 = {index = 0, fcell = 0, lcell = 232, height = 1395, flags = 448, xf = 36, xfflags = 0 '\000', cells = {count = 0, cell = 0x607500}}
(gdb) p/x *pWS
$8 = {filepos = 0xb6f, defcolwidth = 0x800, rows = {lastcol = 0xe8, lastrow = 0x6, row = 0x607440}, workbook = 0x603010, colinfo = {count = 0x4c, col = 0x607140}, maxcol = 0x0}
Starting program: /home/icewall/bugs/libxls-0.2.0/build/bin/xls2csv ./crashes/9204a990ea8f1f0d57cf6d7102e166fc
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7bd165c in xls_addCell (pWS=0x606980, bof=0x7fffffffdc10, buf=0x6066b0 "") at xls.c:438
438 cell->str=xls_getfcell(pWS->workbook,cell);
(gdb) bt
#0 0x00007ffff7bd165c in xls_addCell (pWS=0x606980, bof=0x7fffffffdc10, buf=0x6066b0 "") at xls.c:438
#1 0x00007ffff7bd2ceb in xls_parseWorkSheet (pWS=0x606980) at xls.c:875
#2 0x0000000000400aed in main (pintArgc=2, ptstrArgv=0x7fffffffdd78) at xls2csv.c:90
[----------------------------------registers-----------------------------------]
RAX: 0x665fe8 --> 0xbe0000000000be
RBX: 0x0
RCX: 0x0
RDX: 0x6647f0 --> 0x0
RSI: 0x7fffffffb390 --> 0x6469206c6c6100 ('')
RDI: 0x6647f0 --> 0x0
RBP: 0x7fffffffdbf0 --> 0x7fffffffdc30 --> 0x7fffffffdc90 --> 0x400e30 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffdbb0 --> 0x1
RIP: 0x7ffff7bd165c (<xls_addCell+743>: mov QWORD PTR [rax+0x18],rdx)
R8 : 0x645000 --> 0x0
R9 : 0x0
R10: 0x645000 --> 0x0
R11: 0x1
R12: 0x400820 (<_start>: xor ebp,ebp)
R13: 0x7fffffffdd70 --> 0x2
R14: 0x0
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x7ffff7bd1650 <xls_addCell+731>: call 0x7ffff7bcda00 <xls_getfcell@plt>
0x7ffff7bd1655 <xls_addCell+736>: mov rdx,rax
0x7ffff7bd1658 <xls_addCell+739>: mov rax,QWORD PTR [rbp-0x10]
=> 0x7ffff7bd165c <xls_addCell+743>: mov QWORD PTR [rax+0x18],rdx
0x7ffff7bd1660 <xls_addCell+747>: add DWORD PTR [rbp-0x14],0x1
0x7ffff7bd1664 <xls_addCell+751>: mov rax,QWORD PTR [rbp-0x30]
0x7ffff7bd1668 <xls_addCell+755>: movzx eax,WORD PTR [rax+0x2]
0x7ffff7bd166c <xls_addCell+759>: movzx eax,ax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdbb0 --> 0x1
0008| 0x7fffffffdbb8 --> 0x6066b0 --> 0xf000f00020000
0016| 0x7fffffffdbc0 --> 0x7fffffffdc10 --> 0x5200be
0024| 0x7fffffffdbc8 --> 0x606980 --> 0xe8080000000b6f
0032| 0x7fffffffdbd0 --> 0x52 ('R')
0040| 0x7fffffffdbd8 --> 0x26d600000052
0048| 0x7fffffffdbe0 --> 0x665fe8 --> 0xbe0000000000be
0056| 0x7fffffffdbe8 --> 0x607440 --> 0x57300e800000000
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
2017-08-15 - Vendor Disclosure
2017-11-09 - Public Release
Discovered by Marcin 'Icewall' Noga of Cisco Talos.