多數程式語言都有把任意資料轉成字串的機制,而且通常會定義某種格式化方式,讓開發者指定輸出的樣貌。其中有些機制相當強大且享有高度權限,在記憶體不安全的語言中尤其如此。

當攻擊者能提供一個字串值給應用程式,而該字串被直接當成格式字串使用時,就發生格式字串漏洞(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把目前已印出的字元數寫入參數所指定的指標可造成選擇性的記憶體毀損或應用程式崩潰