Rewind is a snapshot-based coverage-guided fuzzer targeting Windows kernel components.

Related tags

Testing rewind
Overview

README

Rewind is a snapshot-based coverage-guided fuzzer targeting Windows kernel components.

The idea is to start from a snapshot of a live running system. This snapshot is composed of the physical memory pages along with the state of the cpu.

This state is used to setup the initial state of a virtual cpu. By leveraging on-demand paging only the pages needed for the execution of the target function are read from the snapshot.

Because we use a dedicated virtual machine with only the physical memory pages useful for the execution of the target function, restoring a snapshot is fast.

As of now 2 backends are available:

A KVM backend is being developed and should be available soon.

Rewind provides 2 main features:

  • the ability to trace an arbitrary function
  • the ability to fuzz an arbitrary function

It also provides a basic TUI (Terminal User Interface) to report useful information regarding the fuzzing.

It has been tested on Windows and Linux (only bochs backend for linux for now).

Motivation

I always enjoyed doing kernel vulnerability research specially on Windows kernel. The process always involve a mix of static and dynamic analysis. Doing dynamic analysis can quickly become tedious. The cycle debug / crash / reboot / reset all breakpoints is slow and painful. When you want to do some fuzzing, it often requires you to setup one or several virtual machines plus a kernel debugger and craft some ghetto scripts to handle crash detection...

Doing snapshot with virtual machines helps but it's slow.

During 2018, Microsoft introduced a new set of API named Windows Hypervisor Platform (WHVP). These API allow to setup a partition (VM in hyper-V lingua) with some virtual processors and to have a control on the VM exits occurring in the virtual machine. It's almost like having your own VM-exit handler in userland. Quite handy to do useful things, for example Simpleator or applepie.

So I started to play with WHVP and made a first PoC allowing me to execute in a Hyper-V partition some shellcode. It was written in Python and quite slow. This first PoC evolved quite quickly to some kind of snapshot-based tracer. I wanted to have something to bootstrap the virtual CPU and quite easy to setup. So since I was already using a kernel debugger to play with my target, I decided to use kernel dumps made with WinDbg as my snapshot. With that I just needed to setup a partition with a virtual cpu. The virtual cpu context is set with the context taken from the dump. Whenever the virtual cpu needs a physical page I use the ones from the dump.

With this I was able to fork the state of the dump into a partition and then resume execution. It allowed me to easily trace the execution of my target function. By modifying the arguments and reverting the memory state of the partition it was also really easy to fuzz the target.

This work was presented at SSTIC conference in 2020 and released on github.

The tool implements 2 possibilities to obtain the coverage. The first one leverages the classical TF (Trap Flag) to have INT1 interruptions on every instruction. It requires to modify the target and it's slow. I would have preferred to use MONITOR trap flag. But WHVP doesn't offer this possibility.

In order to have proper performances (required for fuzzing), I decided to reduce the precision of the coverage and add a mode when you only know when an instruction is executed for the first time.

To do that I patch the pages fetched from the snapshot with 0xcc bytes (only for executable pages). When the cpu will execute these patched instructions the hypervisor will trap the exception and rewrite the instructions with the original code.

It's like having a unique software breakpoint set on every instruction. It works 95% of the time but in particular piece of code (ones with jump tables for example) it will fail because data will be replaced.

To overcome this one option would be to disassemble the code before mapping it and only patch what is needed (maybe next time).

During my experiment I encountered several limitations when using WHVP. It's slow, like really slow. VirtualBox source code have some interesting comments :)

So to have proper performance you really need to limit VM exits and it's incompatible if you want to use Hyper-V as a tracing hypervisor (since it requires a lot of VM exits).

During the same time I started to use bochs (specially the instrumentation part) to check if the traces obtained by the tool were correct. Bochs was some kind of oracle to see if I had divergent traces.

Bochs is faster than WHVP when doing full trace and you also have the benefits of having memory accesses plus other useful goodies.

I decided to add bochs as another backend. whvp was not a proper name anymore and I settled on rewind.

Typical usage

rewind was designed around my own workflow when I’m conducting security assessments for kernel drivers on Windows platform.

The first step is to install the target software inside a virtual machine. Since I’m using a mix of static and dynamic analysis I will also setup a kernel debugger.

After having opened some random drivers in IDA, I’ll quickly begin to target some functions. To do that I usually put some breakpoints with windbg and combined with ret-sync I can start to play.

That’s where rewind comes into play. Instead of editing random buffer in memory and singlestep and annotate the IDB to have a rough idea of what’s is going on. I’ll take a snapshot with windbg and use rewind instead.

