Writing A Basic Fuzzer
In our fuzzing process, we aim to uncover vulnerabilities by subjecting a vulnerable driver to various inputs and observing its behaviour.
We will start writing a basic fuzzer in C
and use this template to write an exploit for the vulnerable driver once we have found the vulnerable I/O Control Code(s).
Project
Open Visual Studio and go to "File" -> "New" -> "Project" to create a new project.
In the "Create a new project" window, select "Visual C++" from the left sidebar, then choose "Console App (.NET Core)" from the available project templates.
Enter a name for your project (for example "MsiDriverExploit") and choose a location to save it. Click "Create" to proceed.
In the project creation window, make sure ".cpp" is selected as the default file extension for C++ source files.
Click "Create" to create the project.
Exploit.cpp
Now that the project is set up, follow these steps to add a new source file called "Exploit.cpp" and include the necessary code:
In the Solution Explorer pane on the right-hand side, right-click on the project name and select "Add" -> "New Item".
In the "Add New Item" window, select "C++ File (.cpp)" from the available templates.
Enter "Exploit.cpp" as the file name and click "Add" to create the file.
Open the "Exploit.cpp" file and add the following code at the top of the file:
Now you can start writing your code within the main()
function in the "Exploit.cpp" file.
Symbolic Link
Define the Symbolic Link above the main()
function:
Add the following code to our main()
function. The intention of this code snippet is to obtain a handle to the driver using the specified Symbolic Link and perform fuzzing on the driver if the handle acquisition is successful:
The code checks if the CreateFileA
function call was successful by comparing the obtained handle (hDevice
) to (HANDLE)-0x1
. If the handle is equal to (HANDLE)-0x1
, it means that the function call failed.
If the handle is valid (not equal to -0x1
), it means the driver handle was successfully obtained, and the code proceeds to the fuzzing phase.
Fuzzing the Driver
Add the following code in the main()
function after the comment // we will start fuzzing here.
This code initialises an array ioCodes
with four IO Control Codes. It then enters a loop where it iterates over each code and performs fuzzing.
Within the fuzzing loop, it prints the code being fuzzed, and for each iteration, it sends a buffer of increasing sizes to the driver using DeviceIoControl
function. The buffer is allocated and filled with 'A' characters, and then sent to the driver.
Finally, the memory is freed before moving to the next iteration.
To set up the fuzzer in Visual Studio, follow these steps:
Build the fuzzer using the Release x64 build configuration.
Copy the generated fuzzing executable to the target machine.
Ensure that the debugger is connected to the target for monitoring.
Execute the fuzzing executable on the target machine.
Press the Enter key to send the fuzzing buffer repeatedly until the debugger breaks and captures any potential issues or vulnerabilities.
After the first iteration you should find that the OS crashes with just 250 bytes when we send an IOCTL with code 0x80102040
:
If we move back to our debugger we see that Windbg Preview has caught the Access Violation:
If we enter the k
command to view the call stack, we can clearly see that this has been corrupted by our buffer of 250 bytes:
If we look at the address MSI064+0x16b9
in IDA (use the G
key to bring up the "Jump to address" input), we can see that this is the ret
instruction in the MsIoDispatch
routine:
This looks like a classic stack-based buffer overflow. In the next section will look to exploit this vulnerability; turning a denial of service into privilege escalation.
Demo
Exercises
Test the other three I/O Control Codes by removing them one by one in the
ioCodes
array.
Last updated