### SummaryAn exploitable integer overflow vulnerability exists in the xls_appendSST function of libxls 1.4. A specially crafted XLS file can cause memory corruption resulting in remote code execution. An attacker can send a malicious XLS file to trigger this vulnerability.### Tested Versionslibxls 1.4 readxl package 1.0.0 for R (tested using Microsoft R 4.3.1)### Product URLshttp://libxls.sourceforge.net/### CVSSv3 Score8.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### CWECWE-787: Out-of-bounds Write### Detailslibxls 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. Before we will discuss the location where the out-of-bounds write appears, first let's check code where space allocation for that array takes place:“`Line 120 void xls_addSST(xlsWorkBook* pWB,SST* sst,DWORD size)Line 121 {Line 122 verbose("xls_addSST");Line 123Line 124 pWB->sst.continued=0;Line 125 pWB->sst.lastln=0;Line 126 pWB->sst.lastid=0;Line 127 pWB->sst.lastrt=0;Line 128 pWB->sst.lastsz=0;Line 129Line 130 pWB->sst.count = sst->num;Line 131 pWB->sst.string =(struct str_sst_string *)calloc(pWB->sst.count, sizeof(struct str_sst_string));Line 132 xls_appendSST(pWB,&sst->strings,size-8);Line 133 }“`As you can see at `line 131` space for `pWB->sst.string` array is allocated based on the `pWB->sst.count` value. This value is obtained at `line 130` from `sst->num`. The `sst` structure passed as argument is fully controlled by attacker. By controlling the `sst` structure, we can force allocation for any size, for example 1. The malformed `SST` record is located at offset 0x088F:“`088Fh: FC 00 20 20 E7 01 00 00 00 00 00 00 0F 00 00 43 ü. ç……….C 089Fh: 69 73 63 6F 54 61 6C 6F 73 20 20 20 20 20 03 00 iscoTalos .. 08AFh: 00 53 37 38 03 00 00 53 37 39 03 00 00 53 37 36 .S78…S79…S76 08BFh: 04 00 00 53 31 30 33 04 00 00 53 31 30 32 03 00 …S103…S102.. 08CFh: 00 53 37 37 03 00 00 53 37 34 03 00 00 53 37 35 .S77…S74…S75 08DFh: 03 00 00 53 32 39 03 00 00 53 32 38 03 00 00 53 …S29…S28…S 08EFh: 32 37 03 00 00 53 37 33 03 00 00 53 32 32 03 00 27…S73…S22.. 08FFh: 00 .(…)“`The `sst` strcture looks as follows in memory:“`p *sst$2 = { num = 0x0, numofstr = 0x0, strings = 0xf}“`The `num` field is at offset 0x897.With an `sst` structure where values are set like these above, the `pWB->sst.string` array will have space for just one element. Going further inside the `xls_appendSST` function. Each string entry in `sst->strings`, after optional conversion, will be assigned (its address exactly) to separate entry in `pWB->sst.string` array. These string entries look as follows:“`[string_size](SHORT)[flags](BYTE)[string…]example:0F 00 00 43 69 73 63 6F 54 61 6C 6F …….CiscoTalo 73 20 20 20 20 20“`We can observe this in the code below, where the `ret` variable at `line 235` containing a "decoded" string address is assigned to the `pWB->sst.string` array entry at `line 257`. Notice that for each string, `lastid` field value is increased being used as a index in `pWB->sst.string` array.“`Line 135 void xls_appendSST(xlsWorkBook* pWB,BYTE* buf,DWORD size)Line 136 {Line 137 DWORD ln; // String character countLine 138 DWORD ofs; // Current offset in SST bufferLine 139 DWORD rt; // Count of rich text formatting runsLine 140 DWORD sz; // Size of asian phonetic settings blockLine 141 BYTE flag; // String flagsLine 142 BYTE* ret; (…) Line 163 elseLine 164 {Line 165 ln=xlsShortVal(*(WORD_UA *)(buf+ofs));Line 166 rt = 0;Line 167 sz = 0;Line 168Line 169 ofs+=2;Line 170 } (…)Line 231 elseLine 232 {Line 233 ln_toread = min((size-ofs), ln);Line 234Line 235 ret = utf8_decode((buf+ofs), ln_toread, pWB->charset);Line 236Line 237 ln -= ln_toread;Line 238 ofs +=ln_toread;Line 239Line 240 if (xls_debug) {Line 241 printf("String8SST: %s(%u) \n",ret,ln);Line 242 }Line 243 } (…) Line 250 if ( (ln_toread > 0)Line 251 ||(!pWB->sst.continued) )Line 252 {Line 253 // Concat string if it's a continue, or add string in tableLine 254 if (!pWB->sst.continued)Line 255 {Line 256 pWB->sst.lastid++;Line 257 pWB->sst.string[pWB->sst.lastid-1].str=ret;Line 258 }“`In our case, the attempt to assign a second string to that array will cause an out-of-bounds write, resulting in memory corruption and potential code execution.### Crash Information“`Microsoft R crash(96c.954): Access violation – code c0000005 (!!! second chance !!!)*** 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_appendSST+0xa4:00000000`6534a974 4c890cc2 mov qword ptr [rdx+rax*8],r9 ds:00000000`30b49000=????????????????0:000> rrax=0000000000000002 rbx=000000000000001e rcx=0000000000000000rdx=0000000030b48ff0 rsi=0000000031619f70 rdi=0000000000000000rip=000000006534a974 rsp=000000000440ba00 rbp=0000000000000000 r8=0000000000000000 r9=0000000030b4eff0 r10=000000000000001br11=0000000030b4eff0 r12=0000000000002018 r13=0000000000000000r14=0000000000000003 r15=0000000000000003iopl=0 nv up ei pl zr na po nccs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010244readxl!xls_appendSST+0xa4:00000000`6534a974 4c890cc2 mov qword ptr [rdx+rax*8],r9 ds:00000000`30b49000=????????????????0:000> kb 5 # RetAddr : Args to Child : Call Site00 00000000`6534bdf9 : 00000000`31796ff0 00000000`31798fe8 00000000`3161bfb0 00000000`65349fe7 : readxl!xls_appendSST+0xa401 00000000`6534cc96 : 00000000`31619f70 00000000`00000006 00000000`00000000 00000000`0440bd68 : readxl!xls_parseWorkBook+0x3d902 00000000`6538025b : 00000000`0000003e 00000000`0440bc10 00000000`106f0788 00007ffe`155d9a00 : readxl!xls_open+0x13603 00000000`65344d9d : 00000000`00000001 00000000`6cbfdb68 000cc587`59d3b80a 00000000`106f0788 : readxl!ZN11XlsWorkBookC1ERKSs+0x11b04 00000000`65341efa : 00000000`00000000 00000000`0440be80 00000000`0440c700 00000000`6c87662c : readxl!Z10xls_sheetsSs+0x1d0:000> lmv m readxlBrowse full module liststart end module name00000000`65340000 00000000`6543f000 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.04e4Linux version icewall@ubuntu:~/bugs/libxls-1.4.0/build/bin$ valgrind ./xls2csv test/poc.xls ==37508== Memcheck, a memory error detector==37508== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.==37508== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info==37508== Command: ./xls2csv test/poc.xls==37508== ==37508== Invalid write of size 8==37508== at 0x4E4207E: xls_appendSST (xls.c:257)==37508== by 0x4E41D25: xls_addSST (xls.c:132)==37508== by 0x4E43B12: xls_parseWorkBook (xls.c:781)==37508== by 0x4E44F7B: xls_open (xls.c:1272)==37508== by 0x400E80: main (xls2csv.c:108)==37508== Address 0x54607d0 is 0 bytes after a block of size 0 alloc'd==37508== at 0x4C2FB55: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==37508== by 0x4E41CFD: xls_addSST (xls.c:131)==37508== by 0x4E43B12: xls_parseWorkBook (xls.c:781)==37508== by 0x4E44F7B: xls_open (xls.c:1272)“`### Timeline* 2017-10-25 – Vendor Disclosure* 2017-11-15 – Public Release