It will ease the process a lot. Having a snapshot offers a lot of advantages. Everything is deterministic. You can replay ad nauseum a function call. You can launch a fuzzer if the target function looks interesting. You can even close the VM since it’s not needed anymore.

Prerequisites

Obviously you need Rust (installation tested on Windows and Linux with Rust 1.50). CMake is also needed by some dependencies.

Git

First clone the repository:

$ git clone [email protected]:quarkslab/rewind.git

Continue with the installation of bochs backend

Bochs

Clone bochscpu (https://github.com/yrp604/bochscpu) repository in the vendor directory:

$ cd vendor
$ git clone https://github.com/yrp604/bochscpu

Download the prebuilt bochs artifacts from bochscpu-build (https://github.com/yrp604/bochscpu-build)

$ curl.exe [artifact_url] --output bochs-x64-win.zip

Extract the lib and bochs folders into the bochscpu checkout.

$ Expand-Archive -Path .\boch-x64-win.zip -DestinationPath .\
$ copy -Recurse .\bochs-x64-win\msvc\* .\bochscpu\

WHVP

On Windows WHVP will also be built as backend.

In a elevated powershell session, use the following command to check if WHVP is enabled:

Get-WindowsOptionalFeature -FeatureName HypervisorPlatform -Online

FeatureName      : HypervisorPlatform
DisplayName      : Windows Hypervisor Platform
Description      : Enables virtualization software to run on the Windows hypervisor
RestartRequired  : Possible
State            : Enabled
CustomProperties :

If it is not enabled you can use Set-WindowsOptionalFeature cmdlet to enable. You'll also need to enable Hyper-V.

You also need to have a Windows SDK (10.0.19041.0) installed. You can download it from https://developer.microsoft.com/fr-fr/windows/downloads/windows-10-sdk/.

Build from master branch

You need to install LLVM and set the LIBCLANG_PATH environment variable (required by bindgen) See https://rust-lang.github.io/rust-bindgen/requirements.html for a detailed explanation.

$ $env:LIBCLANG_PATH="C:\Program Files\LLVM\bin"

From there you should be able to build rewind (nightly required because of unwind_attributes in bochscpu crate):

$ cd rewind_cli
$ cargo +nightly build --release

rewind binary will be available in the target/release directory.

You could also use cargo to install locally:

$ cd rewind_cli
$ cargo +nightly install --path .

Common build issues

  • if cmake is not in path, you will have an error when building zydis
> error: failed to run custom build command for `zydis v3.1.1`
  • if Windows SDK is different of the supported ones, whvp-sys will fail to build

Examples

A basic tutorial leveraging CVE-2020-17087 is provided in the examples directory

Roadmap

See TODO.md

Known Bugs/Limitations

  • This software is in a very early stage of development and an ongoing experiment.
  • Sometimes the tracer is unable to trace the target function (most common issue is invalid virtual cpu state).
  • When using hit coverage mode, the tracer will misbehave on some functions (it is the case with some switch tables). The reason is that each byte is replaced by software breakpoints (including data if they are present in a executable page). A better way to do that would be to obtain the list of all the basic blocks from a disassembler for example.
  • The target function will be executed with a unique virtual processor, you have no support for hardware so it's probable something will be wrong if you trace hardware related functions
  • This tool is best used for targetting specific functions
  • To have best performances, minimize VM exits and modified pages because they can be really costly and will increase the time needed to execute the function.
  • Don't use hyper-V to do snapshots. Windows Hyper-V are "enlightened" meaning they are using paravirtualization, it's currently not handled
  • Some symbols are not resolved properly

License

This tool is currently developed and sponsored by Quarkslab under the Apache 2.0 license.

Greetz

Hail to @yrp604, @0vercl0k, Alexandre Gazet for their help, feedbacks and thoughts. Thanks also to all my colleagues at Quarkslab!

You might also like...
StdFuzzer - StdFuzzer is the reference implementation of a generic bit-level fuzzer with LibAFL

StdFuzzer StdFuzzer is the reference implementation of a generic bit-level fuzzer with LibAFL Building Build with $ cargo build --release Compiling a

A fuzzer setup to fuzz libc functions.

libc-fuzzer This does what it sounds like! It attempts to, as automatically as possible, generate and run fuzzers for up to the entire set of libc (in

Travis CI and AppVeyor template to test your Rust crate on 5 architectures and publish binary releases of it for Linux, macOS and Windows

trust Travis CI and AppVeyor template to test your Rust crate on 5 architectures and publish binary releases of it for Linux, macOS and Windows Featur

A fast Rust-based safe and thead-friendly grammar-based fuzz generator

Intro fzero is a grammar-based fuzzer that generates a Rust application inspired by the paper "Building Fast Fuzzers" by Rahul Gopinath and Andreas Ze

A minimalist property-based testing library based on the arbitrary crate.

A minimalist property-based testing library based on the arbitrary crate.

 ArchTest is a rule based architecture testing tool for rust
ArchTest is a rule based architecture testing tool for rust

ArchTest is a rule based architecture testing tool. It applies static analyses on the specified rust project to extract use relationships.

Automated property based testing for Rust (with shrinking).

quickcheck QuickCheck is a way to do property based testing using randomly generated input. This crate comes with the ability to randomly generate and

Test for rust-based plugin system for swc

rust-dylib-test Steps Run cargo build in plugin_a Ensure that plugin_a dynamically links to runtime/common by otool -L plugin_a/target/debug/libplugin

Fixture-based test framework for Rust

Fixture-based test framework for Rust Introduction rstest uses procedural macros to help you on writing fixtures and table-based tests. To use it, add

Comments
  • Compilation fails due to llvm-config missing

    Compilation fails due to llvm-config missing

    Hey,

    Thank you so much for the project! Just a quick one, trying to compile and getting the following error:

    C:\Users\symeon\Desktop\rewind\rewind-cli>cargo +nightly build --release
    warning: couldn't execute `llvm-config --prefix` (error: Cannot find file. (os error 2))
    warning: set the LLVM_CONFIG_PATH environment variable to a valid `llvm-config` executable
       Compiling rewind-whvp v0.1.0 (C:\Users\symeon\Desktop\rewind\rewind-whvp)
       Compiling rewind-system v0.1.0 (C:\Users\symeon\Desktop\rewind\rewind-system)
    error[E0425]: cannot find value `WHV_RUN_VP_CANCEL_REASON_WHvRunVpCancelReasonUser` in this scope
        --> rewind-whvp\src\whvp.rs:520:12
         |
    520  |     User = WHV_RUN_VP_CANCEL_REASON_WHvRunVpCancelReasonUser as isize,
         |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: a constant with a similar name exists: `WHV_RUN_VP_CANCEL_REASON_WhvRunVpCancelReasonUser`
         |
        ::: C:\Users\symeon\Desktop\rewind\target\release\build\whvp-sys-9d4536c1db4309c4\out/whvp_bindings.rs:5658:1
         |
    5658 | pub const WHV_RUN_VP_CANCEL_REASON_WhvRunVpCancelReasonUser: WHV_RUN_VP_CANCEL_REASON = 0;
         | ------------------------------------------------------------------------------------------ similarly named constant `WHV_RUN_VP_CANCEL_REASON_WhvRunVpCancelReasonUser` defined here
    
    For more information about this error, try `rustc --explain E0425`.
    error: could not compile `rewind-whvp` due to previous error
    warning: build failed, waiting for other jobs to finish...
    error: build failed
    

    I've previously changed the LIBCLANG_PATH to "C:\Program Files\LLVM\bin" as per documentation but interesting enough there's no llvm-config on that folder... what am I missing here?

    Also, what about the WHV_RUN_VP_CANCEL_REASON_WHvRunVpCancelReasonUser function is complaining?

    Thanks :)

    opened by symeonp 8
  • Dump context pykd seems to not get snapshot for me

    Dump context pykd seems to not get snapshot for me

    Heya again,

    So following your instructions:

    0: kd> !load pykd
    0: kd> !py C:\Users\symeon\Desktop\rewind\scripts\pykd_dump_context.py D:\snapshots
    
    ************* Path validation summary **************
    Response                         Time (ms)     Location
    Deferred                                       srv*c:\symbols*https://msdl.microsoft.com/download/symbols
    saving context
    saving parameters
    saving memory
    

    This does not look to be working for me. I have tried with both Windbg Preview and the old x64, yet the debugger gets stuck with BUSY . However, context.json and params.json both have data, yet the actual mem.dmp is empty and I left it running a for a few hours.. any ideas what's going on?

    Thanks again!

    Edit: Am using pykd_ext_2.0.0.25 latest version on python 3.9.1 if that helps!

    opened by symeonp 3
  • Error(

    Error("unknown error: missing page 0")

    Hello,

    Just got a snapshot of a potential target, unfortunately am getting the below error when trying to trace it with WHVP:

    :: Running tracer
    ==> loading snapshot
    ==> will use whvp backend
    ==> setting tracer initial state
    rax=0000000000000000 rbx=0000000000000001 rcx=000000000148af10
    rdx=00000000c0000000 rsi=0000000000a9b470 rdi=0000000000000000
    rip=00007ffbe4a87860 rsp=000000000188ecb8 rbp=000000000148af10
     r8=0000000000000000  r9=000000000188ed08 r10=000000000188edbb
    r11=000000000148af46 r12=000000000188ed88 r13=000000000188eda0
    r14=0000000000000001 r15=00000000012d1dc0
    cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b  rflags=0246
    ==> running tracer
    ==> executed 1356 instruction(s) in 39.8778ms (Error("unknown error: missing page 0"))
    ==> seen 652 unique address(es)
    ==> mapped 35 page(s) (143.36 kB)
    ==> 17 page(s) were modified
    

    Also interesting enough, the bochs backend completely hangs on the 'running tracer' phase... I believe the snapshot is decent as it targets exactly a single function. Not sure how much I can help you unless I provide you with the snapshot so you can reproduce it?

    Thanks

    opened by symeonp 2
  • rewind_cli failed to compile

    rewind_cli failed to compile

    error[E0599]: no method named `multiple` found for struct `Arg` in the current scope
       --> rewind-cli\src\cli.rs:834:12
        |
    834 |     #[clap(multiple(true), number_of_values(1), parse(try_from_str=parse_hex))]
        |            ^^^^^^^^ method not found in `Arg<'_>`
    
    opened by erynian 0
Owner
Quarkslab
Quarkslab
Structure-aware, in-process, coverage-guided, evolutionary fuzzing engine for Rust functions.

fuzzcheck Fuzzcheck is a structure-aware, in-process, coverage-guided, evolutionary fuzzing engine for Rust functions. Given a function test: (T) -> b

Loïc Lecrenier 394 Dec 20, 2022
A symbolic-model-guided fuzzer for TLS

tlspuffin TLS Protocol Under FuzzINg A symbolic-model-guided fuzzer for TLS Master Thesis | Thesis Presentation | Documentation Description Fuzzing im

null 69 Dec 20, 2022
Advanced Fuzzing Library - Slot your Fuzzer together in Rust! Scales across cores and machines. For Windows, Android, MacOS, Linux, no_std, ...

LibAFL, the fuzzer library. Advanced Fuzzing Library - Slot your own fuzzers together and extend their features using Rust. LibAFL is written and main

Advanced Fuzzing League ++ 1.2k Dec 29, 2022
insta: a snapshot testing library for Rust

insta: a snapshot testing library for Rust Introduction Snapshots tests (also sometimes called approval tests) are tests that assert values against a

Armin Ronacher 1.4k Jan 1, 2023
a grammar based feedback fuzzer

Nautilus NOTE: THIS IS AN OUTDATE REPOSITORY, THE CURRENT RELEASE IS AVAILABLE HERE. THIS REPO ONLY SERVES AS A REFERENCE FOR THE PAPER Nautilus is a

Chair for Sys­tems Se­cu­ri­ty 157 Oct 26, 2022
Black-box fuzzer that fuzzes APIs based on OpenAPI specification. Find bugs for free!

OpenAPI fuzzer Black-box fuzzer that fuzzes APIs based on OpenAPI specification. All you need to do is to supply URL of the API and its specification.

Matúš Ferech 406 Dec 31, 2022
Easy-to-use grammar-based black-box fuzzer. Has found dozens of bugs in important targets like Clang, Deno, and rustc.

tree-crasher tree-crasher is an easy-to-use grammar-based black-box fuzzer. It parses a number of input files using tree-sitter grammars, and produces

Langston Barrett 5 Mar 28, 2023
A fuzzer framework built in Rust

lain This crate provides functionality one may find useful while developing a fuzzer. A recent nightly Rust build is required for the specialization f

Microsoft 469 Dec 9, 2022
Fuzzer to automatically find side-channel (timing) vulnerabilities

SideFuzz: Fuzzing for side-channel vulnerabilities SideFuzz is an adaptive fuzzer that uses a genetic-algorithm optimizer in combination with t-statis

PHAYES 94 Sep 29, 2022
An example fuzzer about how to fuzz a JS engine combinign Nautilus with Token-level fuzzing

LibAFL QuickJS Fuzzing Example An example fuzzer about how to fuzz a JS engine combinign Nautilus with Token-level fuzzing. Prepare Make sure to have

Andrea Fioraldi 32 Dec 21, 2022