-
-
Save klezVirus/a0e401ca409489f53cd4aba95716c4bb to your computer and use it in GitHub Desktop.
A crude IOCTL fuzzer for windows driver testing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import random | |
import sys | |
import struct | |
import io | |
from ctypes import windll, POINTER, byref | |
from ctypes.wintypes import LPVOID, DWORD, LPCSTR, LPSTR, BOOL, HANDLE | |
from enum import Enum | |
''' | |
#define IOCTL_HEVD_TYPE_CONFUSION 0x222023 | |
#define IOCTL_HEVD_INTEGER_OVERFLOW 0x222027 | |
#define IOCTL_HEVD_NULL_POINTER 0x22202b | |
#define IOCTL_HEVD_UNITIALIZED_STACK 0x22202f | |
#define IOCTL_HEVD_UNITIALIZED_HEAP 0x222033 | |
#define IOCTL_HEVD_DOUBLE_FETCH 0x222037 // done win8 | |
#define IOCTL_HEVD_STACK_OVERFLOW 0x222003 // done win8 | |
#define IOCTL_HEVD_STACK_OVERFLOW_GS 0x221fff | |
#define IOCTL_HEVD_ARBITRARY_OVERWRITE 0x221ffb // done win8 | |
#define IOCTL_HEVD_ALLOCATE_UAF 0x221ff7 | |
#define IOCTL_HEVD_FREE_UAF 0x221ff3 | |
#define IOCTL_HEVD_USE_UAF 0x221fef | |
''' | |
class HEVD_IOCTLS(Enum): | |
IOCTL_HEVD_STACK_OVERFLOW = 0x222003 | |
class HEVD_NON_FUZZ_IOCTLS(Enum): | |
IOCTL_HEVD_STACK_OVERFLOW_GS = 0x221fff | |
IOCTL_HEVD_TYPE_CONFUSION = 0x222023 | |
IOCTL_HEVD_INTEGER_OVERFLOW = 0x222027 | |
IOCTL_HEVD_NULL_POINTER = 0x22202b | |
IOCTL_HEVD_UNITIALIZED_STACK = 0x22202f | |
IOCTL_HEVD_UNITIALIZED_HEAP = 0x222033 | |
IOCTL_HEVD_DOUBLE_FETCH = 0x222037 | |
IOCTL_HEVD_ARBITRARY_OVERWRITE = 0x221ffb | |
IOCTL_HEVD_ALLOCATE_UAF = 0x221ff7 | |
IOCTL_HEVD_FREE_UAF = 0x221ff3 | |
IOCTL_HEVD_USE_UAF = 0x221fef | |
class AMP_IOCTLS(Enum): | |
IOCTL_AMP_ = 0x226003 | |
HEVD_DRIVER_PATH = "\\\\.\\HackSysExtremeVulnerableDriver" | |
AMP_DRIVER_PATH = "\\\\.\\AMP" | |
class Driver: | |
def __init__(self, name, device, ioctls): | |
self.name = name | |
self.device = device | |
self.ioctls = ioctls | |
DeviceIoControl = windll.kernel32.DeviceIoControl | |
CreateFileA = windll.kernel32.CreateFileA | |
CloseHandle = windll.kernel32.CloseHandle | |
DeviceIoControl.restype = BOOL | |
DeviceIoControl.argtypes = [HANDLE, DWORD, LPVOID, DWORD, LPVOID, DWORD, | |
POINTER(DWORD), LPVOID] | |
CreateFileA.restype = HANDLE | |
CreateFileA.argtypes = [LPCSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE] | |
CloseHandle.restype = BOOL | |
CloseHandle.argtypes = [HANDLE] | |
GENERIC_READ = (1 << 30) | |
GENERIC_WRITE = (1 << 31) | |
FILE_SHARE_READ = 1 | |
FILE_SHARE_WRITE = 2 | |
OPEN_EXISTING = 3 | |
FILE_ATTRIBUTE_NORMAL = 0x80 | |
def opendevice(dev): | |
# open the device | |
hdev = CreateFileA(dev, GENERIC_READ | GENERIC_WRITE, | |
FILE_SHARE_READ | FILE_SHARE_WRITE, None, OPEN_EXISTING, | |
FILE_ATTRIBUTE_NORMAL, None) | |
return hdev | |
def send_ioctl(hdev, ioctl, input, outbuf_len): | |
# send IOCTLs to the driver | |
outbuf = LPSTR(b"\x00" * outbuf_len) if outbuf_len else None | |
outret = DWORD() | |
DeviceIoControl(hdev, ioctl, input, len(input), outbuf, outbuf_len, | |
byref(outret), None) | |
return outret.value, (outbuf.value if outbuf else b'') | |
def runner(driver, bufferlen, event=None): | |
import os | |
pid = os.getpid() | |
dev = opendevice(driver.device) | |
while 1: | |
length = random.randint(0, bufferlen) | |
# sys.stdout = io.StringIO() # suppress output | |
num = random.choice(driver.ioctls) | |
outlen = length | |
if driver.name == "HEVD": | |
inbuf = "A" * length | |
print(f"[*] Fuzzing {num} with buffer of {length} bytes") | |
elif driver.name == "AMP": | |
first = struct.pack(">Q", random.randint(0, 9)) | |
second = struct.pack(">Q", random.randint(0, sys.maxsize)) | |
third = struct.pack(">Q", random.randint(0, sys.maxsize)) | |
inbuf = first + second + third | |
print(f"[*] Fuzzing {num} with buffer {inbuf}") | |
try: | |
send_ioctl(dev, num, inbuf, outlen) | |
except: | |
pass | |
if __name__ == '__main__': | |
from multiprocessing import Event, cpu_count, Process | |
import argparse | |
import time | |
def _ioctls(x): | |
try: | |
return [int(y, 0) for y in x.split(",")] | |
except ValueError: | |
pass | |
return open(x, "r").readlines() | |
ap = argparse.ArgumentParser() | |
ap.add_argument("-D", "--driver", type=str, choices=["HEVD", "AMP"], required=False, | |
help="Driver to fuzz (predefined information)") | |
ap.add_argument("-i", "--ioctls", type=_ioctls, | |
help=("List of IOCTL codes to fuzz. A comma separated " | |
"list of IOCTLs")) | |
ap.add_argument("-d", "--device", required=False, type=lambda x: x.encode(), | |
help="The device to fuzz") | |
ap.add_argument("-l", "--bufferlen", type=int, default=2500, | |
help=("The length range of input and output buffers " | |
"passed on each call to DeviceIoControl")) | |
args = ap.parse_args() | |
if not args.driver and not (args.device or args.ioctls): | |
print("[-] Need a Driver or Device + IOCTLS to fuzz") | |
sys.exit(1) | |
driver = None | |
if args.driver: | |
if args.driver == "HEVD": | |
driver = Driver(args.driver, HEVD_DRIVER_PATH.encode(), [e.value for e in HEVD_IOCTLS]) | |
elif args.driver == "AMP": | |
driver = Driver(args.driver, AMP_DRIVER_PATH.encode(), [e.value for e in AMP_IOCTLS]) | |
else: | |
name = args.device.split("\\")[-1] | |
driver = Driver(name, args.device, args.ioctls) | |
processes = cpu_count() # try to run one processes on each available CPU | |
stopevent = Event() | |
procs = [] | |
for _ in range(processes): | |
p = Process(target=runner, args=(driver, args.bufferlen, stopevent)) | |
p.daemon = True | |
procs.append(p) | |
p.start() | |
try: | |
while True: | |
time.sleep(1) | |
except KeyboardInterrupt: | |
stopevent.set() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment