Skip to content

Instantly share code, notes, and snippets.

@klezVirus
Forked from uf0o/crude_ioctl_fuzzer.py
Last active May 31, 2021 08:16
Show Gist options
  • Save klezVirus/a0e401ca409489f53cd4aba95716c4bb to your computer and use it in GitHub Desktop.
Save klezVirus/a0e401ca409489f53cd4aba95716c4bb to your computer and use it in GitHub Desktop.
A crude IOCTL fuzzer for windows driver testing
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