LIEF icon indicating copy to clipboard operation
LIEF copied to clipboard

SEGV / Heap-buffer-overflow in LIEF::DEX::Parser::parse_field / LIEF::DEX::Parser::parse_fields at DEX/Parser.tcc

Open bladchan opened this issue 1 year ago • 1 comments

Describe the bug A bad DEX file which can lead LIEF::DEX::Parser::parse to a heap-buffer-overflow(read)/SEGV issue.

Poc here: For heap-buffer-overflow: dex_buffer_overflow_1.zip For SEGV: dex_segv_4.zip

To Reproduce

  1. Build the whole project with ASAN
  2. Drive program (compile it with ASAN too):
// read_dex.c
#include <LIEF/LIEF.hpp>

int main(int argc, char** argv){
	
	if(argc != 2) return 0;
	
	// DEX
	try {
	    std::unique_ptr<LIEF::DEX::File> dex = LIEF::DEX::Parser::parse(argv[1]);
	    if(dex) std::cout << *dex << std::endl;
	} catch (const LIEF::exception& err) {
	    std::cerr << err.what() << std::endl;
	}

	return 0;
}
  1. Run Poc:
$ ./read_dex ./dex_buffer_overflow_1.bin
$ ./read_dex ./dex_segv_4.bin

Expected behavior The code snippet where the issue happened should avoid the out-bounds read operation. Meanwhile, parse the DEX file without segmentation fault because segmentation fault can cause a Denial of Service (Dos).

Environment (please complete the following information):

  • System and Version : Ubuntu 20.04 + gcc 9.4.0
  • Target format : DEX
  • LIEF commit version: https://github.com/lief-project/LIEF/commit/24935f654f6df700a9a062298258b9485f584502

Additional context ①. ASAN says:

$ ./read_dex dex_buffer_overflow_1.bin
Unknown type: 'o'
Unknown type: 'c'
Unknown type: 'n'
Unknown type: 't'
Unknown type: 't'
Unknown type: 't'
Unknown type: 'e'
Unknown type: 'i'
Unknown type: 'i'
Unknown type: 't'
Unknown type: 'o'
Unknown type: 'o'
Unknown type: 'o'
Unknown type: 'o'
Unknown type: 'o'
Unknown type: 'o'
Unknown type: 'a'
Empty field name
Type #4096 out of bound (19)
Type index for class name is corrupted
Type index for class name is corrupted
=================================================================
==29612==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000138 at pc 0x561caacb0b4e bp 0x7ffc0b90bf30 sp 0x7ffc0b90bf20
READ of size 8 at 0x602000000138 thread T0
    #0 0x561caacb0b4d in std::__uniq_ptr_impl<LIEF::DEX::Field, std::default_delete<LIEF::DEX::Field> >::_M_ptr() const /usr/include/c++/9/bits/unique_ptr.h:154
    #1 0x561caac99671 in std::unique_ptr<LIEF::DEX::Field, std::default_delete<LIEF::DEX::Field> >::get() const /usr/include/c++/9/bits/unique_ptr.h:361
    #2 0x561caac99619 in std::unique_ptr<LIEF::DEX::Field, std::default_delete<LIEF::DEX::Field> >::operator->() const /usr/include/c++/9/bits/unique_ptr.h:355
    #3 0x561caacbadc1 in void LIEF::DEX::Parser::parse_field<LIEF::DEX::details::DEX39>(unsigned long, LIEF::DEX::Class&, bool) /home/ubuntu/test/LIEF/src/DEX/Parser.tcc:583
    #4 0x561caaca3691 in void LIEF::DEX::Parser::parse_class_data<LIEF::DEX::details::DEX39>(unsigned int, LIEF::DEX::Class&) /home/ubuntu/test/LIEF/src/DEX/Parser.tcc:517
    #5 0x561caac8c91b in void LIEF::DEX::Parser::parse_classes<LIEF::DEX::details::DEX39>() /home/ubuntu/test/LIEF/src/DEX/Parser.tcc:463
    #6 0x561caac61b59 in void LIEF::DEX::Parser::parse_file<LIEF::DEX::details::DEX39>() /home/ubuntu/test/LIEF/src/DEX/Parser.tcc:45
    #7 0x561caac57e64 in LIEF::DEX::Parser::init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int) /home/ubuntu/test/LIEF/src/DEX/Parser.cpp:90
    #8 0x561caac57197 in LIEF::DEX::Parser::parse(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/ubuntu/test/LIEF/src/DEX/Parser.cpp:40
    #9 0x561caaac0856 in main /home/ubuntu/test/LIEF/fuzz/read_dex.c:9
    #10 0x7f2f3adbe082 in __libc_start_main ../csu/libc-start.c:308
    #11 0x561caaac055d in _start (/home/ubuntu/test/LIEF/fuzz/read_dex+0x33055d)

