Skip to content

Instantly share code, notes, and snippets.

@pzread
Last active October 22, 2022 13:13
Show Gist options
  • Save pzread/2ae0bb3aa5fe0dc69fcf3257c41db944 to your computer and use it in GitHub Desktop.
Save pzread/2ae0bb3aa5fe0dc69fcf3257c41db944 to your computer and use it in GitHub Desktop.

天衣無縫 ~ Fantastic Seamless Textile ~

When executing a ELF binary, Linux kernel will pass the memory address of PHDR(program header) to userspace by AT_PHDR entry of AUXV.

ld.so interpreter will parse the PHDR structure at memory addressAT_PHDR and resolve more ELF structures, such as dynamic section.

But Linux kernel wrongly calculate the PHDR address in memory.

NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);

(http://elixir.free-electrons.com/linux/v4.13.11/source/fs/binfmt_elf.c#L245)

load_addr is the memory base address of the first PT_LOAD segment in PHDR(typically, it will be 0x400000 for no-pie x64 ELF). But according to the spec of PHDR, e_phoff is the file offset of PHDR. Kernel should resolve its memory address by searching the memory region of PT_LOADs, not just add on a base address.

Here is the memory layout of this challenge binary.

00400000-00402000 r-xp 00000000 fd:01 3290575
00601000-00602000 r--p 00001000 fd:01 3290575
00602000-0080a000 rw-p 00002000 fd:01 3290575

The e_phoff of this binary is modified to 0x204270. First, as spec, Kernel will read the PHDR at the file offset e_phoff in binary. I put the original PHDR at 0x204270 in binary. So Kernel will use the original PHDR to load file into memory. Accroding to the original PT_LOADs, this PHDR will be loaded at 0x00624270 in memory(PT_LOAD Offset:0x00001e00 -> VirtAddr:0x000601e00)

But then Kernel will return the PHDR memory address 0x400000 + 0x204270 = 0x604270 to ld.so, which doesn't point to the original PHDR(at 0x00624270). So I put a modified PHDR at 0x4270 in binary, which will be loaded at 0x604270. This modified PHDR contains a modified dynamic section, which points to a modified symbol table. In this symbol table, the string table offset of entry memcmp points to the string strcmp.

As a result, ld.so will parse the modified PHDR at 0x604270 and get the modified symbol table. When dl_fixup tries to resolve the entry memcmp, it will use the modified symbol table from ld.so and return strcmp.

But most of disassembly tools (AFAIK, all disassembly tools) follow the spec correctly and parse the original PHDR. So they will show memcmp on SHA256 comparing.

Once know the memcmp is actually strcmp, you can get a SHA starting with 0x00 by registering users(with probability of 1/256) and bypass the login check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment