Asustek Kernel Driver Asio3.sys 0-day vulnerability
Share
While researching the ASUS AsIO and AsIO2 driver families, I focused on their relevance and PA capabilities. Both are signed kernel drivers with privileged hardware-access capabilities, and applicable builds can still be loaded on Windows 11, so I added them to DOG (Data Only Gadgets tool).
The principal vulnerabilities in these two drivers include CVE-2019-25764, covering insufficient IOCTL access control in legacy ASIO and ASIO2 drivers, and CVE-2021-28685, which exposed physical-memory mapping and MSR access through AsIO2. Related ASUS AI Suite driver issues were assigned CVE-2022-4989 and CVE-2022-4990.
After adding these drivers to DOG, I encountered AsIO3, in the newer Asus Products, AsIO3.sys is a newer implementation of the ASUS hardware-access driver. AsIO3 replaced the direct access model with a different authorization handshake and an additional security layer based on ASUS Certificate Service and a per-process PID allowlist. This architecture became the starting point for analyzing how trusted ASUS processes receive access to the driver and whether that trust could be transferred to another process.
The following sections describe the analysis of this authorization mechanism and show how the handshake and additional security layer were bypassed in the newer AsIO3.sys driver by AsusTek.
Overview
A vulnerability in the ASUS Armoury Crate update workflow allows a local standard user to execute arbitrary code inside a privileged ASUS process, leading ultimately to arbitrary code execution inside the AsIO3.sys driver.

This is the chain base line:
1. ROG Live Service launchs the managed FileOperator.exe utility with a privileged access token while supplying an environment block derived from the interactive user.
2. The Common Language Runtime processed user-controlled .NET profiler variables from this environment and loaded a profiler DLL before executing FileOperator.
3. Code running inside the ASUS-signed process could communicate with ASUS Certificate Service through the \\.\pipe\asuscert named pipe. The service accepted the trusted ASUS process and authorized its PID for access to the ASIO3 kernel driver.
The authorized process could then submit IOCTL 0xA040A490 with a second arbitrary PID. The driver inserted this value into its global PID allowlist, granting the selected process access to privileged ASIO3 operations.
This chain produced local privilege escalation from a standard Windows user to a privileged execution context.
Affected Components
The analyzed attack surface included the following ASUS components:
C:\Program Files\ASUS\ROG Live Service\ROGLiveService.exe
C:\Program Files\ASUS\ROG Live Service\FileOperator.exe
C:\Program Files\ASUS\ROG Live Service\Plugins\UpdatePlugin.dll
C:\Program Files\ASUS\Armoury Crate Service\SystemDevicePlugin\Driver\AsIO\AsusCertService.exe
C:\Program Files\ASUS\Armoury Crate Service\SystemDevicePlugin\Driver\AsIO\AsIO3_64.sys
FileOperator.exe is a .NET Framework 4 application with the following execution manifest:
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
The "asInvoker" execution level causes the application to use the token supplied by its launcher. ROG Live Service controlled the resulting integrity level through its process-creation logic.

FileOperator.exe loaded as a managed .NET Framework application.

FileOperator.exe execution manifest specifying asInvoker.
What we know so far?
During the initial analysis of AsIO3.sys a function restricting the access was identified, basically tracing back the GLE=87 (Parameter Incorrect) or GLE=5 (Access Denied) to a PID allowlisting mechanism, so that was the kick-off. ASUS Certificate Service acted as an authorization broker: it accepts a submitted PID, verifies that the process is an approved and signed ASUS binary, and then registered that PID with the driver.
This design made execution inside an approved process the next research target. So the next most logical step was to start reversing FileOperator.exe and other trusted ASUS binaries for DLL-loading opportunities. Since FileOperator.exe uses the .NET Framework, CLR profiler injection provided a direct path for loading user-controlled native code into the approved process.
-
Privileged Process Launch:
ROG Live Service delegates user-session process creation to UpdatePlugin.dll. -
The service loads the plugin and resolves the following exported function:
RunSessionProcess_User
During the operation, the service provides the FileOperator.exe path, the argument, a timeout, and the requested execution mode.
The process-launch implementation obtains the active interactive session and calls WTSQueryUserToken to retrieve the user token:
WTSQueryUserToken(SessionId, &phToken);

