Vulnerabilities > CVE-2017-2897 - Out-of-bounds Write vulnerability in Libxls Project Libxls 1.4.0

047910
CVSS 6.8 - MEDIUM
Attack vector
NETWORK
Attack complexity
MEDIUM
Privileges required
NONE
Confidentiality impact
PARTIAL
Integrity impact
PARTIAL
Availability impact
PARTIAL
network
libxls-project
CWE-787
nessus

Summary

An exploitable out-of-bounds write vulnerability exists in the read_MSAT 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.

Vulnerable Configurations

Part Description Count
Application
Libxls_Project
1

Common Weakness Enumeration (CWE)

Nessus

  • NASL familyDebian Local Security Checks
    NASL idDEBIAN_DSA-4173.NASL
    descriptionMarcin Noga discovered multiple vulnerabilities in readxl, a GNU R package to read Excel files (via the integrated libxls library), which could result in the execution of arbitrary code if a malformed spreadsheet is processed.
    last seen2020-06-01
    modified2020-06-02
    plugin id109065
    published2018-04-17
    reporterThis script is Copyright (C) 2018 and is owned by Tenable, Inc. or an Affiliate thereof.
    sourcehttps://www.tenable.com/plugins/nessus/109065
    titleDebian DSA-4173-1 : r-cran-readxl - security update
  • NASL familyGentoo Local Security Checks
    NASL idGENTOO_GLSA-202003-64.NASL
    descriptionThe remote host is affected by the vulnerability described in GLSA-202003-64 (libxls: Multiple vulnerabilities) Multiple vulnerabilities have been discovered in libxls. Please review the CVE identifiers referenced below for details. Impact : A remote attacker could entice a user to process a specially crafted Excel file using libxls, possibly resulting in execution of arbitrary code with the privileges of the process or a Denial of Service condition. Workaround : There is no known workaround at this time.
    last seen2020-04-04
    modified2020-03-31
    plugin id135019
    published2020-03-31
    reporterThis script is Copyright (C) 2020 and is owned by Tenable, Inc. or an Affiliate thereof.
    sourcehttps://www.tenable.com/plugins/nessus/135019
    titleGLSA-202003-64 : libxls: Multiple vulnerabilities

Seebug

