Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2021-43312: [bug] heap buffer overflow in PackLinuxElf64::invert_pt_dynamic at p_lx_elf.cpp:5239 · Issue #379 · 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 PackLinuxElf64::invert_pt_dynamic at p_lx_elf.cpp:5239.

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 PackLinuxElf64::invert_pt_dynamic at p_lx_elf.cpp:5239.

ASAN reports:

==110294==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x63000000f760 at pc 0x000000466f38 bp 0x7ffcebb0b6a0 sp 0x7ffcebb0b690 READ of size 4 at 0x63000000f760 thread T0 #0 0x466f37 in PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16, LE32, LE64, LE64, LE64> > const*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:5239 #1 0x46f660 in PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16, LE32, LE64, LE64, LE64> > const*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:5127 #2 0x46f660 in PackLinuxElf64::PackLinuxElf64help1(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:795 #3 0x470479 in PackLinuxElf64Le::PackLinuxElf64Le(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.h:407 #4 0x470479 in PackLinuxElf64amd::PackLinuxElf64amd(InputFile*) /home/test/Desktop/EVAULATION/upx/src/p_lx_elf.cpp:1008 #5 0x4f34b2 in PackMaster::visitAllPackers(Packer* (*)(Packer*, void*), InputFile*, options_t const*, void*) /home/test/Desktop/EVAULATION/upx/src/packmast.cpp:194 #6 0x4f50f9 in PackMaster::getUnpacker(InputFile*) /home/test/Desktop/EVAULATION/upx/src/packmast.cpp:248 #7 0x4f521f in PackMaster::unpack(OutputFile*) /home/test/Desktop/EVAULATION/upx/src/packmast.cpp:266 #8 0x52a1e6 in do_one_file(char const*, char*) /home/test/Desktop/EVAULATION/upx/src/work.cpp:160 #9 0x52a69e in do_files(int, int, char**) /home/test/Desktop/EVAULATION/upx/src/work.cpp:271 #10 0x403ace in main /home/test/Desktop/EVAULATION/upx/src/main.cpp:1538 #11 0x7f8a7e1c882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #12 0x404828 in _start (/home/test/Desktop/EVAULATION/upx/src/upx.out+0x404828)

0x63000000f760 is located 0 bytes to the right of 62304-byte region [0x630000000400,0x63000000f760) allocated by thread T0 here: #0 0x7f8a7edbc602 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:5239 PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16, LE32, LE64, LE64, LE64> > const*) Shadow bytes around the buggy address: 0x0c607fff9e90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c607fff9ea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c607fff9eb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c607fff9ec0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c607fff9ed0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0c607fff9ee0: 00 00 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa 0x0c607fff9ef0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c607fff9f00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c607fff9f10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c607fff9f20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c607fff9f30: 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 ==110294==ABORTING

Then analysis the reasons for segv by debugging:

