Headline
CVE-2021-43311: [bug] multi heap buffer overflows in get_le32() · Issue #380 · upx/upx
A heap-based buffer overflow was discovered in upx, during the generic pointer ‘p’ points to an inaccessible address in func get_le32(). The problem is essentially caused in PackLinuxElf32::elf_lookup() at p_lx_elf.cpp:5382.
What’s the problem (or question)?
Multi heap-based buffer overflows were discovered in upx, during the genric pointer ‘p’ points to an inaccessible address in func get_le32(). The issue can be triggered by different places, which can cause a denial of service. The issue is diff from issue365
ASAN reports:
==112024==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61d00001f3b1 at pc 0x0000005292cb bp 0x7fffc3995640 sp 0x7fffc3995630 READ of size 4 at 0x61d00001f3b1 thread T0 #0 0x5292ca in get_le32(void const*) /home/test/Desktop/EVAULATION/upx/src/bele.h:164 #1 0x5292ca in N_BELE_RTP::LEPolicy::get32(void const*) const /home/test/Desktop/EVAULATION/upx/src/bele_policy.h:192 #2 0x4589c1 in Packer::get_te32(void const*) const /home/test/Desktop/EVAULATION/upx/src/packer.h:296 #3 0x4589c1 in PackLinuxElf32::elf_lookup(char const*) const /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:5382 #4 0x463d30 in PackLinuxElf32::PackLinuxElf32help1(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:315 #5 0x464e96 in PackLinuxElf32Le::PackLinuxElf32Le(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.h:395 #6 0x464e96 in PackLinuxElf32x86::PackLinuxElf32x86(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:4800 #7 0x464e96 in PackBSDElf32x86::PackBSDElf32x86(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:4817 #8 0x464e96 in PackFreeBSDElf32x86::PackFreeBSDElf32x86(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:4828 #9 0x4f337a in PackMaster::visitAllPackers(Packer* (*)(Packer*, void*), InputFile*, options_t const*, void*) /home/test/Desktop/EVAULATION/upx/src/packmast.cpp:190 #10 0x4f50f9 in PackMaster::getUnpacker(InputFile*) /home/test/Desktop/EVAULATION/upx/src/packmast.cpp:248 #11 0x4f521f in PackMaster::unpack(OutputFile*) /home/test/Desktop/EVAULATION/upx/src/packmast.cpp:266 #12 0x52a1e6 in do_one_file(char const*, char*) /home/test/Desktop/EVAULATION/upx/src/work.cpp:160 #13 0x52a69e in do_files(int, int, char**) /home/test/Desktop/EVAULATION/upx/src/work.cpp:271 #14 0x403ace in main /home/test/Desktop/EVAULATION/upx/src/main.cpp:1538 #15 0x7efc08e6182f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #16 0x404828 in _start (/home/test/Desktop/EVAULATION/upx/src/upx.out+0x404828)
0x61d00001f3b1 is located 189 bytes to the right of 2164-byte region [0x61d00001ea80,0x61d00001f2f4) allocated by thread T0 here: #0 0x7efc09a55602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602) #1 0x42732a in MemBuffer::alloc(unsigned long long) /home/test/Desktop/EVAULATION/upx/src/mem.cpp:194
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/test/Desktop/EVAULATION/upx/src/bele.h:164 get_le32(void const*) Shadow bytes around the buggy address: 0x0c3a7fffbe20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c3a7fffbe30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c3a7fffbe40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c3a7fffbe50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 fa 0x0c3a7fffbe60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x0c3a7fffbe70: fa fa fa fa fa fa[fa]fa fa fa fa fa fa fa fa fa 0x0c3a7fffbe80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c3a7fffbe90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c3a7fffbea0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c3a7fffbeb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c3a7fffbec0: 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 Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 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 ==112024==ABORTING
Debug
Program received signal SIGSEGV, Segmentation fault.
0x000000000066f8f8 in get_le32 (p=0xa1fffd) at bele.h:164
164 return ACC_UA_GET_LE32§;
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x0
$rbx : 0xa7ce93
$rcx : 0x2
$rdx : 0xae
$rsp : 0x00007fffffffcc18 → 0x000000000051249b → <PackLinuxElf32::elf_lookup(char+0> xor eax, ebp
$rbp : 0x8e8223e2
$rsi : 0x0000000000a1fffd → 0x0000000000a1fffd
$rdi : 0x00000000009ed3c0 → 0x00000000007cd9c0 → 0x000000000066fe00 → <N_BELE_RTP::LEPolicy::~LEPolicy()+0> lea rsp, [rsp-0x98]
$rip : 0x000000000066f8f8 → <N_BELE_RTP::LEPolicy::get32(void+0> mov eax, DWORD PTR [rsi]
$r8 : 0x1f
$r9 : 0x3fdf
$r10 : 0xae
$r11 : 0x1d9577c0
$r12 : 0x0000000000a00030 → 0x00000000007267a0 → <vtable+0> add BYTE PTR [rax], al
$r13 : 0x0000000000a1fffd → 0x0000000000a1fffd
$r14 : 0x0000000000a00a51 → 0x0000000000000000
$r15 : 0x00000000007cd9c0 → 0x000000000066fe00 → <N_BELE_RTP::LEPolicy::~LEPolicy()+0> lea rsp, [rsp-0x98]
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffcc18│+0x0000: 0x000000000051249b → <PackLinuxElf32::elf_lookup(char+0> xor eax, ebp ← $rsp
0x00007fffffffcc20│+0x0008: 0x00000000006cdffe → “JNI_OnLoad”
0x00007fffffffcc28│+0x0010: 0x0000000200000000
0x00007fffffffcc30│+0x0018: 0x000000000900457f
0x00007fffffffcc38│+0x0020: 0x0000000000000068 ("h"?)
0x00007fffffffcc40│+0x0028: 0x0000000000000000
0x00007fffffffcc48│+0x0030: 0x0000000000070000
0x00007fffffffcc50│+0x0038: 0x0000000000000000
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x66f8e7 <N_BELE_RTP::LEPolicy::get32(void+0> mov rcx, QWORD PTR [rsp+0x8]
0x66f8ec <N_BELE_RTP::LEPolicy::get32(void+0> mov rdx, QWORD PTR [rsp]
0x66f8f0 <N_BELE_RTP::LEPolicy::get32(void+0> lea rsp, [rsp+0x98]
→ 0x66f8f8 <N_BELE_RTP::LEPolicy::get32(void+0> mov eax, DWORD PTR [rsi]
0x66f8fa <N_BELE_RTP::LEPolicy::get32(void+0> ret
0x66f8fb nop DWORD PTR [rax+rax*1+0x0]
0x66f900 <N_BELE_RTP::LEPolicy::get64(void+0> lea rsp, [rsp-0x98]
0x66f908 <N_BELE_RTP::LEPolicy::get64(void+0> mov QWORD PTR [rsp], rdx
0x66f90c <N_BELE_RTP::LEPolicy::get64(void+0> mov QWORD PTR [rsp+0x8], rcx
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── source:bele.h+164 ────
159 }
160
161 inline unsigned get_le32(const void *p)
162 {
163 #if defined(ACC_UA_GET_LE32)
→ 164 return ACC_UA_GET_LE32§;
165 #else
166 return acc_ua_get_le32§;
167 #endif
168 }
169
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "upx.out", stopped, reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x66f8f8 → get_le32(p=0xa1fffd)
[#1] 0x66f8f8 → N_BELE_RTP::LEPolicy::get32(this=0x9ed3c0 <N_BELE_RTP::le_policy>, p=0xa1fffd)
[#2] 0x51249b → Packer::get_te32(this=0xa00030, p=0xa1fffd)
[#3] 0x51249b → PackLinuxElf32::elf_lookup(this=0xa00030, name=0x6cdffe “JNI_OnLoad”)
[#4] 0x529509 → PackLinuxElf32::PackLinuxElf32help1(this=0xa00030, f=0x7fffffffce10)
[#5] 0x52c65e → PackLinuxElf32Le::PackLinuxElf32Le(f=0x7fffffffce10, this=0xa00030)
[#6] 0x52c65e → PackLinuxElf32x86::PackLinuxElf32x86(f=0x7fffffffce10, this=0xa00030)
[#7] 0x52c65e → PackBSDElf32x86::PackBSDElf32x86(f=0x7fffffffce10, this=0xa00030)
[#8] 0x52c65e → PackFreeBSDElf32x86::PackFreeBSDElf32x86(this=0xa00030, f=0x7fffffffce10)
[#9] 0x60448c → PackMaster::visitAllPackers(func=0x602c30 <try_unpack(Packer*, void*)>, f=0x7fffffffce10, o=0x7fffffffcfc8, user=0x7fffffffce10)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ bt
#0 0x000000000066f8f8 in get_le32 (p=0xa1fffd) at bele.h:164
#1 N_BELE_RTP::LEPolicy::get32 (this=0x9ed3c0 <N_BELE_RTP::le_policy>, p=0xa1fffd) at bele_policy.h:192
#2 0x000000000051249b in Packer::get_te32 (this=0xa00030, p=0xa1fffd) at packer.h:296
#3 PackLinuxElf32::elf_lookup (this=0xa00030, name=0x6cdffe “JNI_OnLoad”) at p_lx_elf.cpp:5382
#4 0x0000000000529509 in PackLinuxElf32::PackLinuxElf32help1 (this=this@entry=0xa00030, f=f@entry=0x7fffffffce10) at p_lx_elf.cpp:315
#5 0x000000000052c65e in PackLinuxElf32Le::PackLinuxElf32Le (f=0x7fffffffce10, this=0xa00030) at p_lx_elf.h:395
#6 PackLinuxElf32x86::PackLinuxElf32x86 (f=0x7fffffffce10, this=0xa00030) at p_lx_elf.cpp:4800
#7 PackBSDElf32x86::PackBSDElf32x86 (f=0x7fffffffce10, this=0xa00030) at p_lx_elf.cpp:4817
#8 PackFreeBSDElf32x86::PackFreeBSDElf32x86 (this=0xa00030, f=0x7fffffffce10) at p_lx_elf.cpp:4828
#9 0x000000000060448c in PackMaster::visitAllPackers (func=0x602c30 <try_unpack(Packer*, void*)>, f=0x7fffffffce10, o=0x7fffffffcfc8, user=0x7fffffffce10) at packmast.cpp:190
#10 0x00000000006072ca in PackMaster::getUnpacker (f=<optimized out>) at packmast.cpp:248
#11 PackMaster::unpack (this=0x7fffffffcfb0, fo=0x7fffffffcee0) at packmast.cpp:266
#12 0x0000000000670dc5 in do_one_file (iname=iname@entry=0x7fffffffdf15 "id:000089,sig:11,src:001286,op:MOpt-core-havoc,rep:2", oname=oname@entry=0x7fffffffd550 “/dev/null”) at work.cpp:160
#13 0x000000000067157c in do_files (i=i@entry=0x4, argc=0x5, argv=0x7fffffffdac8) at work.cpp:271
#14 0x00000000004056a1 in main (argc=0x5, argv=0x7fffffffdac8) at main.cpp:1538
Deferencing a generic poniter ‘p’ trigger the overflow.
gef➤ p *p Attempt to dereference a generic pointer. gef➤ p p $1 = (const void *) 0xa1fffd
Essentially, the problem is caused in PackLinuxElf32::elf_lookup() at p_lx_elf.cpp:5382
do if (0==((h ^ get_te32(hp))>>1)) { unsigned st_name = get_te32(&dsp->st_name); char const *const p = get_str_name(st_name, (unsigned)-1); if (0==strcmp(name, p)) { return dsp; } } while (++dsp, 0==(1u& get_te32(hp++)));
Several locations will also trigger vulnerabilities:
PackLinuxElf32::elf_lookup() at p_lx_elf.cpp:5368
unsigned const w = get_te32(&bitmask[(n_bitmask -1) & (h>>5)]);
PackLinuxElf64::elf_lookup() at p_lx_elf.cpp:5404
for (si= get_te32(&buckets[m]); 0!=si; si= get_te32(&chains[si]))
PackLinuxElf32::elf_lookup() at p_lx_elf.cpp:5349
for (si= get_te32(&buckets[m]); 0!=si; si= get_te32(&chains[si])) { char const *const p= get_dynsym_name(si, (unsigned)-1); if (0==strcmp(name, p)) { return &dynsym[si]; } }
What should have happened?
Decompress a crafted/suspicious file.
Do you have an idea for a solution?
We are very grateful to @jreiser for patching the bucket in p_lx_elf.cpp in the issue 365. However, in fact, all places involving get_te32 () should be strengthened in upx, especially in p_lx_elf.cpp. The four positions we reported should be patched at least:
- p_lx_elf.cpp:5382
- p_lx_elf.cpp:5368
- p_lx_elf.cpp:5404
- p_lx_elf.cpp:5349
How can we reproduce the issue?
- compile upx with address-sanitize
- execute cmd
upx.out -df $PoC -o /dev/null
p_lx_elf.cpp:5382
Poc can be found here.
p_lx_elf.cpp:5368
Poc can be found here.
p_lx_elf.cpp:5404
Poc can be found here.
p_lx_elf.cpp:5349
Poc can be found here.
Please tell us details about your environment.
- UPX version used (upx --version):
upx 4.0.0-git-c6b9e3c62d15 (latest-devel-branch) UCL data compression library 1.03 zlib data compression library 1.2.8 LZMA SDK version 4.43
- Host Operating System and version:
Ubuntu 16.04 64-bit - Host CPU architecture:
Intel® Core™ i5-6200U CPU @ 2.30GHz with 8GB - Target Operating System and version:
same as Host - Target CPU architecture:
same as Host