用 Nix 打造模糊測試 netconsd 的工作流程
原文由 Michael Lynch 于 發布,訂閱此部落格
最近睡不著的時候,我就會找一些軟體來做模糊測試。
本週稍早,我想起了 Fady Othman 的文章「Meta Bug Bounty — Fuzzing ’netconsd’ for fun and profit」。這是一篇關於如何徹底對程式碼進行模糊測試的不錯教學。
跟大多數關於模糊測試的部落格文章一樣,我覺得要重現他的成果有點困難,因為讀者得自己想辦法複製作者的環境與工具鏈。
我想試著用 Nix 做一個容易重現的 Othman 模糊測試器版本,就像我對 xpdf 做的那樣。我那篇關於 xpdf 的文章要完整、精緻得多。這裡只是給有興趣的人看的一些快速筆記。
fuzz-netconsd
這是我花了幾個小時為 netconsd 建立的模糊測試工作流程:
如果你已經安裝了 git 和 Nix,可以用以下指令來執行我的工作流程:
nix run gitlab:mtlynch/fuzz-netconsd
這是我寫的第一個模糊測試器,它的模糊測試工具是直接透過函式呼叫來呼叫受測程式碼,而不是透過 shell 去執行編譯好的二進位檔。在 honggfuzz 和 AFL++ 中,這被稱為「persistent mode」(持久模式),我想是因為模糊測試器可以讓同一個處理程序持續執行,而不需要為每一次模糊測試都產生一個新的處理程序。
透過函式呼叫來做模糊測試很有趣,因為即使目標程式碼根本沒有發布共享函式庫,你也能做到。我是在工作流程的 installPhase 階段這麼做的,做法是把包含我想呼叫的程式碼的 .o 檔和對應的 .h 標頭檔複製到該步驟的 Nix 輸出目錄中。
有了需要的標頭檔和目的檔之後,我寫了這個簡單的測試框架,裡面只有剛好足夠呼叫我要進行模糊測試的函式 ncrx_process 的程式碼:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "ncrx.h"
int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) {
// Convert the random bytes of the payload to a null-terminated string.
char *payload = (char *)malloc(len + 1);
if (!payload) return 0;
memcpy(payload, buf, len);
payload[len] = '\0';
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
perror("clock_gettime");
return 1;
}
uint64_t now = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
struct ncrx *ncrx = ncrx_create(NULL);
// Call the function that we're fuzzing.
ncrx_process(payload, now, 0, ncrx);
// Clean up the resources we allocated.
ncrx_destroy(ncrx);
free(payload);
return 0;
}
我發現持久模式的模糊測試比我想像中簡單。我甚至不需要對 netconsd 有太多了解。我到現在還是不太清楚它確切是做什麼的,但已經足夠讓我能在模糊測試器底下呼叫它的其中一個解析函式了。
我大概不會有什麼發現
Othman 回報說他的模糊測試達到了 100% 涵蓋率,做得非常徹底。
我查看了 netconsd 的提交紀錄,自從 Othman 的研究之後,幾乎沒有什麼動靜。他在文章裡沒有明說,但我懷疑這個異動就是修掉了 Othman 發現的漏洞:
我懷疑在這個階段再做模糊測試也找不到什麼東西。我在跑了 12 小時都沒有當機後,就把模糊測試器停掉了。
未完成的工作
- Othman 使用了 klee 工具來為模糊測試語料庫產生邊界案例的輸入。我沒有實作這部分,但它很適合放進工作流程中,因為 klee 已經有 Nix 套件了。
- 我用的是 honggfuzz,而不是 Othman 用的 AFL++。我偏好 honggfuzz 的簡潔,但 Othman 對 AFL++ 的各種參數夠了解,能取得更高的涵蓋率。
- 我的初版實作用的是 AFL++,如果你想玩玩那個版本的話。
隨機一篇部落格
留言
登入後參與討論