ANT-2026-5WVCAVEA · upx
heap-buffer-overflow medium
Severity Claude medium · Security research firm medium · Maintainer -
Discovered by Claude Mythos Preview
Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Trail of Bits.
ANT-2026-5WVCAVEA: Heap buffer overflow in un_DT_INIT relocation restore due to attacker-controlled offset near buffer end
An attacker-controlled offset near the end of a heap buffer leads to an out-of-bounds access during DT_INIT relocation restore.
Target
Project: upx
Location: un_DT_INIT
Discovery: static analysis — not yet dynamically reproduced
Technical Details
ASAN: "heap-buffer-overflow ... WRITE of size 8 at 0x7df4f56f1a8f ... located 3 bytes after 22668-byte region". The root cause is a missing bounds check after elf_find_dynamic(): the returned pointer is validated only as t < file_size_u, guaranteeing the start is in-bounds but not that rp + sizeof(Elf64_Rela) is. The assignments rp->r_info = ... (offset +8) and rp->r_addend = ... (offset +16) therefore write past the end of the file_image MemBuffer. A parallel missing check on dynsym[0] allows an out-of-bounds read of the source values.
Reproduction
This finding was identified by static analysis and has not yet been dynamically reproduced. The Technical Details section above describes the code path; a trigger input is not included.
[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]
Acknowledgement
This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-5WVCAVEA.
Reference: ANT-2026-5WVCAVEA
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure
Triage and disclosure were performed by Trail of Bits. The writeup below is the document the firm sent to the maintainer.
- Verdict
- true positive
- Severity
- medium
This issue tracker is ONLY used for reporting bugs. Please use stackoverflow for supporting issues.
What's the problem (or question)?
From Anthropic "Trail of Bits" report:
Vulnerability Header
- Vulnerability Title & ID: Heap Buffer Overflow in
un_DT_INITRelocation Restore — UPX (Bug 083) - Security-Relevant: Yes
- Severity Rating: Medium
- Bug Category: Heap Buffer Overflow (Out-of-Bounds Write)
- Source Commit: https://github.com/upx/upx @ 36babe770e83d2104f752c0c10d61eb945321f65 (oss-fuzz builder image, 2026-03-05)
- Status: Confirmed (not fixed upstream as of Source Commit)
Executive Summary
A heap buffer overflow (WRITE of 8 bytes, 3 bytes past end of buffer) exists in UPX's PackLinuxElf64::un_DT_INIT function when processing a crafted UPX-packed ELF binary. The vulnerability is triggered during the --test or --decompress operations when restoring DT_INIT_ARRAY relocations. A pointer derived from attacker-controlled dynamic section data can point near the end of the file_image buffer, causing writes to Elf64_Rela fields to overflow the heap allocation.
Root Cause Analysis
Technical Description
When UPX tests or decompresses a packed ELF64 binary, un_DT_INIT() processes the dynamic section to restore original DT_INIT_ARRAY entries. At p_lx_elf.cpp:7659, the code retrieves a relocation entry pointer:
Elf64_Rela *rp = (Elf64_Rela *)elf_find_dynamic(dyn_null->d_val);
The elf_find_dynamic() function (line 8373) converts an RVA to a file offset and returns a pointer into file_image:
upx_uint64_t const t = elf_get_offset_from_address(get_te64(&dynp->d_val));
if (t && t < file_size_u) {
return t + file_image;
}
The check t < file_size_u ensures the start of the returned pointer is within bounds, but does not ensure that the full Elf64_Rela structure (24 bytes: r_offset + r_info + r_addend) fits within the buffer. When rp points close to the end of file_image, the writes at lines 7664-7665 overflow:
rp->r_info = rp_unc->r_info; // write at rp+8, can overflow
rp->r_addend = rp_unc->r_addend; // write at rp+16, can overflow further
Additionally, rp_unc is cast from &dynsym[0] without verifying that dynsym points to at least one full entry (24 bytes) within file_image before reading r_info and r_addend.
The file_image buffer is allocated at line 1144 with size file_size (22668 bytes for this PoC). The ASAN report confirms the write occurs 3 bytes past the end of this buffer.
First Faulty Condition
Missing bounds check at p_lx_elf.cpp:7661 — after obtaining rp from elf_find_dynamic() and confirming it is non-null, the code proceeds to write sizeof(Elf64_Rela) bytes through rp without verifying the full structure fits within file_image.
Trace Analysis
==7==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7df4f56f1a8f
WRITE of size 8 at 0x7df4f56f1a8f thread T0
#0 PackLinuxElf64::un_DT_INIT(...) p_lx_elf.cpp:7664 ← faulting write (rp->r_info)
#1 PackLinuxElf64::unpack(OutputFile*) p_lx_elf.cpp:8063 ← calls un_DT_INIT
#2 Packer::doTest() packer.cpp:107 ← test operation
#3 do_one_file(...) work.cpp:340 ← file processing
#4 do_files(...) work.cpp:427 ← main loop
#5 upx_main(...) main.cpp:1323 ← entry point
#6 LLVMFuzzerTestOneInput test_packed_file_fuzzer.cpp:42
0x7df4f56f1a8f is located 3 bytes after 22668-byte region [0x7df4f56ec200,0x7df4f56f1a8c)
allocated by: MemBuffer::alloc() → PackLinuxElf64::PackLinuxElf64help1(InputFile*)
The crash address is 3 bytes past the 22668-byte file_image buffer. The rp pointer was computed to an offset near the end of the file, and the 8-byte write to rp->r_info (at offset 8 within the Elf64_Rela struct) extends past the allocation boundary.
Sibling Verification
| Sibling Entrypoint | Unpatched Crash | Same Root Cause? | Patched Result | Notes |
|---|---|---|---|---|
| decompress_packed_file_fuzzer | un_shlib_1 (p_lx_elf.cpp:7146) |
No | Still crashes (parent patch only) | Different bug: heap overflow in readx due to unchecked len. This is covered as Finding 1 (with its own patch). |
Proposed Fix
Add bounds checks before writing through rp and reading through dynsym[0] (see patch.diff):
if ((char *)rp + sizeof(Elf64_Rela) > (char *)&file_image[0] + file_size_u)
throwCantUnpack("bad DT_INIT_ARRAY relocation offset");
if ((char *)&dynsym[1] > (char *)&file_image[0] + file_size_u)
throwCantUnpack("bad dynsym for DT_INIT_ARRAY");
This ensures both the relocation entry and the source dynsym entry are fully within the allocated buffer before any writes occur. Validated: the PoC is rejected with CantUnpackException (no ASAN crash) after applying the patch.
Exploitability Assessment
Attack Vector & Reachability
This bug is triggered when UPX processes a crafted UPX-packed ELF64 binary via upx -t <file> (test) or upx -d <file> -o <out> (decompress). The oss-fuzz harness writes the input to a temporary file and invokes upx_main with -t, so fuzz-harness reachability matches real-world reachability for this issue.
The attacker needs the victim to run UPX on the attacker-supplied packed binary (e.g., during manual analysis or automated processing).
Reachability verdict (fuzz harness): CONFIRMED
Reachability verdict (real world): CONFIRMED — requires the user to run UPX on an attacker-supplied packed binary.
Prerequisite checks before the vulnerable code is reached:
- File is accepted as an ELF64 input and routed to PackLinuxElf64.
- PT_DYNAMIC must satisfy p_offset + p_filesz <= file_size (or UPX rejects the file).
- The dynamic table must take the "no DT_INIT; DT_INIT_ARRAY/DT_PREINIT_ARRAY" restoration path in un_DT_INIT.
The Attack Sequence
- An attacker crafts a UPX-packed ELF64 binary with dynamic section values such that
elf_find_dynamic(dyn_null->d_val)returns a relocation pointer near the end offile_image. - A victim runs
upx -torupx -don the file. - UPX enters
PackLinuxElf64::un_DT_INIT, obtainsrpviaelf_find_dynamic(), and attempts to restorerp->r_info/rp->r_addend, resulting in out-of-bounds heap writes. - This leads to a crash; depending on heap layout and exploitation technique, it may be usable as a memory corruption primitive.
Technical Primitive
Grade 0.50 — WRITE crash with memory corruption. The attacker controls:
- Write location: partially controlled via crafted ELF dynamic section values (the dyn_null->d_val field determines where rp points)
- Write content: the written data comes from dynsym[0] which is also within the attacker-controlled file image
- Write size: 8 bytes (fixed, from r_info field assignment)
The 3-byte overflow past the heap buffer could corrupt heap metadata or adjacent allocations. While the overflow is small, writing 8 bytes of attacker-controlled data at a partially-controlled heap offset provides a potential primitive for heap exploitation.
Mitigation Analysis
- ASLR: Mitigates exploitation since heap layout is randomized, but the small overflow primarily affects adjacent heap chunks.
- DEP/NX: Does not protect against heap metadata corruption.
- Stack canaries: Not applicable (heap overflow).
- Heap hardening: Modern heap allocators with guard pages or quarantine zones may detect or prevent exploitation of this small overflow.
Given the small overflow (3 bytes), converting this to reliable code execution would be challenging but not impossible with heap grooming.
Reproduction Steps
```bash
Clone oss-fuzz and build UPX fuzzers with ASAN
cd /path/to/oss-fuzz python3 infra/helper.py build_fuzzers --sanitizer address upx
Run the primary PoC
build/out/upx/test_packed_file_fuzzer
Expected: ASAN reports heap-buffer-overflow WRITE in un_DT_INIT at p_lx_elf.cpp:7664
Please tell us details about your environment.
- UPX version used (
upx --version): 5.1.1 commit 36babe770e83d2104f752c0c10d61eb945321f65 - Host Operating System and version: Linux 6.x
- Host CPU architecture: x86_64
- Target Operating System and version: same
- Target CPU architecture:
[poc.bin.gz]((https://github.com/user-attachments/files/26144179/poc.bin.gz)
Dates from discovery through public reveal.
- 2026-03-20 Sent to maintainer
- 2026-03-29 Reported to tracker
- 2026-05-09 Maintainer acknowledged
- 2026-06-08 Patch released
- 2026-08-17 Publicly revealed
SHA-3-512 hash:
0734711e5afe8fa99b46eae24834a17a136a72dd2c6d5ca662c6872de6ae54b2a686471646eb4d5a746ecb184755a0bfca4172b1e9bd8af0551387a9fe9738e8
Committed 2026-05-07 00:01 PT
Revealed 2026-08-17 10:47 PT
Verify (download preimage.json)
Show preimage JSON
{
"ant_id": "ANT-2026-5WVCAVEA",
"bug_class": "Heap Buffer Overflow",
"claude_severity": "medium",
"commit_sha": null,
"created_at": "2026-03-29T20:43:28+00:00",
"description": "An attacker-controlled offset near the end of a heap buffer leads to an out-of-bounds access during DT_INIT relocation restore.",
"discovered_at": null,
"location": "un_DT_INIT",
"poc_sha256": null,
"preimage_version": 1,
"project": "UPX",
"reproduction": null,
"technical_details": "ASAN: \"heap-buffer-overflow ... WRITE of size 8 at 0x7df4f56f1a8f ... located 3 bytes after 22668-byte region\". The root cause is a missing bounds check after elf_find_dynamic(): the returned pointer is validated only as `t < file_size_u`, guaranteeing the start is in-bounds but not that `rp + sizeof(Elf64_Rela)` is. The assignments `rp->r_info = ...` (offset +8) and `rp->r_addend = ...` (offset +16) therefore write past the end of the file_image MemBuffer. A parallel missing check on dynsym[0] allows an out-of-bounds read of the source values.",
"title": "Heap buffer overflow in un_DT_INIT relocation restore due to attacker-controlled offset near buffer end",
"vendor_severity": "medium"
}