TBC with a link to our BlackHat talk :)
Disclaimer: We used hAFL1 to fuzz Hyper-V’s virtual switch (vmswitch.sys). The fuzzer can be adjusted to fuzz other target drivers, but this tutorial will focus on fuzzing the above-mentioned driver.
This phase will build Linux, kvm and hAFL1 (a modified kAFL) on your Linux machine.
Note: make sure you run the fuzzer on a machine with a CPU that supports Intel-PT.
-
Clone this repository.
-
Enter the kAFL directory.
-
Run
install.sh all
You need to compile both the harness and fuzzing binaries from the kAFL codebase. We will be using two of them - packet_sender.exe (the program which triggers the packet-sending IOCTL) and loader.exe (which loads and executes packet_sender.exe).
-
Compile hAFL1’s fuzzing binaries by executing
./hAFL1/targets/windows_86_64/compile.sh
. -
Use Visual Studio to compile both:
- The harness driver (Harness.{sys,inf,cer,cat})
- StructsInitiator.exe
Note: During the installation, whenever Windows tries to restart, QEMU might hang with a black screen. If that is the case, quit QEMU (Ctrl+C
) and re-run the VM.
-
Obtain a Windows 10 ISO file. We’ll be using
Windows10_InsiderPreview_Client_x64_en-us_21354.iso
. -
Create a QEMU disk image by running
./hAFL1/qemu-5.0.0/qemu-img create -f qcow2 windows.qcow2 50G
. -
Download OVMF_CODE-pure-efi.fd.
-
Boot the machine:
./hAFL1/qemu-5.0.0/x86_64-softmmu/qemu-system-x86_64 -cpu host,hv_relaxed,hv_spinlocks=0x1fff,hv_vapic,hv_time,+intel-pt,-hypervisor,+vmx -machine q35 -enable-kvm -m 6144 -hda ./windows.qcow2 -bios /root/kAFL-1/OVMF_CODE-pure-efi.fd -cdrom ./Windows10_InsiderPreview_Client_x64_en-us_21354.iso -net none -usbdevice tablet
-
Install Windows Pro, which has Hyper-V capabilities, and complete the installation process.
-
Consider disabling Windows Defender permanently.
-
Disable memory dump collection during crashes, by running the following PowerShell command as Administrator:
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\CrashControl" -Name "CrashDumpEnabled" -Value 0
-
Disable Fast Startup from within an elevated command prompt:
REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Power" /V HiberbootEnabled /T REG_DWORD /D 1 /F
-
Enable Hyper-V on the VM by running the following within a PowerShell console as Administrator:
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
-
Create an empty VM by using PowerShell console as Administrator:
New-VM -Name "VM" -MemoryStartupBytes 512MB
-
Enable Driver Verifier for vmswitch.sys:
verifier /standard /driver vmswitch.sys
-
Turn off the VM.
-
Group the following files in a dedicated folder (files in bold should be compiled in previous steps):
- Devcon.exe (Debugging Tools for Windows) - this will install the harness driver
- Harness.sys, Harness.inf, Harness.cat, Harness.cer (Harness folder in hAFL1 Repo) - these files comprise the harness driver
- loader.exe (
./hAFL1/targets/windows_86_64/bin/loader/loader.exe
) - this is a kAFL-provided binary which loads - StructsInitiator.exe - This will prepare all necessary structures in vmswitch for the fuzzing process
- VMSwitchInitBuffer.bin (
./hAFL1/Harness/StructsInitiator/VMSwitchInitBuffer.bin
)- this is a file required by StructsInitiator.exe - EfiDSEFix.exe (Download here)
-
Copy the files from the dedicated folder to the VM by running:
./hAFL1/copy_files_to_vm.sh <dedicated_folder_path> windows.qcow2
The files will be copied to the
C:\
hard drive.
-
Create an overlay in a dedicated folder of overlays:
./hAFL1/qemu-5.0.0/qemu-img create -f qcow2 -b windows.qcow2 overlay_0.qcow2
-
Download EFIGuardBootable.iso and save it to the hAFL1 folder:
-
Execute the overlay VM:
./hAFL1/qemu-5.0.0/x86_64-softmmu/qemu-system-x86_64 -enable-kvm -cpu host,hv_relaxed,hv_spinlocks=0x1fff,hv_vapic,hv_time,+intel-pt,-hypervisor,-vmx -usbdevice tablet -m 6144 -bios /root/hAFL1/OVMF_CODE-pure-efi.fd -drive file=overlay_0.qcow2 -machine q35 -cdrom /root/hAFL1/EFIGuardBootable.iso boot menu=on
-
Boot to EFI Shell by pressing ESC once the “TianoCore” logo appears.
-
Enter “Boot Manager” and choose “EFI Internal Shell”
-
Execute the following command:
load FS1:\EFI\Boot\EfiGuardDxe.efi
-
You should see “Success” printed at the bottom of the screen.
-
Type
exit
and hit enter to exit the shell. -
Choose
Windows Boot Manager
and Windows will be loaded without PatchGuard. -
Open cmd.exe and execute
C:\EfiDSEFix.exe -d
-
Install harness driver by using devcon.exe:
devcon.exe install Harness.inf root\Harness
Approve the popup message.
-
Extract the new VM’s GUID and its network adapter’s GUID by running the following commands within PowerShell:
(Get-VMNetworkAdapter VM)[0].id
The output contains:
Microsoft:<VM_GUID>\<NETWORK_ADAPTER_GUID>
. -
Execute StructsInitiator.exe:
StructsInitiator.exe <VM_GUID> <ADAPTER_GUID> <VM_NAME> VMSwitchInitBuffer.bin
-
Execute loader.exe. This will create a snapshot to which the fuzzer will return after crashes.
If you’d like to run multiple VM instances to increase the performance of the fuzzing process, duplicate the overlay_0.qcow2 file by executing the following command. Replace X
with the number of instances you’d like to create in addition to the original overlay_0
file.
for f in overlay_{1..X}.qcow2; do cp overlay_0.qcow2 $f; done
- Run the following command and fetch from its output the address of vmswitch in memory:
python3 kAFL-Fuzzer/kafl_info.py -work_dir work -vm_dir <OVERLAY_DIR> -bios OVMF_CODE-pure-efi.fd -mem 6144 -agent targets/windows_x86_64/bin/info/info.exe -v
- Run the following command in order to start with the fuzzing process in debug mode. Replace
<start_address>
and<end_address>
with the output from the previous step.
python3 kAFL-Fuzzer/kafl_fuzz.py -work_dir work --purge -vm_dir <OVERLAY_DIR> -bios OVMF_CODE-pure-efi.fd -mem 6144 -agent targets/windows_x86_64/bin/fuzzer/packet_sender.exe -seed_dir <SEED_DIR> -p <NUMBER_OF_INSTANCES>-ip0 <start_address>-<end_address> --debug -v
- You can run the fuzzer’s GUI by executing in a separate terminal (or tmux pane):
python3 kAFL-Fuzzer/kafl_gui.py work
- Once you verify that everything works properly, you may omit the
-v
and--debug
flags to save some space on the disk (and not write all of the logs.)
See this guide.
We’ve integrated structure-awareness into hAFL1 by representing RNDIS packets as Protocol Buffers and mutating them using libprotobuf-mutator. As we never quite “productionized” this integration (and left it only as a PoC), we decided to give a general outline of the implementation instead of releasing half-working code.
The following are the steps we took to integrate structure-based mutations into hAFL1:
- We wrote
.proto
files that represent RNDIS packets and compiled them using Protocol Buffers compiler (protoc) into source and header files (rndis.cc, rndis.h}
). - We wrote two conversion functions:
a.
ProtoToData
- conversion from a Protocol Buffer object to a binary buffer. b.DataToProto
- conversion in the opposite direction. These functions allowed us to mutate Protocol Buffers objects but eventually send them as binary payloads to our fuzzed target (vmswitch.sys). - We used libprotobuf-mutator in order to apply field-based mutations on the Protocol Buffer objects. We also overrode some of the
Mutator
class’s mutation methods with those of libfuzzer, as we found them more efficient. However, this is completely optional. - Finally, we compiled a shared object (
rndis_fuzzer.so
) which exports two functions:afl_custom_init
which initializes aMutator
object with a random seed value.afl_custom_fuzz
which takes a single fuzzing input in binary format, converts it to a Protobuf object usingDataToProto()
, mutates it, converts back to binary usingProtoToData()
and returns the binary buffer.
- For integration with hAFL1, we added a new state to the
state_logic.py
file (in functionprocess_node()
). The new state, which we named LPBM for Lib-Proto-Buf-Mutator, callsafl_custom_fuzz()
with the current payload to generate a newly-mutated fuzzing input. - We’ve also made some modifications to
kafl_gui.py
to support the display of the new LPBM state.