Program received signal SIGSEGV, Segmentation fault. 0x000000000053b1a8 in PackLinuxElf64::invert_pt_dynamic (this=this@entry=0xa00030, dynp=<optimized out>) at p_lx_elf.cpp:5239 5239 if (buckets[j]) { [ Legend: Modified register | Code | Heap | Stack | String ] ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ──── $rax : 0x0
$rbx : 0x0000000000a00030 → 0x0000000000726a30 → <vtable+0> add BYTE PTR [rax], al $rcx : 0x180
$rdx : 0xc00
$rsp : 0x00007fffffffcbf0 → 0x000000000065fa86 → <mem_size(unsigned+0> mov rax, QWORD PTR [rsp+0x10] $rbp : 0xffffffff
$rsi : 0x7aa9
$rdi : 0x0000000000a01548 → 0x480000104fe81024 $rip : 0x000000000053b1a8 → <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov eax, DWORD PTR [rdi+rsi*4+0x14] $r8 : 0x12
$r9 : 0xffe0
$r10 : 0xc10
$r11 : 0x7
$r12 : 0x8
$r13 : 0x180
$r14 : 0x0000000000a0f4d8 → 0x000000000000000d $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 ──── 0x00007fffffffcbf0│+0x0000: 0x000000000065fa86 → <mem_size(unsigned+0> mov rax, QWORD PTR [rsp+0x10] ← $rsp 0x00007fffffffcbf8│+0x0008: 0x0000000000400298 → add eax, DWORD PTR [rax] 0x00007fffffffcc00│+0x0010: 0x000000000000ffe0 0x00007fffffffcc08│+0x0018: 0x000000000000f360 0x00007fffffffcc10│+0x0020: 0x00007ffff7352260 → <read+16> cmp rax, 0xfffffffffffff001 0x00007fffffffcc18│+0x0028: 0x000000000000f360 0x00007fffffffcc20│+0x0030: 0x00007ffff7362447 → <lseek64+7> cmp rax, 0xfffffffffffff001 0x00007fffffffcc28│+0x0038: 0x0000000000539692 → <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov rax, QWORD PTR [rsp+0x10] ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ──── 0x53b197 <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov rcx, QWORD PTR [rsp+0x8] 0x53b19c <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov rdx, QWORD PTR [rsp] 0x53b1a0 <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> lea rsp, [rsp+0x98] → 0x53b1a8 <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov eax, DWORD PTR [rdi+rsi*4+0x14] 0x53b1ac <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> test eax, eax 0x53b1ae <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> je 0x53b235 <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16, LE32, LE64, LE64, LE64> > const*)+7109> 0x53b1b4 <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> lea rsp, [rsp-0x98] 0x53b1bc <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov QWORD PTR [rsp], rdx 0x53b1c0 <PackLinuxElf64::invert_pt_dynamic(N_Elf::Dyn<N_Elf::ElfITypes<LE16,+0> mov QWORD PTR [rsp+0x8], rcx ───────────────────────────────────────────────────────────────────────────────────────────────────────────────── source:p_lx_elf.cpp+5239 ──── 5234 unsigned const *const hasharr = &buckets[n_bucket]; (void)hasharr; 5235 //unsigned const *const gashend = &hasharr[n_bucket]; // minimum, except: 5236 // Rust and Android trim unused zeroes from high end of hasharr[] 5237 unsigned bmax = 0; 5238 for (unsigned j= 0; j < n_bucket; ++j) { → 5239 if (buckets[j]) { 5240 if (buckets[j] < symbias) { 5241 char msg[50]; snprintf(msg, sizeof(msg), 5242 "bad DT_GNU_HASH bucket[%d] < symbias{%#x}\n", 5243 buckets[j], symbias); 5244 throwCantPack(msg); ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "upx.out", stopped, reason: SIGSEGV ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x53b1a8 → PackLinuxElf64::invert_pt_dynamic(this=0xa00030, dynp=<optimized out>) [#1] 0x53d654 → PackLinuxElf64::invert_pt_dynamic(dynp=<optimized out>, this=0xa00030) [#2] 0x53d654 → PackLinuxElf64::PackLinuxElf64help1(this=0xa00030, f=0x7fffffffce10) [#3] 0x53f186 → PackLinuxElf64Le::PackLinuxElf64Le(f=0x7fffffffce10, this=0xa00030) [#4] 0x53f186 → PackLinuxElf64amd::PackLinuxElf64amd(this=0xa00030, f=0x7fffffffce10) [#5] 0x6048ac → PackMaster::visitAllPackers(func=0x602c30 <try_unpack(Packer*, void*)>, f=0x7fffffffce10, o=0x7fffffffcfc8, user=0x7fffffffce10) [#6] 0x6072ca → PackMaster::getUnpacker(f=<optimized out>) [#7] 0x6072ca → PackMaster::unpack(this=0x7fffffffcfb0, fo=0x7fffffffcee0) [#8] 0x670dc5 → do_one_file(iname=0x7fffffffdf12 "id:000036,sig:11,src:000541+000827,op:MOpt-splice,rep:2", oname=0x7fffffffd550 “/dev/null”) [#9] 0x67157c → do_files(i=0x4, argc=0x5, argv=0x7fffffffdac8)

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

mov eax, DWORD PTR [rdi+rsi*4+0x14]

The register rdi and rsi are:

$rsi : 0x7aa9
$rdi : 0x0000000000a01548

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

gef➤ x /10xg 0xa20000 0xa20000: 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.

upx_uint64_t const *const bitmask = (upx_uint64_t 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]) { if (buckets[j] < symbias) { char msg[50]; snprintf(msg, sizeof(msg), "bad DT_GNU_HASH bucket[%d] < symbias{%#x}\n", buckets[j], symbias); throwCantPack(msg); } if (bmax < buckets[j]) { bmax = 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