Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] multi heap buffer overflows in get_le32() #380

Closed
14isnot40 opened this issue May 23, 2020 · 2 comments
Closed

[bug] multi heap buffer overflows in get_le32() #380

14isnot40 opened this issue May 23, 2020 · 2 comments

Comments

@14isnot40
Copy link

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(p);
[ 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(p);
    165     #else
    166         return acc_ua_get_le32(p);
    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:

  1. p_lx_elf.cpp:5382
  2. p_lx_elf.cpp:5368
  3. p_lx_elf.cpp:5404
  4. p_lx_elf.cpp:5349

How can we reproduce the issue?

  1. compile upx with address-sanitize
  2. 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(R) Core(TM) i5-6200U CPU @ 2.30GHz with 8GB
  • Target Operating System and version:
    same as Host
  • Target CPU architecture:
    same as Host
jreiser added a commit that referenced this issue May 23, 2020
@jreiser
Copy link
Collaborator

jreiser commented May 28, 2020

Fixed on devel branch by above commit.

@jreiser jreiser closed this as completed May 28, 2020
markus-oberhumer pushed a commit that referenced this issue Aug 17, 2022
@jreiser
Copy link
Collaborator

jreiser commented Apr 17, 2023

Verified as "reported problem is not present" in official release upx-4.0.2 of Jan.30, 2023.

$ for i in *; do $UPX402 -df -o /dev/null $i; done
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2023
UPX 4.0.2       Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 30th 2023

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
upx: hbo_PackLinuxElf32__elf_lookup5349: NotPackedException: not packed by UPX

Unpacked 0 files.
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2023
UPX 4.0.2       Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 30th 2023

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
upx: hbo_PackLinuxElf32__elf_lookup5368: NotPackedException: not packed by UPX

Unpacked 0 files.
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2023
UPX 4.0.2       Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 30th 2023

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
upx: hbo_PackLinuxElf32__elf_lookup5382: NotPackedException: not packed by UPX

Unpacked 0 files.
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2023
UPX 4.0.2       Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 30th 2023

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
upx: hbo_PackLinuxElf64__elf_lookup5404: CantUnpackException: bad DT_STRSZ 0

Unpacked 0 files.

Also, by independent verification none of the PoC files are packed by UPX, as evidenced by not containing the string "UPX", so attempted de-compression via upx -d is correctly rejected:

$ grep UPX hbo_PackLinuxElf32__elf_lookup5349 hbo_PackLinuxElf32__elf_lookup5368 hbo_PackLinuxElf32__elf_lookup5382 hbo_PackLinuxElf64__elf_lookup5404
   ##  empty output
$ echo $?
1   ## no matches found
$ 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants