LIEF
LIEF copied to clipboard
SEGV / Heap-buffer-overflow in LIEF::DEX::Parser::parse_field / LIEF::DEX::Parser::parse_fields at DEX/Parser.tcc
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
- Build the whole project with ASAN
- 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;
}
- 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
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