10.4 构建 Driver Fuzzer

第一步在完成 PyCommand:IOCTL-dump。

#ioctl_dump.py 
import pickle 
import driverlib
from immlib import * 
def main( args ):
    ioctl_list = [] device_list = []
    imm = Debugger() 
    driver = driverlib.Driver()
    # Grab the list of IOCTL codes and device names 
    ioctl_list = driver.getIOCTLCodes()
    if not len(ioctl_list):
        return "[*] ERROR! Couldn't find any IOCTL codes." 
    device_list = driver.getDeviceNames()
    if not len(device_list):
        return "[*] ERROR! Couldn't find any device names."
    # Now create a keyed dictionary and pickle it to a file 
    master_list = {}
    master_list["ioctl_list"] = ioctl_list 
    master_list["device_list"] = device_list
    filename = "%s.fuzz" % imm.getDebuggedName() 
    fd = open( filename, "wb" )
    pickle.dump( master_list, fd ) 
    fd.close()
    return "[*] SUCCESS! Saved IOCTL codes and device names to %s" % filename

这个 PyCommand 相当简单:检索 IOCTL 代码列表,检索设备名列表,将他们存到字 典中,然后保存到文件里。下次我们只要在 Immunity 的命令行中简单的输入 !ioctl_dump, pickle 文件就会保存到 Immunity 目录下。

万事俱备只欠 fuzzer。接下来就是 coding and coding,我们实现的这个 fuzzer 检测范围 限制在内存错误和缓冲区溢出,不过扩展也是很容易的。

#my_ioctl_fuzzer.py
import pickle 
import sys 
import random
from ctypes import * 
kernel32 = windll.kernel32
# Defines for Win32 API Calls 
GENERIC_READ = 0x80000000 
GENERIC_WRITE = 0x40000000 
OPEN_EXISTING = 0x3
# Open the pickle and retrieve the dictionary 
fd = open(sys.argv[1], "rb") 
master_list = pickle.load(fd)
ioctl_list = master_list["ioctl_list"] 
device_list = master_list["device_list"] 
fd.close()
# Now test that we can retrieve valid handles to all
# device names, any that don't pass we remove from our test cases 
valid_devices = [] 
for device_name in device_list:
    # Make sure the device is accessed properly
    device_file = u"\\\\.\\%s" % device_name.split("\\")[::-1][0] 
    print "[*] Testing for device: %s" % device_file
    driver_handle = kernel32.CreateFileW(device_file,GENERIC_READGENERIC_WRITE,0,None,OPEN_EXISTI NG,0,None)
    if driver_handle:
        print "[*] Success! %s is a valid device!" 
        if device_file not in valid_devices:
            valid_devices.append( device_file )
        kernel32.CloseHandle( driver_handle )
    else:
        print "[*] Failed! %s NOT a valid device."
if not len(valid_devices):
    print "[*] No valid devices found. Exiting..." 
    sys.exit(0)
# Now let's begin feeding the driver test cases until we can't be
# it anymore! CTRL-C to exit the loop and stop fuzzing 
while 1:
    # Open the log file first
    fd = open("my_ioctl_fuzzer.log","a")
    # Pick a random device name 
    current_device = valid_devices[random.randint(0, len(valid_devices)-1 )]
    fd.write("[*] Fuzzing: %s\n" % current_device)
    # Pick a random IOCTL code 
    current_ioctl = ioctl_list[random.randint(0, len(ioctl_list)-1)]
    fd.write("[*] With IOCTL: 0x%08x\n" % current_ioctl)
    # Choose a random length current_length = random.randint(0, 10000) fd.write("[*] Buffer length: %d\n" % current_length)
    # Let's test with a buffer of repeating As
    # Feel free to create your own test cases here 
    in_buffer = "A" * current_length
    # Give the IOCTL run an out_buffer
    out_buf = (c_char * current_length)() 
    bytes_returned = c_ulong(current_length)
    # Obtain a handle
    driver_handle = kernel32.CreateFileW(device_file, GENERIC_READ|GENERIC_WRITE,0,None,OPEN_EXISTING,0,None)
    fd.write("!!FUZZ!!\n")
    # Run the test case
    kernel32.DeviceIoControl( driver_handle, current_ioctl, in_buffer, current_length, byref(out_buf), current_length, byref(bytes_returned), None )
    fd.write( "[*] Test case finished. %d bytes returned.\n\n" % bytes_returned.value )
    # Close the handle and carry on! 
    kernel32.CloseHandle( driver_handle ) 
    fd.close()

先从 pickle 文件中取出包含 IOCTL 代码和设备名的字典。从列表中找出能够获得句柄 的设备名。如果无法获取,就从列表中移除。接着随机选取一个设备名和 IOCTL 代码,创 建一个随机长度的缓冲区。最后将 IOCTL 发送给驱动。

使用如下命令进行 fuzzing。

C:\>python.exe my_ioctl_fuzzer.py i2omgmt.sys.fuzz

如果 fuzzer crash 了机器,我们能够很准确的获得发送的 IOCTL 代码。接着就是调试驱 动了。表 10-7 显示的就是一个未知驱动的 fuzzing 过程。

[*] Fuzzing: \\.\unnamed
[*] With IOCTL: 0x84002019
[*] Buffer length: 3277
!!FUZZ!!
[*] Test case finished. 3277 bytes returned. 
[*] Fuzzing: \\.\unnamed
[*] With IOCTL: 0x84002020
[*] Buffer length: 2137
!!FUZZ!!
[*] Test case finished. 1 bytes returned. 
[*] Fuzzing: \\.\unnamed
[*] With IOCTL: 0x84002016
[*] Buffer length: 1097
!!FUZZ!!
[*] Test case finished. 1097 bytes returned. 
[*] Fuzzing: \\.\unnamed
[*] With IOCTL: 0x8400201c
[*] Buffer length: 9366
!!FUZZ!!

Listing 10-7: 一次成功的 fuzzing 记录

能够很清楚的看到,上一个 IOCTL,0x8400201c 引发了系统崩溃,因为这是最后一条 记录。目前为止我们的 fuzzer 很简单,但是很漂亮,可以通过不断的扩展功能,使它更强大。 其中一个可能的方法就是,将 InBufferLength 或者 OutBufferLength 参数设置成和实际传入的数据长度不一样。开始毁灭之路吧 ,哈哈!! destroy all drivers in your path!