Nix로 netconsd 퍼징 워크플로 만들기
원문은 Michael Lynch님이 에 게재했습니다. 이 블로그 구독하기
최근 잠이 잘 안 올 때면 퍼즈 테스트할 소프트웨어를 찾아본다.
이번 주 초, Fady Othman의 글 “Meta Bug Bounty — Fuzzing ’netconsd’ for fun and profit.”이 떠올랐다. 코드를 철저하게 퍼징하는 방법에 대한 훌륭한 튜토리얼이다.
대부분의 퍼징 관련 블로그 글처럼, 이 작업도 재현하기가 조금 어려웠다. 독자가 저자의 환경과 툴체인을 어떻게 재현할지 직접 알아내야 하기 때문이다.
xpdf에서 했던 것처럼 Nix를 이용해 Othman의 퍼저를 쉽게 재현할 수 있는 버전으로 만들어 보기로 했다. xpdf에 대한 내 글은 훨씬 더 자세하고 잘 다듬어져 있다. 이 글은 그냥 궁금한 사람들을 위해 남기는 간단한 메모 정도다.
fuzz-netconsd
몇 시간 만에 netconsd를 위해 만든 퍼징 워크플로는 다음과 같다:
git과 Nix가 설치되어 있다면 다음 명령어로 내 워크플로를 실행할 수 있다:
nix run gitlab:mtlynch/fuzz-netconsd
내가 만든 퍼저 중 퍼징 도구가 컴파일된 바이너리를 셸을 통해 실행하는 대신 함수 호출을 통해 테스트 대상 코드에 직접 호출하는 방식은 이번이 처음이다. 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;
}
persistent mode 퍼징은 생각보다 쉬웠다. netconsd에 대해 깊이 알 필요도 없었다. 지금도 그게 정확히 무슨 일을 하는지 잘 모르지만, 퍼저에서 파싱 함수 중 하나를 호출할 정도는 이해했다.
아마 아무것도 찾지 못할 것이다
Othman은 자신의 퍼징 작업으로 100% 커버리지를 달성했다고 했는데, 이는 극도로 철저한 수준이다.
netconsd의 커밋 히스토리를 확인해 보니 Othman의 작업 이후로는 거의 활동이 없었다. 그의 글에서 명시적으로 언급하지는 않았지만, Othman이 발견한 취약점을 수정한 변경 사항은 아마 이것일 거라고 생각한다:
이 시점에서 퍼징으로 뭔가를 더 찾을 수 있을 것 같지는 않다. 12시간 동안 크래시가 발생하지 않아 퍼저를 중단했다.
남은 작업
- Othman은 klee 도구를 사용해 퍼즈 코퍼스를 위한 엣지 케이스 입력을 생성했다. 나는 그 부분을 구현하지 않았지만, klee가 이미 Nix용으로 패키징되어 있어 워크플로에 잘 어울릴 것이다.
- 나는 Othman이 사용한 AFL++ 대신 honggfuzz를 사용했다. 나는 honggfuzz의 단순함을 선호하지만, Othman은 AFL++의 다양한 옵션을 잘 다룰 줄 알아 더 높은 커버리지를 달성했다.
- 내 첫 번째 초안 구현은 AFL++를 사용했으니, 그 버전을 써보고 싶다면 참고하면 된다.
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기