0x602000000138 is located 0 bytes to the right of 8-byte region [0x602000000130,0x602000000138)
allocated by thread T0 here:
    #0 0x7f2f3b3e7587 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cc:104
    #1 0x561caacf6ce0 in __gnu_cxx::new_allocator<std::unique_ptr<LIEF::DEX::Field, std::default_delete<LIEF::DEX::Field> > >::allocate(unsigned long, void const*) /usr/include/c++/9/ext/new_allocator.h:114
    #2 0xfff817218a9  (<unknown module>)
    #3 0x561caac8653a in void LIEF::DEX::Parser::parse_types<LIEF::DEX::details::DEX39>() /home/ubuntu/test/LIEF/src/DEX/Parser.tcc:158

SUMMARY: AddressSanitizer: heap-buffer-overflow /usr/include/c++/9/bits/unique_ptr.h:154 in std::__uniq_ptr_impl<LIEF::DEX::Field, std::default_delete<LIEF::DEX::Field> >::_M_ptr() const
Shadow bytes around the buggy address:
  0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff8000: fa fa 00 00 fa fa fd fa fa fa 00 00 fa fa 00 fa
  0x0c047fff8010: fa fa fd fa fa fa 00 fa fa fa fd fa fa fa fd fd
=>0x0c047fff8020: fa fa 04 fa fa fa 00[fa]fa fa fd fa fa fa fd fa
  0x0c047fff8030: fa fa 00 00 fa fa fd fd fa fa fd fa fa fa 00 00
  0x0c047fff8040: fa fa fd fa fa fa 00 00 fa fa fd fa fa fa fd fd
  0x0c047fff8050: fa fa 00 fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==29612==ABORTING

②. ASAN says:

./read_dex dex_segv_4.bin 
Unknown type: ''
Unknown type: ''
...
Name of field #0 is out of bound!
Name of field #1 is out of bound!
AddressSanitizer:DEADLYSIGNAL
=================================================================
==34244==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f4885fbcc08 bp 0x0fffbd3cd4f4 sp 0x7ffde9e6a768 T0)
==34244==The signal is caused by a READ memory access.
==34244==Hint: address points to the zero page.
    #0 0x7f4885fbcc07 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (/lib/x86_64-linux-gnu/libstdc++.so.6+0x145c07)
    #1 0x5612f14b934c in void LIEF::DEX::Parser::parse_fields<LIEF::DEX::details::DEX35>() /home/ubuntu/test/LIEF/src/DEX/Parser.tcc:228
    #2 0x5612f14af7ba in void LIEF::DEX::Parser::parse_file<LIEF::DEX::details::DEX35>() /home/ubuntu/test/LIEF/src/DEX/Parser.tcc:42
    #3 0x5612f14a5dad in LIEF::DEX::Parser::init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int) /home/ubuntu/test/LIEF/src/DEX/Parser.cpp:78
    #4 0x5612f14a5197 in LIEF::DEX::Parser::parse(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/ubuntu/test/LIEF/src/DEX/Parser.cpp:40
    #5 0x5612f130e856 in main /home/ubuntu/test/LIEF/fuzz/read_dex.c:9
    #6 0x7f4885b3f082 in __libc_start_main ../csu/libc-start.c:308
    #7 0x5612f130e55d in _start (/home/ubuntu/test/LIEF/fuzz/read_dex+0x33055d)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/lib/x86_64-linux-gnu/libstdc++.so.6+0x145c07) in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
==34244==ABORTING

bladchan avatar Sep 16 '22 15:09 bladchan

There is another bad DEX file which can also lead LIEF::DEX::Parser::parse to a heap-buffer-overflow(read) issue. Maybe it is the same reason which caused heap-buffer-overflow in DEX/Parser.tcc, so I report it under this issue.

