首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C語言vfprintf段錯誤根因分析:%格式字符串注入與可變參數陷阱

     編輯:關於C

故障現場

Program received signal SIGSEGV, Segmentation fault.
0x0098735e in vfprintf () from /lib/libc.so.6
#1  0x0098e3cf in fprintf () from /lib/libc.so.6

將fprintf(fp, ...)改為printf(...)即正常,定位為fprintf/vfprintf內部崩潰。

根因:用戶數據中含%導致格式字符串解析異常

char *user_input = get_url_param(); // 可能包含 "%20" (空格URL編碼)
fprintf(log_fp, user_input);        // ❌ 災難:user_input被當作格式字符串
// 若user_input = "Error %20s" → 視為 "%20s" 期望字符串參數 → 讀取棧垃圾 → 崩潰

漏洞原理

  1. vfprintf解析格式字符串,遇到%期望後續格式修飾符
  2. %20s → 寬度20的字符串格式,需對應char*參數
  3. 實際調用無額外參數 → 讀取棧上隨機值作為指針 → 解引用崩潰
  4. 同理%n可任意寫內存,構成安全漏洞

修復方案

✅ 方案一:固定格式字符串 + %s
fprintf(log_fp, "%s", user_input); // user_input作為參數,不被解析
✅ 方案二:fputs 純字符串輸出
fputs(user_input, log_fp); // 無格式解析,最高效安全
⚠ 方案三:預處理轉義 % → %%
char *escape_percent(const char *src) {
    // 分配2倍空間,將%替換為%%
}
fprintf(log_fp, "%s", escape_percent(user_input));

同類風險函數族

函數風險安全替代
printf(fmt)高printf("%s", fmt)
sprintf(buf, fmt)高(緩衝區溢出+格式注入)snprintf(buf, n, "%s", fmt)
syslog(pri, fmt)高syslog(pri, "%s", fmt)
setproctitle(fmt)高避免用戶輸入作格式

編譯器防護

  • -Wformat-security (GCC/Clang默認開啟):檢測非字面量格式字符串
  • -Wformat=2:更嚴格檢查
  • -D_FORTIFY_SOURCE=2:運行時緩衝區檢查
$ gcc -Wformat-security test.c
warning: format not a string literal and no format arguments [-Wformat-security]
    fprintf(fp, user_input);

最佳實踐清單

  1. ✅ 永遠不將用戶輸入/外部數據直接作格式字符串
  2. ✅ 日志函數封裝:log_write(const char *fmt, ...) 內部vfprintf(fp, fmt, ap)
  3. ✅ 純字符串輸出用fputs/puts/fwrite
  4. ✅ 格式字符串必須為字面量或受信任常量
  5. ✅ 開啟-Wformat-security並視為錯誤-Werror=format-security
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved