多數程式語言都有把任意資料轉成字串的機制,而且通常會定義某種格式化方式,讓開發者指定輸出的樣貌。其中有些機制相當強大且享有高度權限,在記憶體不安全的語言中尤其如此。
當攻擊者能提供一個字串值給應用程式,而該字串被直接當成格式字串使用時,就發生格式字串漏洞(format string vulnerability)。最知名、也大概是最危險的格式化器,是 C 語言的 printf 及其變體(例如輸出到字串的 sprintf)。printf 的第一個參數是格式字串,後面接著要格式化的值。
def process_authentication()
{
string username = read_string();
string password = read_string();
// Print username and password to terminal
printf(username); /* 網路來的字串直接當成格式字串 */
printf(password);
return check_user_password(username, password))
}printf 的格式字串以 %? 語法指定資料的位置與型別,其中問號會被替換成某個英數字元。格式指定子還可以帶上格式化資訊,例如數字要顯示幾位小數。
能直接控制格式字串的攻擊者,可以毀損記憶體,或是洩漏目前堆疊(stack)的資訊——而這些資訊往往正是後續攻擊所需要的。
下表列出攻擊者常濫用的 printf 格式指定子:
| 格式指定子 | 說明 | 潛在漏洞 |
|---|---|---|
%d、%p、%u、%x | 印出整數 | 若結果回傳給攻擊者,可用來洩漏堆疊上的資訊 |
%s | 印出零結尾字串 | 若結果回傳給攻擊者可洩漏堆疊資訊;也可能造成無效記憶體存取,導致阻斷服務 |
%n | 把目前已印出的字元數寫入參數所指定的指標 | 可造成選擇性的記憶體毀損或應用程式崩潰 |