二伊履、格式化函數(shù)
譯者:飛龍
日期:2001.9.1
版本:v1.2
格式化函數(shù)是一類特殊的 ANSI C 函數(shù)蓖宦,接受可變數(shù)量的參數(shù)宵呛,其中的一個就是所謂的格式化字符串篙悯。當(dāng)函數(shù)求解格式化字符串時蚁阳,它會訪問向函數(shù)提供的額外參數(shù)。它是一個轉(zhuǎn)換函數(shù)鸽照,用于將原始的 C 數(shù)據(jù)類型表示為人類可讀的字符串形式螺捐。它們在幾乎任何 C 程序中都會使用,來輸出信息、打印錯誤信息或處理字符串定血。
這一章中赔癌,我們會涵蓋格式化函數(shù)使用中的典型漏洞,正確用法澜沟,它們的一些參數(shù)届榄,以及格式化字符串漏洞的一般概念。
2.1 格式化字符串
如果攻擊者能夠向 ANSI C 格式化函數(shù)提供字符串倔喂,無論部分還是全部,就出現(xiàn)了格式化字符串漏洞靖苇。由此席噩,格式化函數(shù)的行為會改變,并且攻擊者就可能控制目標(biāo)應(yīng)用贤壁。
在下面的例子中悼枢,字符串user
由攻擊者提供 -- 他可以控制整個 ASCIIZ 字符串,例如通過使用命令行參數(shù)脾拆。
錯誤用法:
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)的一部分绰上,但是廣泛可用。
實際成員為:
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ù)的用法
為了理解這個漏洞在 C 語言代碼的哪里,我們必須檢驗格式化函數(shù)的目的迷扇。
功能
用于將簡單的 C 數(shù)據(jù)類型轉(zhuǎn)換為字符串表示
允許指定表示的格式
處理產(chǎn)生的字符串(輸出到
stderr
百揭、stdout
、syslog
...)
格式化函數(shù)工作原理
格式化字符串控制了函數(shù)的行為
它指定了需要打印的參數(shù)類型
直接(傳值)或間接(傳址)保存二者
調(diào)用函數(shù)
需要知道它向棧中壓入了多少參數(shù)蜓席,因為它當(dāng)格式化函數(shù)返回時需要清棧器一。
2.4 格式化字符串具體是什么?
格式化字符串是一個 ASCIIZ 字符串厨内,包含文本和格式化參數(shù)祈秕。
例如:
printf ("The magic number is: %d\n", 1911);
要打印的文本是The magic number is:
,后面是格式化參數(shù)%d
隘庄,它在輸出中會被參數(shù)1911
代替踢步。所以輸出是這個樣子:he magic number is: 1911
。
一些格式化參數(shù):
參數(shù) | 輸出 | 傳遞方式 |
---|---|---|
%d |
十進制(int ) |
傳值 |
%u |
無符號十進制(unsigned int ) |
傳值 |
%x |
十六進制(unsigned int ) |
傳值 |
%s |
字符串((const) char* ) |
傳址 |
%n |
目前為止寫入的字節(jié)數(shù)(int * ) |
傳址 |
\
字符用于轉(zhuǎn)義特殊字符丑掺。它會被 C 編譯器在編譯使其替換获印,將轉(zhuǎn)義序列替換為二進制中的適當(dāng)字符。格式化函數(shù)并不會識別這些特殊的序列。實際上兼丰,它們并不對格式化字符串做任何事情玻孟,但是有時會產(chǎn)生混淆,就像它們被編譯器求值一樣鳍征。
例如:
printf ("The magic number is: \x25d\n", 23);
上面的代碼可以工作黍翎,因為\x25
在編譯時期替換為%
,雖然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
,一次讀取一個字符戴差。如果它不是%
送爸,字符會復(fù)制到輸出中。否則暖释,%
后面的字符規(guī)定了要求值的參數(shù)類型袭厂。字符串%%
擁有特殊函數(shù),用于打印轉(zhuǎn)義字符%
本身球匕。其它每個參數(shù)都和數(shù)據(jù)相關(guān)纹磺,位于棧上。