For privileged execution, the implementation retrieves an elevated linked token and creates a primary token:

GetTokenInformation(
phToken,
TokenLinkedToken,
&TokenInformation,
sizeof(TokenInformation),
&ReturnLength
);
DuplicateTokenEx(
TokenInformation,
MAXIMUM_ALLOWED,
nullptr,
SecurityImpersonation,
TokenPrimary,
&TokenHandle
);
WTSQueryUserToken: Obtains the primary access token of the logged-on user specified by the session ID. Microsoft MSDN reference
GetTokenInformation: The GetTokenInformation function retrieves a specified type of information about an access token.
DuplicateTokenEx: The DuplicateTokenEx function creates a new access token that duplicates an existing token.
Bonustrack: The code also contains a service-token execution path. This path duplicates the token of the service process and assigns it to the active user session, so that is also an interesting path to look at.
But let's not get distracted and go back to our user token via User-Controlled Environment Block The central trust-boundary issue occurs during environment construction.
UpdatePlugin.dll creates an environment block from the interactive user token:
CreateEnvironmentBlock(
&Environment,
phToken,
FALSE
);

This environment contains variables associated with the user profile, including values configured under: HKCU\Environment
The same routine launches the child with the privileged primary token while passing the user-derived environment:
CreateProcessAsUserW(
TokenHandle,
lpApplicationName,
lpCommandLine,
nullptr,
nullptr,
FALSE,
CREATE_UNICODE_ENVIRONMENT,
Environment,
nullptr,
&StartupInfo,
&ProcessInformation
);

The resulting process combines two security contexts:
-
Execution token: Privileged token selected by ROG Live Service
- Environment: Interactive standard-user environment
The user-derived environment reaches the Common Language Runtime, or CLR for short, inside the privileged process.
Abusing of a DLL injection to get us to Kernel Space:
The .NET Framework supports native profiler components configured through environment variables and a profiler CLSID. A standard user could configure the profiler entirely through the current-user registry hive, the folowing is an example using InjectDLLPoc.dll that gets loaded from c:\Windows\Tasks\ folder:

We choose it to be stored in C:\Windows\Tasks, as it's a location available to the standard-user context. When FileOperator.exe initializes the CLR, the runtime reads the inherited profiler configuration. Profiler initialization takes place before the managed entry point or for better context it runs before: FileOperator.Program.Main()
In simple words, the injected DLL therefore receives execution before the main application logic. Think of this technique, as something similar as LD_Preload in Unix/Linux.
In this screenshot, the .DLL as been injected and as a log file a file has been created using the new token, duplicated from the service itself, that is how this log is written with Integrity level: High (RID=0x00003000)
The Handshake with the Driver: ASUS Certificate Service Interaction
The profiler DLL executed within the identity of the signed ASUS host process.
because of this it can sucessfully connect to the following named pipe:
\\.\pipe\asuscert
The DLL then send the current FileOperator.exe PID as a four-byte value. ASUS Certificate Service evaluated the submitted process and returned a UTF-16 response:
OK!
The trust decision focused on the identity of the executable associated with the PID. At this point, the signed process already contained code supplied by the standard user through CLR profiler initialization.
The certificate service subsequently authorized the PID for access to the ASIO3 driver.
ASIO3 Device Authorization
The ASIO3 driver processes device control requests through its IRP_MJ_DEVICE_CONTROL dispatch routine. The dispatch code compares the requested control code with 0xA040A490:
Screenshot of the ASIO3 IOCTL dispatch for control code 0xA040A490, that leads to the vulnerable function (renamed for clarity to: AllowPiDFromIOCTL)

