vendredi 30 mars 2012

[Tool/PoC] IOCTLbf - Scanning IOCTLs & Fuzzing Windows kernel drivers

IOCTLbf is just a small tool (Proof of Concept) that can be used to search vulnerabilities in Windows kernel drivers by performing two tasks:

Scanning for valid IOCTL codes supported by drivers,

Generation-based IOCTL fuzzing

An advantage of this tool is that it does not rely on captured IOCTLs. Therefore, it is able to detect valid IOCTL codes supported by drivers and that are not often, or even never, used by applications from user land. For example, it may be the case for:

IOCTLs called in very specific conditions (not easy to discover and/or to reproduce).

IOCTLs used for debugging purpose that are sometimes let in drivers.

Once scanning is done and valid IOCTLs have been found for a given driver, the user can choose one IOCTL in the list to begin the fuzzing process. Note that this tool only performs generation-based

fuzzing. Compared to mutation-based fuzzing (which consists in taking valid IOCTL buffers and adding anomalies), the code coverage is of course less important.

Note: for mutation-based IOCTL fuzzing, check out the great tool "IOCTL fuzzer" [1]. Basically, it hooks nt!NtDeviceIoControlFile in order to take control of all IOCTL requests throughout the system. A good example of use in the real world can be found at [2].

2. Reminder about IOCTLs

(Very good reference for this part: [3])

2.1. IOCTL codes

According to winioctl.h:

IOCTL's are defined by the following bit layout.
[Common |Device Type|Required Access|Custom|Function Code|Transfer Type]
31 30 16 15 14 13 12 2 1 0
Common - 1 bit. This is set for user-defined
device types.
Device Type - This is the type of device the IOCTL
belongs to. This can be user defined
(Common bit set). This must match the
device type of the device object.
Required Access - FILE_READ_DATA, FILE_WRITE_DATA, etc.
This is the required access for the
device.
Custom - 1 bit. This is set for user-defined
IOCTL's. This is used in the same
manner as "WM_USER".
Function Code - This is the function code that the
system or the user defined (custom
bit set)
Transfer Type - METHOD_IN_DIRECT, METHOD_OUT_DIRECT,
METHOD_NEITHER, METHOD_BUFFERED, This
the data transfer method to be used.

For a given device, only the fields "Function Code" and "Transfer Type"change for the different supported IOCTL codes.

The input buffer is passed in using "BUFFERED" implementation. The output buffer is passed in using a MDL (which permits Direct MemoryAccess). The difference between "IN" and "OUT" is that with "IN", you can use the output buffer to pass in data! The "OUT" is only used to return data.

Input & output buffers sizes may be different. The I/O manager does not provide any system buffers or MDLs. The IRP supplies the user-mode virtual addresses of the input and output buffer

3. How to use it ?

First of all, it is necessary to locate the target driver. A tool like "DriverView" [4] can be used in order to easily spot non-Microsoft drivers (third-party drivers).

Then, it is necessary to check what are the device(s) associated with the target driver. A good tool to do this is "DeviceTree" for example [5].

Check the security attributes (DACL) of the device(s). It should be available for limited users in order to make it interesting from an attacker point of view. Indeed, vulnerabilities in drivers may lead to Local Privilege Escalation on the system, or just Denial of Service when it is not exploitable.

Retrieve the symbolic link used by applications to communicate with one device of the target driver. All symbolic links can be listed with the Sysinternal's tool "WinObj" in the "GLOBAL??" section [6].

Finally, it is necessary to know at least one valid IOCTL code supported by the target driver. For example, it can be easily done by monitoring IRPs with a tool like "OSR's IrpTracker Utility" [7].

Make sure to apply a filter on "DEVICE_CONTROL" only and to select only the target driver.
Of course, it is also possible to retrieve valid IOCTL codes directly by reverse engineering the driver.

Once a valid IOCTL code is retrieved, "IOCTLbf" can be used. One of the following IOCTL codes scanning modes can be chosen:

Here is a simple example of use of the tool on the driver "aswSnx.sys" installed by "Avast! Antivirus":

5. Conclusion

"IOCTL Fuzzer" will, of course, do a better job on the IOCTLs it captures, but the fact is that it won't always capture all the IOCTLs that are supported by drivers... And this is where a tool like "IOCTLbf" can be useful even if it's far from being perfect and won't be able to detect all the supported IOCTLs every time (depends on the way the dispatch routine was written). It's still very improvable !

Anyway, it appears that dumb fuzzing is still working in 2012 :))
For example, here is an exploitable bug (Kernel pointer dereference) that has been found using "IOCTLbf" in the antivirus "Norman Security Suite 8". It leads to a possibility of local privilege escalation: