Tokyo Westerns CTF: load
Not a complete writeup, just my thoughts while working on it.
Load
At first glance, it looks like it’s nontrivial to locate main()
in a
stripped ELF.
Via this SO question.
__libc_start_main
has the following prototype:
int __libc_start_main(int (*main) (int, char**, char**),
int argc,
char *__unbounded *__unbounded ubp_av,
void (*init) (void),
void (*fini) (void),
void (*rtld_fini) (void),
void (*__unbounded stack_end));
So, we can look at the top address of the stack in order to get a pointer
to main, set a breakpoint on it, and continue
till we get there.
The entry point of the binary is 0x400720
(via info file
).
This is distinct from the address of main
— it corresponds to _start
in
tools like Binja. (can also be found with readelf -h ./load | grep Entry
)
x64 Linux calling convention is these registers for args RDI, RSI, RDX,
RCX, R8, R9, XMM0–7
.
So, the address of main
on my local machine on first run is
0x7ffff7ffe168
. Setting that as a breakpoint and continuing doesn’t work.
So, I pull the address of main out of Binja.
I then go reverse the file, annotating it.
Looks like a -1 for the size causes a crash, or at least, a silent exit w/o a successful file read.
The read appears to read into a stack-allocated variable that doesn’t look very big. I’m guessing that if I read a particular file (maybe something in /proc that I control, offset to wherever in the binary) then I’ll be able to overwrite the return address with that of libc or something.
__printf_chk
should still be usable, so I bet I can get arbitrary read.
But I can’t write things to the stack, so that’s a pain in the ass.
/dev/stdin
is a file I control 100%, solving that problem.
I’m not sure if ASLR is on or not, but I’m guessing no. I can probably
check by cat
ting a file.
I think the proper approach is ROP, by loading the stack with some addresses. But, I need addresses. I have things in the code section so I can search for ROP gadgets there.
I’m curious if a shellcode-level execve can do pipes. I think the answer is yes?
Sebastian pointed me to reopen
, and “try to open /dev/console or something as fd 1”.
Misc other stuff:
__printf_chk
isprintf
with stack checking to prevent%n
./proc/self/mem
gives us arbitrary memory, but we don’t actually see the output ever so that’s nothing.- .got.plt and .plt.got are two separate things. .plt.got has AX, .got.plt has WA.
readelf -S
for sections.
Post-competition, looks like one solution is reading a character of the flag at a time and terminating on match, hanging on mismatch. Thus you leak the entire flag. Interesting!