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.