Creating a Nix Workflow to Fuzz netconsd

Michael Lynch

建立用於 Fuzz netconsd 的 Nix 工作流程

最近,當我睡不著時,我就會找些軟體來做 fuzz test(模糊測試)。

本週稍早,我想起了 Fady Othman(法迪·奧斯曼)的文章「Meta Bug Bounty — Fuzzing ’netconsd’ for fun and profit.(《Meta 漏洞賞金——為樂趣與收益而對 netconsd 進行模糊測試》)」。這是一篇關於徹底對程式碼進行 fuzzing 的優秀教學。

和大多數 fuzzing 部落格文章一樣,我覺得這份實作有點難以重現,因為它需要讀者自行摸索如何複製作者的環境與工具鏈。

我想試著用 Nix 為奧斯曼的 fuzzer 製作一個容易重現的版本,就像我對 xpdf 所做的那樣。我那篇關於 xpdf 的文章要詳盡、完善得多。這裡只是給有興趣的人的一些快速筆記。

fuzz-netconsd

這是我花了幾個小時為 netconsd 建立的 fuzzing 工作流程:

如果你已經安裝了 Git 和 Nix,可以用以下指令執行我的工作流程:

nix run gitlab:mtlynch/fuzz-netconsd

這是我寫的第一個 fuzzer,它透過函式呼叫直接呼叫受測程式碼,而不是透過 shell 執行編譯好的二進位檔。在 honggfuzz 和 AFL++ 中,這被稱為 persistent mode(持久模式),我想是因為 fuzzer 可以持續保持同一個處理程序,而不需要為每一次 fuzz run 都產生一個新的處理程序。

透過函式呼叫進行 fuzzing 很有趣,因為即使目標程式碼根本沒有發布 shared library,你也能這麼做。我在工作流程的 installPhase透過將包含我想呼叫的程式碼的 .o 檔案,以及對應的 .h 標頭檔複製到該步驟的 Nix 輸出目錄來達成這一點。

在取得所需的標頭檔與目的檔後,我撰寫了這個簡單的 test harness,它只包含足以呼叫我正在進行 fuzzing 的函式 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;
}

我發現 persistent mode fuzzing 比我想像中容易。我甚至不需要對 netconsd 有太多了解。我到現在還是不太清楚它確切是做什麼的,但我已經了解足夠的內容,得以在 fuzzer 底下呼叫它其中一個解析函式。

我大概不會發現什麼

奧斯曼回報說他的 fuzzing 工作達到了 100% 覆蓋率,這非常徹底。

我查看了 netconsd 的提交紀錄,自奧斯曼的研究以來幾乎沒有任何活動。他在文章中沒有明確說明,但我懷疑這個異動就是修復了奧斯曼所發現的漏洞:

我懷疑此刻再進行 fuzzing 也不會有什麼收穫。我在 fuzzer 連續執行 12 小時都沒有當掉後就停止了。

未完成的工作

  • 奧斯曼使用了 klee 工具來為 fuzz corpus 產生邊界案例輸入。我沒有實作這部分,但它很適合整合到工作流程中,因為 klee 已經為 Nix 打包好了
  • 我使用的是 honggfuzz,而非奧斯曼所用的 AFL++。我偏好 honggfuzz 的簡潔,但奧斯曼對 AFL++ 的各種參數足夠熟悉,能夠取得更高的覆蓋率。
    • 我的初版實作使用的是 AFL++,如果你想試玩那個版本的話。

原文由 Michael Lynch 發布

本文章由 muse-spark-1.2-contributor 進行翻譯