
wtf v0.5.8
분산형, 코드 커버리지 기반 스냅샷 퍼저로, Windows 및 Linux의 사용자 모드와 커널 모드 타겟을 지원하며, 에뮬레이터 및 하이퍼바이저 백엔드를 포함합니다.
what the fuzz
분산형, 코드 커버리지 기반, 크로스 플랫폼 스냅샷 기반 퍼저로, Microsoft Windows 및 Linux 사용자 모드(실험적!)에서 실행되는 사용자 또는 커널 모드 대상을 공격하도록 설계되었습니다.
개요
what the fuzz 혹은 wtf는 분산형, 코드 커버리지 기반, 사용자 정의 가능, 크로스 플랫폼 스냅샷 기반 퍼저로, Microsoft Windows 또는 Linux(실험적, linux_mode 참조)에서 실행되는 사용자 또는 커널 모드 대상을 공격하도록 설계되었습니다. 대상의 실행은 bochscpu를 사용한 에뮬레이터 내부(가장 느리고 정밀), Windows Hypervisor Platform APIs를 사용한 Windows VM 내부, 또는 KVM APIs를 사용한 Linux VM 내부(가장 빠름)에서 수행할 수 있습니다.
이는 다양한 소프트웨어에서 메모리 손상 취약점을 발견했습니다: IDA Pro, 유명 AAA 게임, Windows 커널, Microsoft RDP 클라이언트, NVIDIA GPU Display 드라이버 등이 있습니다.
컴파일된 바이너리는 CI 아티팩트 또는 Windows 및 Linux용 릴리스 섹션에서 확인할 수 있습니다.
이 퍼저의 역사나 실제 대상에서의 사용 방법에 대해 더 알고 싶다면, 시작하기 좋은 다음 글들을 추천합니다 🔥
- Building a new snapshot fuzzer & fuzzing IDA
- Fuzzing Modern UDP Game Protocols With Snapshot-based Fuzzers by Markus Gaasedelen
- Fuzzing RDPEGFX with "what the fuzz" by Colas Le Guernic, Jérémy Rubert, and Anonymous
- A Journey to Network Protocol Fuzzing – Dissecting Microsoft IMAP Client Protocol by Wayne Chin Yick Low
- Snapshot Fuzzing section of Trail Of Bits' Testing Handbook
- Attacking EDRs Part 4: Fuzzing Defender's Scanning and Emulation Engine (mpengine.dll) by Manuel Feifel
사용법
기능을 시험해보는 가장 좋은 방법은 fuzzer_hevd / fuzzer_tlv_server 모듈을 사용하는 것입니다. target-hevd.7z / target-tlv_server.7z 아카이브를 다운로드하여 targets/ 디렉토리에 압축 해제하세요. 아카이브에는 모든 대상에 예상되는 디렉토리 트리가 포함되어 있습니다:
inputs는 입력 테스트 케이스가 들어가는 폴더입니다.outputs는 현재 최소 세트 파일이 저장되는 폴더입니다.coverage는.cov파일이 위치해야 하는 폴더입니다.crashes는 크래시가 저장되는 폴더입니다.state는 메모리 덤프(mem.dmp), CPU 상태(regs.json), 그리고 심볼 저장소(symbol-store.json)가 저장되는 폴더입니다. 심볼 저장소는 Linux 시스템에서 심볼/dbgeng을 지원하지 않기 때문에 중단점을 설정할 위치를 알기 위해 사용되는 간단한 JSON 파일입니다. wtf는 Windows에서 대상을 실행할 때마다 런타임에 이 파일을 생성합니다.
다음은 최신 릴리스에 첨부된 target-hevd.7z 파일을 다운로드하여 wtf의 복제본 targets 디렉토리에 압축 해제했다고 가정합니다. wtf/targets/hevd 디렉토리가 있어야 하며, 그 안에 inputs / outputs 등의 디렉토리가 있습니다.
서버 노드 시작
서버는 기본적으로 두뇌 역할을 하며 모든 상태(집계된 코드 커버리지, 코퍼스 등)를 추적하고, 테스트 케이스를 생성하여 클라이언트에 배포합니다.
로컬 서버 노드를 시작하는 방법은 다음과 같습니다:```text wtf.exe master --name hevd --max_len=1028 --runs=10000000
`max_len` 옵션은 생성된 테스트 케이스의 크기를 제한하는 데 사용됩니다. `runs`는 생성할 테스트 케이스의 개수이고, `address`는 **wtf**가 수신 대기해야 하는 주소를 지정합니다. `target`은 위에서 설명한 디렉터리 트리가 있는 디렉터리입니다(사용자는 `--input` / `--output` / `--crashes`로 해당 디렉터리를 재정의할 수도 있습니다). `name`은 퍼징 모듈 이름을 지정하여, 마스터가 정의한 생성자 함수를 호출할 수 있도록 합니다.
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/4a03fc75eed3ed5a92f7f10def697dbf220ae36b0701f05b37759eb432fc0fd9.webp">
</p>
### 퍼징 노드
클라이언트 노드는 서버가 생성하여 배포한 테스트 케이스를 실행하고, 그 결과(코드 커버리지, 결과 등)를 서버로 다시 전달합니다.
*bochscpu* 백엔드를 사용하는 클라이언트 노드를 시작하는 방법은 다음과 같습니다.```text
wtf.exe fuzz --name hevd --limit 10000000
The fuzz 서브커맨드는 name 옵션과 함께 사용되어 어떤 퍼저 모듈을 사용할지 지정하고, backend는 실행 백엔드를 지정하며, limit는 테스트 케이스당 실행할 최대 명령어 수를 지정합니다(백엔드에 따라 이 옵션의 의미가 다릅니다).
테스트 케이스 실행
테스트 케이스(또는 테스트 케이스로 가득 찬 폴더)를 실행하려면 run 서브커맨드를 사용할 수 있습니다.
다음은 crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 테스트 케이스를 실행하는 방법입니다:```
wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/49b3ca8582d6314724e5615c687472499d5f8f41d04c9543c0a6a070c51f56f8.webp">
</p>
### 코퍼스 minset 하기
코퍼스를 minset 하기 위해서는, 퍼징 작업과 마찬가지로 서버 노드 하나와 필요한 만큼의 클라이언트 노드를 사용해야 합니다. `runs` 옵션을 0으로 설정하기만 하면 됩니다.
이렇게 하면 `outputs`에 있는 코퍼스를 `minset` 디렉토리로 minset할 수 있습니다 (또한 `inputs`와 `outputs` 디렉토리를 재정의하는 방법을 보여줍니다):```
wtf.exe master --name hevd --max_len=1028 --runs=0 --inputs=outputs --outputs=minset
실행 트레이스 생성
실행 백엔드에서 내부를 조사할 수 있는 주요 메커니즘은 실행 트레이스를 생성하는 것입니다. bochscpu는 이를 수행하는 가장 빠른 백엔드입니다. 다른 백엔드에서 VMX 모드를 종료하는 것은 매우 비용이 많이 들기 때문입니다.
다음은 crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 테스트 케이스에 대한 실행 트레이스를 생성하는 방법입니다:```
wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 --trace-type=rip
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/340b4b251e378eaf21c6f5ffdd4f013bd2cef89f23a604cffd0ebf897d27c71f.webp">
</p>
실행 트레이스를 심볼화하려면 [symbolizer-rs](https://github.com/0vercl0k/symbolizer-rs)를 사용해야 합니다. 다음은 위에서 생성된 `crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.trace` 실행 트레이스를 심볼화하는 방법입니다:```
symbolizer-rs.exe --trace crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.rip.trace
Tenet 추적 생성
더 많은 컨텍스트 인식이 필요하다면, bochscpu 백엔드를 사용하여 Tenet 추적 탐색기에 로드할 수 있는 실행 추적을 생성할 수 있습니다. 아래에서는 memmove의 충돌에서 시작하여 소스 포인터가 어디에서 오는지(사용자 모드!)를 역추적합니다:```
wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 --trace-type=tenet
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/34df2f5ca8f378db664d3f01bcbefdd43409e300d256d50e3f4f630eb06cc7be.webp">
</p>
### 코드 커버리지 트레이스 생성
코드 커버리지 트레이스를 생성하려면 `run` 하위 명령어에 `--trace-type=cov` 옵션을 사용하면 됩니다.
`minset` 폴더 내의 모든 파일에 대한 코드 커버리지 트레이스를 생성하여 `coverage-traces` 폴더에 저장하는 방법은 다음과 같습니다:
```bash
python kitty.py run --trace-type=cov --pcap-dir minset --output-dir coverage-traces
wtf.exe run --name hevd --input minset --trace-path=coverage-traces --trace-type=cov
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/c338442f63a8dd105180ec38f8d3f7ffd5679442b400d5fb14e843ef73658ce3.webp">
</p>
이러한 트레이스들은 심볼화되지 않았기 때문에 [lighthouse](https://github.com/gaasedelen/lighthouse)에 직접 로드할 수 없습니다.
`coverage-traces` 폴더 내의 모든 파일을 심볼화하고 결과를 `coverage-traces-symbolized`에 쓰는 방법은 다음과 같습니다:```
symbolizer-rs.exe --trace coverage-traces -o coverage-traces-symbolized --style modoff
마지막으로, 이들을 lighthouse에 로드할 수 있습니다:
또한 개별 코드 커버리지에 신경 쓰지 않는다면, 마스터는 실행된 고유 집계 코드 커버리지가 포함된 coverage.cov 파일을 유지합니다. 이를 통해 퍼징 작업 중에 전역 코드 커버리지를 빠르게 확인할 수 있습니다.
어떻게 작동하나요?
wtf는 실행 백엔드를 통해 사용자 모드와 커널 모드를 실행하며, 사용자가 대상에 테스트 케이스를 삽입하는 데 의존합니다. 다른 전통적인 퍼저 도구와 달리, wtf는 많은 무거운 작업을 직접 수행하지 않습니다. 사용자가 작업을 수행합니다. 사용자는 하네스된 대상을 매우 잘 알고 있어야 하며, 대상을 온보딩하는 것은 시간이 걸리는 반복적인 과정입니다. 하지만 해킹에 준비가 되었다면 많은 유연성을 제공합니다 :)
대상을 하네스하는 일반적인 워크플로는 다음과 같습니다:
-
대상이 하나의 가상 CPU와 4GB RAM을 가진 Hyper-V VM(Windows 실행)에서 실행되도록 설정합니다.
-
KD를 사용하여 대상을 원하는 상태로 만듭니다. 예를 들어, HEVD의 IOCTL 핸들러를 대상으로 할 때, 클라이언트가 DeviceIoControl을 호출하기 직전 사용자 모드에서 멈추도록 선택했습니다. 이는 대상에 따라 달라지지만, 일반적으로 퍼징하려는 코드에 가까운 지점이 좋습니다.
kd> r rax=000000dfd98ff3d0 rbx=0000000000000088 rcx=0000000000000088 rdx=00000000deadbeef rsi=0000000000000000 rdi=0000000000000000 rip=00007ff6f5bb111e rsp=000000dfd98ff380 rbp=0000000000000000 r8=000000dfd98ff3d0 r9=0000000000000400 r10=000002263e823055 r11=00007ff6f5bcb54d r12=0000000000000000 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 iopl=0 nv up ei pl nz na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 hevd_client!main+0xae: 00007ff6`f5bb111e ff15dc1e0100 call qword ptr [hevd_client!_imp_DeviceIoControl (00007ff6`f5bc3000)] ds:002b:00007ff6`f5bc3000={KERNEL32!DeviceIoControlImplementation (00007ff8`3e2e6360)} -
snapshot을 사용하여 커널 크래시 덤프와 CPU 상태가 포함된
regs.json파일을 생성합니다. 이 파일들을target디렉토리 아래의state디렉토리(예:targets/hevd/state)에 덤프하는 것을 권장합니다:kd> .load c:\work\codes\snapshot\target\release\snapshot.dll kd> !snapshot -h [snapshot] Usage: snapshot [OPTIONS] [STATE_PATH] Arguments: [STATE_PATH] The path to save the snapshot to Options: -k, --kind <KIND> The kind of snapshot to take [default: full] [possible values: active-kernel, full] -h, --help Print help kd> !snapshot c:\work\codes\wtf\targets\hevd\state [snapshot] Dumping the CPU state into c:\work\codes\wtf\targets\hevd\state\regs.json.. [snapshot] Dumping the memory state into c:\work\codes\wtf\targets\hevd\state\mem.dmp.. Creating c:\\work\\codes\\wtf\\targets\\hevd\\state\\mem.dmp - Full memory range dump 0% written. 5% written. 1 min 50 sec remaining. 10% written. 1 min 17 sec remaining. 15% written. 1 min 30 sec remaining. [...] Wrote 4.0 GB in 1 min 32 sec. The average transfer rate was 44.5 MB/s. Dump successfully written [snapshot] Done! -
퍼저 모듈을 만들고, 대상에 테스트 케이스를 삽입하는 코드를 작성한 후, 충돌을 감지하거나 테스트 케이스 종료를 감지하기 위한 다양한 조건을 정의합니다.
-
Mutator_t 인터페이스를 서브클래싱하여 자신만의 뮤테이터/제너레이터를 만들 수도 있습니다. fuzzer_tlv_server.cc는 자체 구현 방법을 이해하는 좋은 예제입니다.
이 시점에서 퍼저 모듈이 예상대로 작동하는지 반복하고 확인해야 합니다. 실행 백엔드는 블랙박스이므로 실행 추적을 생성하여 올바른 경로를 통과하고 올바른 작업을 수행하는지 확인해야 합니다. 이 단계에서는 주로 bochscpu 백엔드를 사용합니다. 이는 완전히 결정적이며, 빠르게 시작하고, 실행 추적 생성이 가능하며, 코드 커버리지가 무료로 제공되는 등 개발 및 프로토타이핑에 더 좋은 환경입니다.
모듈에 만족하면, 필요에 따라 winhv / kvm 백엔드에서도 작동하도록 설정하는 것을 고려할 수 있습니다. bochscpu 백엔드와 다른 백엔드의 주요 차이점 중 하나는 다른 백엔드가 코드 커버리지 정보를 제공하기 위해 소프트웨어 브레이크포인트를 사용한다는 점입니다. 결과적으로, IDA에서 커버리지를 원하는 모듈을 로드하고 gen_coveragefile_ida.py 스크립트를 사용하여 wtf에서 로드하는 간단한 JSON 파일을 생성해야 합니다. 원하는 도구를 사용하여 이 JSON 파일을 직접 생성할 수도 있습니다. 기본적으로 기본 블록 가상 주소의 목록입니다.
또한 !wow64exts.sw Windbg 명령을 사용하여 스냅샷을 생성하기 직전에 64비트 컨텍스트로 전환하여 WoW64 애플리케이션을 대상으로 할 수 있습니다(@cube0x8님이 이 트릭을 공유해 주셨습니다!):```
32.kd:x86> !wow64exts.sw
The context is partially valid. Only x86 user-mode context is available.
Switched to Host mode
32.kd> !snapshot
## 대상에게 멀티 패킷을 전달하는 방법?
복잡한 대상은 일반적으로 복잡한 상태도 함께 가지며, 복잡한 문제를 트리거하기 위해 세션에서 여러 개의 테스트케이스를 전달해야 할 가능성이 있습니다. [tlv_server.cc](https://github.com/0vercl0k/wtf/blob/HEAD/src/tlv_server/tlv_server.cc)는 하나의 테스트케이스만으로 파싱 함수를 실행해서는 버그를 발견하기에 충분하지 않은 서버의 예시입니다.
이 경우를 처리하려면, 이 문제를 해결하는 방법을 보여주는 [fuzzer_tlv_server.cc](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_tlv_server.cc)를 확인하세요.
## 사용자 정의 변이기/생성기를 제공하는 방법?
**wtf**는 두 가지 인기 있는 일반 변이기인 [libfuzzer](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/mutator.h)와 [honggfuzz](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/mutator.h)를 함께 제공합니다. 자체 변이기를 제공하거나 직접 테스트케이스를 생성하고 싶을 수도 있습니다.
그렇게 하려면 [Mutator_t](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/mutator.h) 인터페이스를 서브클래싱하고, 퍼징 모듈을 정의할 때 변이기를 인스턴스화하는 함수를 등록하면 됩니다:```c++
class CustomMutator_t : public Mutator_t {
public:
static std::unique_ptr<Mutator_t> Create(std::mt19937_64 &Rng,
const size_t TestcaseMaxSize) {
return std::make_unique<CustomMutator_t>(Rng, TestcaseMaxSize);
}
// ...
};
Target_t target("target", Init, InsertTestcase, Restore, CustomMutator_t::Create);
Check out the CustomMutator_t class in the fuzzer_tlv_server.cc module for a complete example.
실행 백엔드
이 섹션에서는 실행 백엔드 간의 다양한 차이점에 대해 간략히 설명합니다.
bochscpu
- ✅ 전체 시스템 코드 커버리지 (
--edges를 통해 엣지 커버리지 가능), - ✅ 요구 페이징,
- ✅ 타임아웃은 매우 정확한 명령어 수입니다,
- ✅ 전체 실행 트레이스 지원,
- ✅ 완전 결정적,
- ❌속도는 짧은 실행에는 좋지만 긴 실행에는 좋지 않은 것 같습니다 (IDA를 퍼징할 때 KVM보다 약 100배 느림).
whv
- ✔ 소프트웨어 브레이크포인트를 통한 코드 커버리지,
- ❌ 요구 페이징으로 인해 시작이 느림 (전체 충돌 덤프를 메모리에 로드해야 하므로),
- ✔ 타임아웃은 타이머로 구현됨,
- ✅ 전체 실행 트레이스 지원되지만 느림 (VMX 종료는 비용이 많이 듦),
- ✔ 비결정성의 원인을 수동으로 처리하면 결정적 (예:
rdrand를 사용하는nt!ExGenRamdom패치), - ✔ 긴 실행에는 속도가 괜찮은 것 같음 (whv에는 병목 현상이 많지만; IDA를 퍼징할 때 kvm보다 약 10배 느림).
KVM
- ✔ 소프트웨어 브레이크포인트를 통한 코드 커버리지,
- ✅ UFDD를 통해 요구 페이징 지원,
- ✔ 타임아웃은 타이머로 구현됨. ✅ 하드웨어가 PMU 가상화를 지원하는 경우 X개의 퇴역 명령어 후에 PMI를 생성하는 데 사용됩니다 (
MSR_IA32_FIXED_CTR0), - ✅ 전체 실행 트레이스 지원되지만 느림 (VMX 종료는 비용이 많이 듦),
- ✔ 비결정성의 원인을 수동으로 처리하면 결정적 (예:
rdrand를 사용하는nt!ExGenRamdom패치), - ✅ 긴 실행에 가장 빠름 (~5억 - 15억 명령어; IDA를 퍼징할 때 bochscpu보다 약 100배 빠름, whv보다 약 10배 빠름).
빌드
CI는 Ubuntu에서 clang++ / g++을 모두 사용하여 wtf를 빌드하고, Windows에서는 Microsoft의 Visual Studio를, OSX에서는 clang++을 사용하여 빌드합니다.
직접 빌드하려면 Visual Studio Developper Command Prompt를 시작하고, build-release.bat (Ninja 생성기 사용) 또는 build-release-msvc.bat(Visual Studio 솔루션 파일 생성)를 실행해야 합니다:``` (base) wtf\src\build>build-release.bat [...] [2/2] Linking CXX executable wtf.exe
(base) wtf\src\build_msvc>..\build\build-release-msvc.bat [...] Finished generating code wtf.vcxproj -> wtf\src\build_msvc\RelWithDebInfo\wtf.exe Building Custom Rule wtf/src/CMakeLists.txt
## 저자
* Axel '[0vercl0k](https://twitter.com/0vercl0k)' Souchet
## 기여자
특별히 감사드립니다:
- [@yrp604](https://github.com/yrp604)님께 프로젝트 전반에 걸쳐 귀중한 의견을 제공해 주신 점,
- [@masthoon](https://github.com/masthoon)님께 [HEVD](https://github.com/hacksysteam/HackSysExtremeVulnerableDriver) 보안 모드를 대상으로 데모를 작성하자고 제안해 주신 점,
- [Markus Gaasedelen](https://github.com/0vercl0k/wtf/pull/12/)님께 Tenet 지원을 추가해 주신 점,
- [@y0ny0ns0n](https://github.com/y0ny0ns0n)님께 [다중 입력 퍼징 예제](https://github.com/0vercl0k/wtf/pull/67)를 기여해 주신 점,
- [Colas Le Guernic](https://github.com/clslgrnc) / Jérémy Rubert / Anonymous님께 [bochscpu에 엣지 커버리지](https://github.com/0vercl0k/wtf/pull/137)를 구현해 주신 점,
- [@1ndahous3](https://github.com/1ndahous3)님께 [일반적인 ioctl 퍼저 모듈](https://github.com/0vercl0k/wtf/pull/155)을 기여해 주신 점,
- Cisco ASIG의 Jason Crowder / [Kyle Ossinger](https://k0ss.net/)님께 [Linux 모드](https://github.com/0vercl0k/wtf/pull/192)에 대해,
- 그리고 다른 모든 기여자분들께 🙏
[  ](https://github.com/0vercl0k/wtf/graphs/contributors)