高级ROP之SROP利用

admin 2024年1月21日17:06:38评论10 views字数 2667阅读8分53秒阅读模式

写在开头

这篇文章是我在先知社区投的稿,给各位师弟师妹们起个头,抛砖引玉。

先知原文链接:https://xz.aliyun.com/t/13198

高级ROP之SROP利用

高级ROP之SROP利用

这次第六届安洵杯有两道和srop相关的题目,正好对所学的SROP知识做个总结

高级ROP之SROP利用

基础知识

SROP,全称为Sigreturn Oriented Programming,主要触发原理为sigreturn这个系统调用,这个系统调用一般是程序在发生 signal 的时候被间接地调用

signal机制

来源于ctfwiki:

https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/advanced-rop/srop

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

高级ROP之SROP利用

1.内核向某个进程发送 signal 机制,该进程会被暂时挂起,进入内核态。

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

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

高级ROP之SROP利用

漏洞利用

看完原理之后我们可以发现,sigreturn 系统调用会将进程恢复为之前"保存的",也就是从栈中pop回到各寄存器,在执行sigreturn 系统调用期间,我们对栈中的值是可以任意读写的,而且由于内核与信号处理程序无关, signal 对应的各个寄存器值并不会被记录,那么,只要我们能够劫持栈中的数据,伪造一个 Signal Frame ,那么就可以控制任意寄存器的值

shell获取

shell获取

当我们可以控制任意寄存器的值时,只要程序中的数据满足条件我们便可以利用SROP来获取shell

通过对ROP攻击的学习我们知道,获取shell实际上就是执行了系统调用 execve("/bin/sh",0,0),那么有没有可能通过控制寄存器来执行这个系统调用?答案是肯定的。

获取shell需要满足以下条件:

  1. 栈溢出以控制栈的内容

  2. 能控制rax寄存器为sigreturn 系统调用函数的调用号

  3. 有syscall系统调用函数或者汇编代码

  4. 栈空间足够大

具体SROP操作如下

  1. 通过栈溢出劫持返回地址,构造SROP

  2. 控制rax寄存器为sigreturn 的系统调用号

  3. 执行syscall进入sigreturn 系统调用

  4. 控制栈布局,使sigreturn 系统调用结束后的pop指令能够准确控制各个寄存器成我们想要的值

    例如获取shell的各寄存器控制:

    rax —>59(execve的系统调用号)

    rdi —> '/bin/sh'

    rsi  —> 0

    rdx —>0

    rip —> syscall

    此时再继续向下调用时就可以执行execve("/bin/sh",0,0)了

SROP链调用

当然,SROP并不是只能够调用一次,只要栈布局合理并且知道一些关键数据,我们便可以执行一串SROP链

例如,当程序开启了沙箱保护时

我们需要利用open,read,write三次调用

便可进行如下构造

高级ROP之SROP利用

这样通过对rsp的设置可以保证srop链依次进行调用

高级ROP之SROP利用

工具使用

pwntools集成了有关SROP链的构造函数 SigreturnFrame()

工具构造和利用如下:

frame = SigreturnFrame()

frame.rax = 

frame.rdi = 

frame.rsi = 

frame.rdx = 

frame.rcx = 

frame.rip = 

frame.rsp = 

用与参与栈布局的构造

在payload构造中利用bytes(frame)包裹即可

高级ROP之SROP利用

一些容易踩的坑

在程序中有两种syscall:

一种是syscall函数,在ida中以以下类似汇编代码出现:

高级ROP之SROP利用

也能在ida中找到其plt表

高级ROP之SROP利用

另一种是以syscall的机器码形式出现的:

高级ROP之SROP利用

一般以sys_read等伪c代码形式出现

高级ROP之SROP利用

一般以sys_read等伪c代码形式出现

其调用过程如下:

由于syscall函数调用需要遵循寄存器传参条件,所以在syscall函数调用中会将寄存器重新赋值,那么在工具函数 SigreturnFrame() 构造时便需要根据传参规则改变调用规则

高级ROP之SROP利用
高级ROP之SROP利用

例题1.2023江西省赛初赛pwn2

ida

高级ROP之SROP利用

非常直白的一道题

给了对应的gadget

高级ROP之SROP利用

直接的SROP构造即可

exp

高级ROP之SROP利用

exp分析

高级ROP之SROP利用

因为程序中没有sh字符串,那么我们就需要构造一个read来写一段/bin/sh字符串到程序中,并在后面继续执行下一段srop链

然后rsp位置设置成了一个bss段的地址,实际上是产生了一个类似于栈迁移的效果,使rsp到对应位置继续向下执行代码

高级ROP之SROP利用
高级ROP之SROP利用

那么实际上data+8恰好就是输入后p64(poprax)的位置

高级ROP之SROP利用

接着执行下去即可getshell

高级ROP之SROP利用
高级ROP之SROP利用

例题2.第六届安洵杯网络安全挑战赛 pwn2

ida

高级ROP之SROP利用

还开了沙箱

高级ROP之SROP利用

orw都有

而且两种syscall都有

高级ROP之SROP利用

分析代码

高级ROP之SROP利用

可以看到有两段输入,一段在bss段上,大小为0x1000,另一段在栈上,溢出了0x10字节

输入长度不够如何构造srop?

此时我们便可以用这个0x10字节溢出来实现栈迁移,将栈迁移到bss段上后第一段读入就可以实现一个非常大的数据读入,足够进行三段SROP链的构造了

程序中也给了rax的赋值

高级ROP之SROP利用

exp解析

高级ROP之SROP利用

首先是一个经典0x10字节的栈迁移过程,不了解的师傅可以看看这篇文章:https://xz.aliyun.com/t/12189

这里不多赘述

接下来就是SROP链的构造:

高级ROP之SROP利用

三段式的构造,open,read,write,将flag字符串和垃圾数据放在一起构造栈溢出

然后就是rax—>15,syscall,frame

重点是rsp位置需要通过调试来确定运行地址

高级ROP之SROP利用
高级ROP之SROP利用

使rsp恰好落在下一段SROP链的起始点,一段接着一段执行

高级ROP之SROP利用
高级ROP之SROP利用
高级ROP之SROP利用
高级ROP之SROP利用

完整exp:

高级ROP之SROP利用

原文始发于微信公众号(火炬木攻防实验室):高级ROP之SROP利用

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月21日17:06:38
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   高级ROP之SROP利用http://cn-sec.com/archives/2414307.html

发表评论

匿名网友 填写信息