bulletinFamilyexploit
description### Summary An exploitable out-of-bounds write vulnerability exists in the read_MSAT 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. ### Tested Versions libxls 1.4 readxl package 1.0.0 for R (tested using Microsoft R 4.3.1) ### Product URLs http://libxls.sourceforge.net/ ### 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: https://www.first.org/cvss/calculator/3.0 ### CWE CWE-787: Out-of-bounds Write ### Details libxls is a C library supported on Windows, Mac and Linux 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. An out-of-bounds write appears in the read_MSAT function. Let's take a look at the vulnerable code: ``` Line 425 // Read MSAT Line 426 static int read_MSAT(OLE2* ole2, OLE2Header* oleh) Line 427 { Line 428 int sectorNum; Line 429 Line 430 // reconstitution of the MSAT Line 431 ole2->SecID = malloc(ole2->cfat*ole2->lsector); Line 432 (...) Line 433 int posInSector; Line 434 Line 435 // read MSAT sector Line 436 sector_read(ole2, sector, sid); Line 437 // read content Line 438 for (posInSector = 0; posInSector < (ole2->lsector-4)/4; posInSector++) Line 439 { Line 440 unsigned int s = *(int*)(sector + posInSector*4); Line 441 Line 442 if (s != FREESECT) Line 443 { Line 444 sector_read(ole2, (BYTE*)(ole2->SecID)+sectorNum*ole2->lsector, s); Line 445 sectorNum++; Line 446 } Line 447 } ``` As we can see in `line 431 ole2->SecID` buffer is allocated based on `cfat` and `lsector` value. The `cfat` value is read directly from the file (in our PoC cfat has value 0x1) where lsector has fixed size 0x200. Next in `lines 438-447` we see that further "sectors" are read from the file (via sector_read) to the `ole2->SecID` buffer in the amount of `(ole2->lsector-4)/4`. We can observe a lack of any check whether the new calculated offset for "sector" inside the `SecID` buffer does not exceed the buffer size allocated earlier. This thus leads to out of bounds writes and heap memory corruption, which can potentially lead to arbitrary code execution. ### Crash Information ``` Crash in Microsoft R platform: library(readxl) path <- readxl_example("509075387a944995bb90bf109fe8191b.xls") lapply(excel_sheets(path), read_excel, path = path) (...) fread: wanted 1 got 0 loc=68988965376 fread: wanted 1 got 0 loc=68988965376 fread: wanted 1 got 0 loc=68988965376 fread: wanted 1 got 0 loc=68988965376 fread: wanted 1 got 0 loc=68988965376 fread: wanted 1 got 0 loc=541435367936 fread: wanted 1 got 0 loc=1319413954048 fread: wanted 1 got 0 loc=547393582080 fread: wanted 1 got 0 loc=8355054592 fread: wanted 1 got 0 loc=132608 fread: wanted 1 got 0 loc=75611136 fread: wanted 1 got 0 loc=838861312 fread: wanted 1 got 0 loc=1536 fread: wanted 1 got 0 loc=67160064 fread: wanted 1 got 0 loc=637534720 fread: wanted 1 got 0 loc=137438955008 fread: wanted 1 got 0 loc=537149952 *** caught segfault *** address 0x30895e0, cause 'memory not mapped' Segmentation fault directly in libxls lib: Starting program: /home/icewall/bugs/libxls-1.4.0/build/bin/xls2csv ./crashes/509075387a944995bb90bf109fe8191b Program received signal SIGSEGV, Segmentation fault. __mempcpy_sse2 () at ../sysdeps/x86_64/memcpy.S:125 125 ../sysdeps/x86_64/memcpy.S: No such file or directory. (gdb) bt #0 __mempcpy_sse2 () at ../sysdeps/x86_64/memcpy.S:125 #1 0x00007ffff787903e in __GI__IO_file_xsgetn (fp=0x603310, data=<optimized out>, n=512) at fileops.c:1392 #2 0x00007ffff786e236 in __GI__IO_fread (buf=<optimized out>, size=512, count=1, fp=0x603310) at iofread.c:38 #3 0x00007ffff7bd03e6 in sector_read (ole2=0x6032b0, buffer=0x623f50 '\b' <repeats 35 times>, "?\232\231\231\231\231\231\271? \001", sid=0) at ole.c:421 #4 0x00007ffff7bd0525 in read_MSAT (ole2=0x6032b0, oleh=0x6030a0) at ole.c:462 #5 0x00007ffff7bcfe33 in ole2_open (file=0x7fffffffe12c "./crashes/509075387a944995bb90bf109fe8191b", charset=0x400fce "iso-8859-15//TRANSLIT") at ole.c:327 #6 0x00007ffff7bd2e00 in xls_open (file=0x7fffffffe12c "./crashes/509075387a944995bb90bf109fe8191b", charset=0x400fce "iso-8859-15//TRANSLIT") at xls.c:910 #7 0x0000000000400957 in main (pintArgc=2, ptstrArgv=0x7fffffffdd78) at xls2csv.c:45 (gdb) frame 4 #4 0x00007ffff7bd0525 in read_MSAT (ole2=0x6032b0, oleh=0x6030a0) at ole.c:462 462 sector_read(ole2, (BYTE*)(ole2->SecID)+sectorNum*ole2->lsector, s); (gdb) p/x sectorNum $2 = 0xfd (gdb) p/x ole2->lsector $3 = 0x200 (gdb) p/x ole2->SecID $4 = 0x604550 (gdb) peda_active gdb-peda$ vmmap 0x604550 Start End Perm Name 0x00603000 0x00624000 rw-p [heap] gdb-peda$ 0x00624000-0x604550 Undefined command: "0x00624000-0x604550". Try "help". gdb-peda$ p 0x00624000-0x604550 $5 = 0x1fab0 gdb-peda$ p (BYTE*)(ole2->SecID)+sectorNum*ole2->lsector $6 = (BYTE *) 0x623f50 '\b' <repeats 35 times>, "?\232\231\231\231\231\231\271?\001" gdb-peda$ frame 3 #3 0x00007ffff7bd03e6 in sector_read (ole2=0x6032b0, buffer=0x623f50 '\b' <repeats 35 times>, "?\232\231\231\231\231\231\271? \001", sid=0x0) at ole.c:421 421 fread(buffer, ole2->lsector, 1, ole2->file); [----------------------------------registers-----------------------------------] RAX: 0xffffffffffffffff RBX: 0x603310 --> 0xfbad2488 RCX: 0xffffffffffffffff RDX: 0x10 RSI: 0x6035e3 --> 0xffffffffffffffff RDI: 0x623ff3 --> 0xffffffffffffffff RBP: 0xb3 RSP: 0x7fffffffdac8 --> 0x7ffff787903e (<__GI__IO_file_xsgetn+382>: add QWORD PTR [rbx+0x8],rbp) RIP: 0x7ffff788f41a (<__mempcpy_sse2+106>: mov QWORD PTR [rdi+0x8],r8) R8 : 0x6e69ffffffffffff R9 : 0xffffffffffffffff R10: 0xffffffffffffffff R11: 0x246 R12: 0x14d R13: 0x200 R14: 0x623f50 --> 0x808080808080808 R15: 0x0 EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x7ffff788f410 <__mempcpy_sse2+96>: mov rcx,QWORD PTR [rsi] 0x7ffff788f413 <__mempcpy_sse2+99>: mov r8,QWORD PTR [rsi+0x8] 0x7ffff788f417 <__mempcpy_sse2+103>: mov QWORD PTR [rdi],rcx => 0x7ffff788f41a <__mempcpy_sse2+106>: mov QWORD PTR [rdi+0x8],r8 0x7ffff788f41e <__mempcpy_sse2+110>: sub edx,0x10 0x7ffff788f421 <__mempcpy_sse2+113>: lea rsi,[rsi+0x10] 0x7ffff788f425 <__mempcpy_sse2+117>: lea rdi,[rdi+0x10] 0x7ffff788f429 <__mempcpy_sse2+121>: jne 0x7ffff788f410 <__mempcpy_sse2+96> [------------------------------------stack-------------------------------------] 0000| 0x7fffffffdac8 --> 0x7ffff787903e (<__GI__IO_file_xsgetn+382>: add QWORD PTR [rbx+0x8],rbp) 0008| 0x7fffffffdad0 --> 0x603310 --> 0xfbad2488 0016| 0x7fffffffdad8 --> 0x200 0024| 0x7fffffffdae0 --> 0x200 0032| 0x7fffffffdae8 --> 0x1 0040| 0x7fffffffdaf0 --> 0x0 0048| 0x7fffffffdaf8 --> 0x7ffff786e236 (<__GI__IO_fread+150>: test DWORD PTR [rbx],0x8000) 0056| 0x7fffffffdb00 --> 0x400820 (<_start>: xor ebp,ebp) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV gdb-peda$ ``` ### Timeline * 2017-08-29 - Vendor Disclosure * 2017-11-15 - Public Release
idSSV:96902
last seen2017-12-25
modified2017-11-29
published2017-11-29
reporterRoot
titlelibxls read_MSAT Code Execution Vulnerability(CVE-2017-2897)

Talos

idTALOS-2017-0404
last seen2019-05-29
published2017-11-15
reporterTalos Intelligence
sourcehttp://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0404
titlelibxls read_MSAT Code Execution Vulnerability