Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2021-43313: [bug]heap buffer overflow in PackLinuxElf32::invert_pt_dynamic at p_lx_elf.cpp:1688 · Issue #378 · upx/upx

A heap-based buffer overflow was discovered in upx, during the variable ‘bucket’ points to an inaccessible address. The issue is being triggered in the function PackLinuxElf32::invert_pt_dynamic at p_lx_elf.cpp:1688.

CVE
#android#ubuntu#linux#git#intel#buffer_overflow

What’s the problem (or question)?

A heap-based buffer overflow was discovered in upx, during the variable ‘bucket’ points to an inaccessible address. The issue is being triggered in the function PackLinuxElf32::invert_pt_dynamic at p_lx_elf.cpp:1688.

ASAN reports:

==110358==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61600000fed9 at pc 0x00000045acc8 bp 0x7ffd88c9b020 sp 0x7ffd88c9b010 READ of size 4 at 0x61600000fed9 thread T0 #0 0x45acc7 in PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16, LE32, LE32, LE32, LE32> > const*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:1688 #1 0x463ba5 in PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16, LE32, LE32, LE32, LE32> > const*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:1583 #2 0x463ba5 in PackLinuxElf32::PackLinuxElf32help1(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:305 #3 0x464e96 in PackLinuxElf32Le::PackLinuxElf32Le(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.h:395 #4 0x464e96 in PackLinuxElf32x86::PackLinuxElf32x86(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:4800 #5 0x464e96 in PackBSDElf32x86::PackBSDElf32x86(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:4817 #6 0x464e96 in PackFreeBSDElf32x86::PackFreeBSDElf32x86(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:4828 #7 0x4f337a in PackMaster::visitAllPackers(Packer* (*)(Packer*, void*), InputFile*, options_t const*, void*) /home/test/Desktop/EVAULATION/upx/src/packmast.cpp:190 #8 0x4f50f9 in PackMaster::getUnpacker(InputFile*) /home/test/Desktop/EVAULATION/upx/src/packmast.cpp:248 #9 0x4f521f in PackMaster::unpack(OutputFile*) /home/test/Desktop/EVAULATION/upx/src/packmast.cpp:266 #10 0x52a1e6 in do_one_file(char const*, char*) /home/test/Desktop/EVAULATION/upx/src/work.cpp:160 #11 0x52a69e in do_files(int, int, char**) /home/test/Desktop/EVAULATION/upx/src/work.cpp:271 #12 0x403ace in main /home/test/Desktop/EVAULATION/upx/src/main.cpp:1538 #13 0x7ff33c8dc82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #14 0x404828 in _start (/home/test/Desktop/EVAULATION/upx/src/upx.out+0x404828)

0x61600000fed9 is located 1 bytes to the right of 600-byte region [0x61600000fc80,0x61600000fed8) allocated by thread T0 here: #0 0x7ff33d4d0602 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/p_lx_elf.cpp:1688 PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16, LE32, LE32, LE32, LE32> > const*) Shadow bytes around the buggy address: 0x0c2c7fff9f80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c2c7fff9f90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c2c7fff9fa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c2c7fff9fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c2c7fff9fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0c2c7fff9fd0: 00 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa 0x0c2c7fff9fe0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c2c7fff9ff0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c2c7fffa000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c2c7fffa010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c2c7fffa020: 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 ==110358==ABORTING

Then analysis the reasons for segv by debugging:

