[轉載] 格式化字符串漏洞原理介紹
首先,對格式化字符串漏洞的原理進行簡單介紹。
格式化字符串函數介紹
格式化字符串函數可以接受可變數量的參數,並將第一個參數作為格式化字符串,根據其來解析之後的參數。通俗來說,格式化字符串函數就是將計算機內存中表示的數據轉化為我們人類可讀的字符串格式。幾乎所有的 C/C++ 程序都會利用格式化字符串函數來輸出信息,調試程序,或者處理字符串。一般來說,格式化字符串在利用的時候主要分為三個部分
- 格式化字符串函數
- 格式化字符串
- 後續參數,可選
這裏我們給出一個簡單的例子,其實相信大多數人都接觸過 printf 函數之類的。之後我們再一個一個進行介紹。
格式化字符串函數
常見的有格式化字符串函數有
- 輸入
- scanf
- 輸出
函數 | 基本介紹 |
---|---|
printf | 輸出到 stdout |
fprintf | 輸出到指定 FILE 流 |
vprintf | 根據參數列表格式化輸出到 stdout |
vfprintf | 根據參數列表格式化輸出到指定 FILE 流 |
sprintf | 輸出到字符串 |
snprintf | 輸出指定字節數到字符串 |
vsprintf | 根據參數列表格式化輸出到字符串 |
vsnprintf | 根據參數列表格式化輸出指定字節到字符串 |
setproctitle | 設置 argv |
syslog | 輸出日誌 |
err, verr, warn, vwarn 等 | 。。。 |
格式化字符串
這裏我們了解一下格式化字符串的格式,其基本格式如下
%[parameter][flags][field width][.precision][length]type
每一種 pattern 的含義請具體參考維基百科的格式化字符串 。以下幾個 pattern 中的對應選擇需要重點關註
- parameter
- n$,獲取格式化字符串中的指定參數
- flag
- field width
- 輸出的最小寬度
- precision
- 輸出的最大長度
- length,輸出的長度
- hh,輸出一個字節
- h,輸出一個雙字節
- type
- d/i,有符號整數
- u,無符號整數
- x/X,16 進制 unsigned int 。x 使用小寫字母;X 使用大寫字母。如果指定了精度,則輸出的數字不足時在左側補 0。默認精度為 1。精度為 0 且值為 0,則輸出為空。
- o,8 進制 unsigned int 。如果指定了精度,則輸出的數字不足時在左側補 0。默認精度為 1。精度為 0 且值為 0,則輸出為空。
- s,如果沒有用 l 標誌,輸出 null 結尾字符串直到精度規定的上限;如果沒有指定精度,則輸出所有字節。如果用了 l 標誌,則對應函數參數指向 wchar_t 型的數組,輸出時把每個寬字符轉化為多字節字符,相當於調用 wcrtomb 函數。
- c,如果沒有用 l 標誌,把 int 參數轉為 unsigned char 型輸出;如果用了 l 標誌,把 wint_t 參數轉為包含兩個元素的 wchart_t 數組,其中第一個元素包含要輸出的字符,第二個元素為 null 寬字符。
- p, void * 型,輸出對應變量的值。printf("%p",a) 用地址的格式打印變量 a 的值,printf("%p", &a) 打印變量 a 所在的地址。
- n,不輸出字符,但是把已經成功輸出的字符個數寫入對應的整型指針參數所指的變量。
- %, ‘
%
‘字面值,不接受任何 flags, width。
參數
就是相應的要輸出的變量。
格式化字符串漏洞原理
在一開始,我們就給出格式化字符串的基本介紹,這裏再說一些比較細致的內容。我們上面說,格式化字符串函數是根據格式化字符串函數來進行解析的。那麽相應的要被解析的參數的個數也自然是由這個格式化字符串所控制。比如說‘%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");此時我們可以發現我們並沒有提供參數,那麽程序會如何運行呢?程序照樣會運行,會將棧上存儲格式化字符串地址上面的三個變量分別解析為
- 解析其地址對應的字符串
- 解析其內容對應的整形值
- 解析其內容對應的浮點值
對於 2,3 來說倒還無妨,但是對於對於 1 來說,如果提供了一個不可訪問地址,比如 0,那麽程序就會因此而崩潰。
這基本就是格式化字符串漏洞的基本原理了。
[轉載] 格式化字符串漏洞原理介紹