二、格式化函數(shù)
譯者:飛龍
日期:2001.9.1
版本:v1.2
格式化函數(shù)是一類特殊的 ANSI C 函數(shù),接受可變數(shù)量的參數(shù),其中的一個(gè)就是所謂的格式化字符串。當(dāng)函數(shù)求解格式化字符串時(shí),它會訪問向函數(shù)提供的額外參數(shù)。它是一個(gè)轉(zhuǎn)換函數(shù),用于將原始的 C 數(shù)據(jù)類型表示為人類可讀的字符串形式。它們在幾乎任何 C 程序中都會使用,來輸出信息、打印錯(cuò)誤信息或處理字符串。
這一章中,我們會涵蓋格式化函數(shù)使用中的典型漏洞,正確用法,它們的一些參數(shù),以及格式化字符串漏洞的一般概念。
2.1 格式化字符串
如果攻擊者能夠向 ANSI C 格式化函數(shù)提供字符串,無論部分還是全部,就出現(xiàn)了格式化字符串漏洞。由此,格式化函數(shù)的行為會改變,并且攻擊者就可能控制目標(biāo)應(yīng)用。
在下面的例子中,字符串user由攻擊者提供 -- 他可以控制整個(gè) ASCIIZ 字符串,例如通過使用命令行參數(shù)。
錯(cuò)誤用法:
int func (char *user) {
printf (user);
}
正確用法:
int func (char *user) {
printf ("%s", user);
}
2.2 格式化函數(shù)系列
ANSI C 規(guī)范中定義了大量格式化函數(shù)。有一些基本的格式化函數(shù),復(fù)雜的函數(shù)基于它們,它們中的一些并不是標(biāo)準(zhǔn)的一部分,但是廣泛可用。
實(shí)際成員為:
fprintf-- 打印到FILE流printf-- 打印到stdout流sprintf-- 打印到字符串snprintf-- 打印到字符串,帶有長度檢查vfprintf-- 從va_arg結(jié)構(gòu)打印到FILE流vprintf-- 從va_arg結(jié)構(gòu)打印到stdout流vsprintf-- 從va_arg結(jié)構(gòu)打印到字符串vsnprintf-- 從va_arg結(jié)構(gòu)打印到字符串,帶有長度檢查
近親:
setproctitle-- 設(shè)置argv[]syslog-- 輸出到syslog設(shè)施其它類似
err*,verr*,warn*,vwarn*的函數(shù)
2.3 格式化函數(shù)的用法
為了理解這個(gè)漏洞在 C 語言代碼的哪里,我們必須檢驗(yàn)格式化函數(shù)的目的。
功能
用于將簡單的 C 數(shù)據(jù)類型轉(zhuǎn)換為字符串表示
允許指定表示的格式
處理產(chǎn)生的字符串(輸出到
stderr、stdout、syslog...)
格式化函數(shù)工作原理
格式化字符串控制了函數(shù)的行為
它指定了需要打印的參數(shù)類型
直接(傳值)或間接(傳址)保存二者
調(diào)用函數(shù)
需要知道它向棧中壓入了多少參數(shù),因?yàn)樗?dāng)格式化函數(shù)返回時(shí)需要清棧。
2.4 格式化字符串具體是什么?
格式化字符串是一個(gè) ASCIIZ 字符串,包含文本和格式化參數(shù)。
例如:
printf ("The magic number is: %d\n", 1911);
要打印的文本是The magic number is:,后面是格式化參數(shù)%d,它在輸出中會被參數(shù)1911代替。所以輸出是這個(gè)樣子:he magic number is: 1911。
一些格式化參數(shù):
| 參數(shù) | 輸出 | 傳遞方式 |
|---|---|---|
%d |
十進(jìn)制(int) |
傳值 |
%u |
無符號十進(jìn)制(unsigned int) |
傳值 |
%x |
十六進(jìn)制(unsigned int) |
傳值 |
%s |
字符串((const) char*) |
傳址 |
%n |
目前為止寫入的字節(jié)數(shù)(int *) |
傳址 |
\字符用于轉(zhuǎn)義特殊字符。它會被 C 編譯器在編譯使其替換,將轉(zhuǎn)義序列替換為二進(jìn)制中的適當(dāng)字符。格式化函數(shù)并不會識別這些特殊的序列。實(shí)際上,它們并不對格式化字符串做任何事情,但是有時(shí)會產(chǎn)生混淆,就像它們被編譯器求值一樣。
例如:
printf ("The magic number is: \x25d\n", 23);
上面的代碼可以工作,因?yàn)?code>\x25在編譯時(shí)期替換為%,雖然0x25(37)是百分號字符的 ASCII 值。
2.5 棧和它在格式化字符串中的作用
格式化函數(shù)的行為由格式化字符串控制。函數(shù)接受棧上的一些參數(shù),它們由格式化字符串請求。
printf ("Number %d has no address, number %d has: %08x\n", i, a, &a);
從printf來看,棧的樣子是:
棧頂
+--------+
| ... |
| &a |
| a |
| i |
| A |
| ... |
+--------+
棧底
其中:
| 符號 | 含義 |
|---|---|
| A | 格式化字符串的地址 |
| i | 變量i的值 |
| a | 變量a的值 |
| &a | 變量a的地址 |
格式化字符串現(xiàn)在解析了格式化字符串A,一次讀取一個(gè)字符。如果它不是%,字符會復(fù)制到輸出中。否則,%后面的字符規(guī)定了要求值的參數(shù)類型。字符串%%擁有特殊函數(shù),用于打印轉(zhuǎn)義字符%本身。其它每個(gè)參數(shù)都和數(shù)據(jù)相關(guān),位于棧上。