The handler checks for a minimum four-byte input buffer, retrieves the buffered input, searches for an empty allowlist entry, and stores the supplied PID:
if (InputBufferLength < sizeof(uint32_t))
return STATUS_INFO_LENGTH_MISMATCH;
InputPid = *(uint32_t *)Irp->AssociatedIrp.SystemBuffer;
for (index = 0; AllowedPidList[index] != 0; index++)
{
if (index >= MaximumEntries)
return STATUS_UNSUCCESSFUL;
}
AllowedPidList[index] = InputPid;
return STATUS_SUCCESS;
The nominated PID originates directly from the IOCTL input buffer. An authorized client can therefore delegate driver access to a second process selected by the caller, and this is exactly what we are going to do next.
Privileged Driver Access
After receiving authorization, the injected code opened the driver device:
CreateFileW(
L"\\\\.\\Asusgio3",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr
);
It then submitted a target PID through IOCTL 0xA040A490:
DeviceIoControl(
hAsus,
0xA040A490,
&targetPid,
sizeof(targetPid),
&targetPid,
sizeof(targetPid),
&bytesReturned,
nullptr
);
The result on the log was:
Pipe reply: OK!
ASUSGIO3_ADD_PID succeeded

The target process subsequently opened \\.\Asusgio3 and gained access to the driver’s privileged IOCTL surface.
ASIO3 exposes low-level hardware operations involving I/O ports, model-specific registers, PCI configuration space, physical memory, and memory mapping. Access to these operations crosses the user-to-kernel security boundary.
The attack chain using DOG:
First, DOG is launched with the ASIO3 backend selected. For simplicity, DOG writes its process ID to C:\Windows\Temp\pid.txt and pauses while waiting for authorization.
Armoury Crate is then opened, and an action is triggered that causes ROG Live Service to launch FileOperator.exe through UpdatePlugin.dll. The CLR loads the profiler DLL inside FileOperator.exe. The PoC log confirms High-integrity execution, successful communication with \\.\pipe\asuscert, and access to the \\.\Asusgio3 device.

The injected profiler reads the DOG PID from pid.txt and submits it through IOCTL 0xA040A490. This operation adds the DOG process to the ASIO3 PID allowlist.

After authorization is confirmed in the log, we can resume the execution of DOG. The process can then open the ASIO3 device under its own PID and issue any of the privileged IOCTL operations exposed by the driver.

And if you were wondering if this works with VBS/HVCI enabled:

The complete chain provided the following capabilities:
1. A standard-user controls the .NET profiler configuration through HKCU.
2. Native DLL execution inside a privileged ASUS-signed process.
3. Access to ASUS Certificate Service under the identity of the trusted host.
4. Authorization to open the ASIO3 kernel device.
5. Delegation of ASIO3 access to an arbitrary PID.
6. Access to privileged kernel-driver operations from the selected process.
For a standard user, the chain represents local privilege escalation. For a user operating with a filtered administrator token, the same primitive can provide elevation without a UAC consent prompt.
What is the main vulnerability here?
The vulnerability resulted from several trust decisions operating across component boundaries. ROG Live Service selected a privileged token for FileOperator.exe while constructing its environment from the interactive user token. The environment carried user-controlled CLR profiler configuration into the privileged process.
ASUS Certificate Service trusted the signed executable identity associated with the submitted PID. Profiler initialization had already introduced user-controlled code into that signed process. Then, ASIO3 driver allowed an authorized process to submit a second PID through IOCTL 0xA040A490. The driver stored the caller-supplied value directly in its authorization list.
Each stage extended trust from one component to the next, producing a complete user-to-kernel escalation path.
Disclosure Timeline
24 August 2026
ASUS PSIRT acknowledged the report and forwarded it to the software team.
4 September 2026
ASUS provided an updated build and requested verification.
9 September 2026
The updated components were tested against the profiler injection and ASIO3 access chain. So we are glad to report that this have been now fixed by AsusTek.

Conclusion and take aways:
The vulnerability demonstrates the security impact of combining a privileged token with an environment derived from a lower-privileged user.
CLR profiler initialization occurs before managed application code. A signed and trusted executable can therefore host user-controlled native code when its startup environment crosses a privilege boundary.
ASUS Certificate Service then extended trust from the signed host to the ASIO3 driver. The driver’s arbitrary PID allowlisting operation extended that trust to a second process.
The resulting chain connected user-controlled registry configuration, privileged process creation, trusted service communication, and kernel-driver authorization into a local privilege-escalation path.
And most important it was fun to do, next week we will publish another research so stay tunned!