>swap – tokyo westerns ctf 3rd 2017
Hi, I’m David and I’m a Security Research Engineer at Synack. I work primarily on automating vulnerability analysis of iOS applications, fuzzing, and reverse engineering IoT devices. I’ve spoken at a variety of security conferences around the country to present original research and I also like to create and play security games in my free time. This is a post originally published on my personal site…Enjoy!
The problem description is as follows:
The swapping is interesting. Let’s try!
nc pwn1.chal.ctf.westerns.tokyo 19937
This is a post-mortem writeup, i worked on the problem during the ctf but did not solve it in time. i read this solution and wrote my own exploit.
it looks like that we can actually attempt swap values at any given location in memory. how cool, this is like demolition in a binary form!
we’re given a 64bit binary, i checked the memory map and readelf to see which addresses were static & readable/writable we know ASLR is enabled.
we also need to know which addresses effect control of execution, we know the stack does but its mapped to a random location in memory. the relocations section effects execution, this includes both the global offset table (GOT) and the program linkage table (PLT). they’re both at static addresses without the program independent executable (PIE) protection. i noticed that memcpy and exit have static addresses in their GOT entries and the rest of the entries in the GOT seem like randomized addresses:
checking the binary protections in place again:
since the binary has only partial RELRO enabled the non-PLT GOT is read-only but the GOT is still writable. i went ahead and decompiled the main parts of the program:
i had to think through what would happen by replacing memcpy with read, there are three calls to read but the second one is where we’ve got the most control. assuming we enter “0” for the first input and some destination write address for the second input; the first call would result in a non-fatal error which there are no checks for since our destination buffer would be null, the second call would result in our intended write, and the third call would also result in a non-fatal error. this is all according to the read man page which specifies the errno values it returns:
so the plan is swap memcpy for read since they take the same type arguments and then we have an arbitrary write primitive. with that we can overwrite atoi with puts and leak memory off the stack, looking at read_int:
and the stack when we call atoi with the selection “1”:
notice the libc address that ends in 0x31 (‘1’) where $rax, $rsi and $rdi point to. you might be wondering why this would ever work as the string we’ve entered is not ending in a null byte. atoi actually only pays attention to characters between ‘0’ and ‘9’, anything can come afterwards and it will ignore it, so atoi only actually pays attention to the 0x31 and disregards the following characters, puts will leak the rest of these bytes to us though. 🙂
we must however consider that after overwriting atoi we potentially cripple our menu because atoi returned the menu option selected. we can however keep our write primitive in tact by abusing the return value of puts, puts returns the number of bytes printed so if we insert null bytes at the right place we can keep using the menu to finish our exploit!
from here we can calculate the offset of system in our libc and replace atoi with system, since they both take our string as input we can directly pass system “sh\x00” and get our shell!
full exploit code: