天衣無縫 ~ 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
ld.so interpreter will parse the PHDR structure at memory address
AT_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);
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
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
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
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.