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!