Program received signal SIGSEGV, Segmentation fault. 0x0000000000527258 in PackLinuxElf32::invert_pt_dynamic (this=this@entry=0xa00030, dynp=<optimized out>) at p_lx_elf.cpp:1688 1688 if (buckets[j]) { [ Legend: Modified register | Code | Heap | Stack | String ] ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ──── $rax : 0x0000000000a009c1 → 0x00000000ffffffff $rbx : 0x0000000000a00030 → 0x00000000007267a0 → <vtable+0> add BYTE PTR [rax], al $rcx : 0x0
$rdx : 0x0
$rsp : 0x00007fffffffcc10 → 0x0000000000a007c0 → 0xff0000010900457f $rbp : 0xffffff00
$rsi : 0x7d88
$rdi : 0x6
$rip : 0x0000000000527258 → <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov r9d, DWORD PTR [rax+rsi*4+0x1c] $r8 : 0x0
$r9 : 0x0
$r10 : 0x10
$r11 : 0x0
$r12 : 0xd
$r13 : 0xffffffff
$r14 : 0x200
$r15 : 0x0
$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 ──── 0x00007fffffffcc10│+0x0000: 0x0000000000a007c0 → 0xff0000010900457f ← $rsp 0x00007fffffffcc18│+0x0008: 0x0000000000000000 0x00007fffffffcc20│+0x0010: 0x00007ffff7352260 → <read+16> cmp rax, 0xfffffffffffff001 0x00007fffffffcc28│+0x0018: 0x0000000000000258 0x00007fffffffcc30│+0x0020: 0x00007ffff7362447 → <lseek64+7> cmp rax, 0xfffffffffffff001 0x00007fffffffcc38│+0x0028: 0x00000000005255b2 → <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov rax, QWORD PTR [rsp+0x10] 0x00007fffffffcc40│+0x0030: 0x0000000000000000 0x00007fffffffcc48│+0x0038: 0x0000000000000000 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ──── 0x527247 <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov rcx, QWORD PTR [rsp+0x8] 0x52724c <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov rdx, QWORD PTR [rsp] 0x527250 <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> lea rsp, [rsp+0x98] → 0x527258 <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov r9d, DWORD PTR [rax+rsi*4+0x1c] 0x52725d <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> test r9d, r9d 0x527260 <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> je 0x5272eb <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16, LE32, LE32, LE32, LE32> > const*)+7515> 0x527266 <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> xchg ax, ax 0x527268 <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> lea rsp, [rsp-0x98] 0x527270 <PackLinuxElf32::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov QWORD PTR [rsp], rdx ───────────────────────────────────────────────────────────────────────────────────────────────────────────────── source:p_lx_elf.cpp+1688 ──── 1683 unsigned const *const hasharr = &buckets[n_bucket]; (void)hasharr; 1684 //unsigned const *const gashend = &hasharr[n_bucket]; // minimum, except: 1685 // Rust and Android trim unused zeroes from high end of hasharr[] 1686 unsigned bmax = 0; 1687 for (unsigned j= 0; j < n_bucket; ++j) { → 1688 if (buckets[j]) { 1689 if (buckets[j] < symbias) { 1690 char msg[50]; snprintf(msg, sizeof(msg), 1691 "bad DT_GNU_HASH bucket[%d] < symbias{%#x}\n", 1692 buckets[j], symbias); 1693 throwCantPack(msg); ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "upx.out", stopped, reason: SIGSEGV ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x527258 → PackLinuxElf32::invert_pt_dynamic(this=0xa00030, dynp=<optimized out>) [#1] 0x529294 → PackLinuxElf32::invert_pt_dynamic(dynp=<optimized out>, this=0xa00030) [#2] 0x529294 → PackLinuxElf32::PackLinuxElf32help1(this=0xa00030, f=0x7fffffffce20) [#3] 0x52c65e → PackLinuxElf32Le::PackLinuxElf32Le(f=0x7fffffffce20, this=0xa00030) [#4] 0x52c65e → PackLinuxElf32x86::PackLinuxElf32x86(f=0x7fffffffce20, this=0xa00030) [#5] 0x52c65e → PackBSDElf32x86::PackBSDElf32x86(f=0x7fffffffce20, this=0xa00030) [#6] 0x52c65e → PackFreeBSDElf32x86::PackFreeBSDElf32x86(this=0xa00030, f=0x7fffffffce20) [#7] 0x60448c → PackMaster::visitAllPackers(func=0x602c30 <try_unpack(Packer*, void*)>, f=0x7fffffffce20, o=0x7fffffffcfd8, user=0x7fffffffce20) [#8] 0x6072ca → PackMaster::getUnpacker(f=<optimized out>) [#9] 0x6072ca → PackMaster::unpack(this=0x7fffffffcfc0, fo=0x7fffffffcef0)

The instruction to crash is, corresponds to the bucket [j] in the source code

mov r9d, DWORD PTR [rax+rsi*4+0x1c]

The value of register rax and rsi are:

$rax : 0x0000000000a009c1 $rsi : 0x7d88

The DWORD PTR pointer to 0xa1fffd, where the 0xa20000 is a invalid address.

gef➤ x /10xg 0xa1fffd 0xa1fffd: Cannot access memory at address 0xa20000

What should have happened?

Decompress a crafted/suspicious file.

Do you have an idea for a solution?

A boundary check is needed for loop variable ‘j’ because the size allocated for variable ‘buckets’ is limited.

unsigned const *const bitmask = (unsigned const *)(void const *)&gashtab[4]; unsigned const *const buckets = (unsigned const *)&bitmask[n_bitmask]; unsigned const *const hasharr = &buckets[n_bucket]; (void)hasharr; //unsigned const *const gashend = &hasharr[n_bucket]; // minimum, except: // Rust and Android trim unused zeroes from high end of hasharr[] unsigned bmax = 0; for (unsigned j= 0; j < n_bucket; ++j) { if (buckets[j]) {

How can we reproduce the issue?

  1. compile upx with address-sanitize
  2. execute cmd

upx.out -df $PoC -o /dev/null

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

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907