Poc here : dex_buffer_overflow_2.zip

Asan says:

$ ./read_dex dex_buffer_overflow_2.bin
Unknown type: 'g'
Prototype #57856 out of bound (9)
=================================================================
==29981==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000002c0 at pc 0x55aa6060847a bp 0x7ffd3ba08b50 sp 0x7ffd3ba08b40
READ of size 8 at 0x6020000002c0 thread T0
    #0 0x55aa60608479 in std::__uniq_ptr_impl<LIEF::DEX::Method, std::default_delete<LIEF::DEX::Method> >::_M_ptr() const /usr/include/c++/9/bits/unique_ptr.h:154
    #1 0x55aa605ee9e5 in std::unique_ptr<LIEF::DEX::Method, std::default_delete<LIEF::DEX::Method> >::get() const /usr/include/c++/9/bits/unique_ptr.h:361
    #2 0x55aa605ee935 in std::unique_ptr<LIEF::DEX::Method, std::default_delete<LIEF::DEX::Method> >::operator->() const /usr/include/c++/9/bits/unique_ptr.h:355
    #3 0x55aa6060aae5 in void LIEF::DEX::Parser::parse_method<LIEF::DEX::details::DEX35>(unsigned long, LIEF::DEX::Class&, bool) (/home/ubuntu/test/LIEF/fuzz/read_dex+0x527ae5)
    #4 0x55aa605f10f1 in void LIEF::DEX::Parser::parse_class_data<LIEF::DEX::details::DEX35>(unsigned int, LIEF::DEX::Class&) /home/ubuntu/test/LIEF/src/DEX/Parser.tcc:568
    #5 0x55aa605c3721 in void LIEF::DEX::Parser::parse_classes<LIEF::DEX::details::DEX35>() /home/ubuntu/test/LIEF/src/DEX/Parser.tcc:463
    #6 0x55aa605b47e1 in void LIEF::DEX::Parser::parse_file<LIEF::DEX::details::DEX35>() /home/ubuntu/test/LIEF/src/DEX/Parser.tcc:45
    #7 0x55aa605aadad in LIEF::DEX::Parser::init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int) /home/ubuntu/test/LIEF/src/DEX/Parser.cpp:78
    #8 0x55aa605aa197 in LIEF::DEX::Parser::parse(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/ubuntu/test/LIEF/src/DEX/Parser.cpp:40
    #9 0x55aa60413856 in main /home/ubuntu/test/LIEF/fuzz/read_dex.c:9
    #10 0x7fd3f19b2082 in __libc_start_main ../csu/libc-start.c:308
    #11 0x55aa6041355d in _start (/home/ubuntu/test/LIEF/fuzz/read_dex+0x33055d)

0x6020000002c0 is located 0 bytes to the right of 16-byte region [0x6020000002b0,0x6020000002c0)
allocated by thread T0 here:
    #0 0x7fd3f1fdb587 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cc:104
    #1 0x55aa6064c5d6 in __gnu_cxx::new_allocator<std::unique_ptr<LIEF::DEX::Method, std::default_delete<LIEF::DEX::Method> > >::allocate(unsigned long, void const*) /usr/include/c++/9/ext/new_allocator.h:114
    #2 0xfffa7741235  (<unknown module>)

SUMMARY: AddressSanitizer: heap-buffer-overflow /usr/include/c++/9/bits/unique_ptr.h:154 in std::__uniq_ptr_impl<LIEF::DEX::Method, std::default_delete<LIEF::DEX::Method> >::_M_ptr() const
Shadow bytes around the buggy address:
  0x0c047fff8000: fa fa 00 00 fa fa fd fa fa fa 00 00 fa fa 00 fa
  0x0c047fff8010: fa fa fd fa fa fa 00 fa fa fa 04 fa fa fa fd fa
  0x0c047fff8020: fa fa fd fd fa fa 04 fa fa fa fd fa fa fa fd fd
  0x0c047fff8030: fa fa fd fa fa fa 00 fa fa fa fd fd fa fa 00 fa
  0x0c047fff8040: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
=>0x0c047fff8050: fa fa fd fa fa fa 00 00[fa]fa 00 fa fa fa fd fa
  0x0c047fff8060: fa fa 00 00 fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff80a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==29981==ABORTING

bladchan avatar Sep 16 '22 15:09 bladchan