Fuzzing Windows
- Download Volatility3 from https://github.com/volatilityfoundation/volatility3/releases/latest
- Build it:
python3 setup.py build
- Boot your Windows VM
- Run
vmi-win-guid name <vm_name>
# vmi-win-guid name windows10
Windows Kernel found @ 0x3400000
Version: 64-bit Windows 10
PE GUID: d3f646971046000
PDB GUID: 3fcc539ff307dd2d9c509206d352b9aa1
Kernel filename: ntkrnlmp.pdb
Note the PDB GUID and Kernel filename
- Use Volatility3's pdbconv to grab the PDB and convert it to JSON:
python3 volatility3/volatility/framework/symbols/windows/pdbconv.py \
--guid 3fcc539ff307dd2d9c509206d352b9aa1 \
-p ntkrnlmp.pdb \
-o windows10.json
When you have the ability to recompile your target you can use the standard cpuid
harness to mark the beginning and end of the code you want to fuzz. Follow the setup steps from the official Microsoft documentation to set up the Visual Studio environment for WDK: Download the Windows Driver Kit (WDK). You can take a look at the testmodule_win sample driver that includes the cpuid
harness you can compile into your target:
TITLE kfx harness
.code
;void harness(void);
harness PROC
push rax
push rbx
push rcx
push rdx
mov rax,13371337h
cpuid
pop rdx
pop rcx
pop rbx
pop rax
ret
harness ENDP
;void harness_extended(int magic_mark, unsigned long long address, size_t size);
harness_extended PROC
push rax
push rbx
push rcx
push rdx
push rsi
mov rax,rcx
mov rsi,rdx
mov rcx,r8
cpuid
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret
harness_extended ENDP
END
Make sure you enable loading of the test kernel module in your VM by running the following commands and then rebooting your VM:
bcdedit /set nointegritychecks on
bcdedit /set testsigning on
The setup and fuzzing phase are the same as when fuzzing Linux. In order to override the default built-in Linux sink points, you can specify --sink
on the kfx
command line to specify the functions you want to report as crash. For example, in the following we'll report all calls to KiDispatchException as a crash to AFL:
AFL_KILL_SIGNAL=15 ./AFLplusplus/afl-fuzz -i input -o output -- \
./kfx --domain windows10 --json ~/windows10.json \
--address 0xffffde8cd4144800 \
--input-limit 69 \
--input @@ --ptcov \
--sink KiDispatchException
If you don't have the ability to recompile your target you can use breakpoints
as your harness. You will want to use windbg
to set the breakpoints in your target that will mark the beginning and end of the code you are fuzzing. To be able to attach windbg remotely to your VM you'll need 2 Windows VMs: one that you will fuzz (target VM) and the other where you are running windbg
. The VM config for both needs to contain the option serial = 'pty'
and you'll need socat
installed.
- Boot your VM that will run
windbg
, startwindbg
and pressCTRL+K
to open the debug menu, select COM as your target and hit OK. - Boot your target VM, open a
cmd.exe
prompt with Administrator privileges and run:
bcdedit /debug on
bcdedit /dbgsettings serial debugport:1 baudrate:115200
- Reboot your target VM.
- Run
scripts/serial-bridge.sh
from this repository with the names of the VMs. For example./scripts/serial-bridge.sh windows10-1 windows10-2
. - As the target VM is booting the
windbg
session will activate. - Select the
Debug -> Break
option inwindbg
to activate debugging (you can also wait till Windows finished booting completely). - Place a breakpoint into the target where you want to start fuzzing at (for example
bu sioctl!SioctlDeviceControl
) - Resume the VM
Debug -> Go
- Trigger the execution of your target
- When the start breakpoint is caught by
windbg
note what the first byte is at your target RIP - Place a breakpoint into the target where you want to stop fuzzing at (make sure it's not in the fast-path of the kernel's normal execution, you want the end breakpoint to trigger only if the start breakpoint triggers as well)
- Resume the VM
Debug -> Go
- Start kfx in dom0 with
--setup --harness breakpoint --start-byte <first_byte>
where <first_byte> is the byte you found above in hex (for example0x48
) - Trigger the execution of your target again
- kfx will catch when the breakpoint triggers now instead of windbg
- You can start fuzzing your target
The above process assumes your target code (including your start and end breakpoints) only execute when you manually trigger them. In case you need to place your end breakpoint into a location that's in the kernel's execution fast-path, then you'll not be able to place it directly with windbg
. You can still place the start breakpoint with windbg
. Catch the start breakpoint with kfx --setup
as noted above, and use rwmem
to write the end breakpoint into the VM.
echo -n -e '\xCC' > cc
./rwmem --domid <target_domid> --read <end_harness> --file backup --limit 1
./rwmem --domid <target_domid> --write <end_harness> --file cc --limit 1
To figure out where to place the end harness manually may require additional information. For example to place it at the address where a function returns to you want to catch the function's entry (for example bu sioctl!SioctlDeviceControl
). When kfx catches the start breakpoint run xen-hvmctx <target_domid> | grep rsp
and check what the address is in the RSP register (for example rsp 0xffff82021097f688
).
Use rwmem
to figure out what's the return address is by checking the value on the stack where rsp points to:
./rwmem --domid <target_domid> --read <rsp_value> --limit 8 --file ret
xxd -e ret | awk '{ print "0x" $3 $2 }'
Once you know what the return address is you can use rwmem as described above to write the breakpoint (cc) into that address to be your end harness. Once you are finished fuzzing, you will want to remove the end harness breakpoint you manually placed by writing the original byte saved in the backup
file above.