
Using a C# Shellcode Runner and ConfuserEx to Bypass UAC
A red-team engagement writeup describing how to convert djhohenstein's CSharp SetThreadContext shellcode runner into a DLL, using the DllExport NuGet package and x64 Release build settings, so it can be loaded via rundll32.exe. The DLL and a modified chryzsh Aggressor script implementing the SilentCleanup scheduled-task DLL-hijack UAC bypass are then run through ConfuserEx to evade Windows Defender detection, enabling a Cobalt Strike beacon to escalate from a medium-integrity to high-integrity token without a UAC prompt.
What it is
A step-by-step build recipe for turning @djhohenstein's CSharpSetThreadContext shellcode runner (itself derived from @_xpn_'s work) into a 64-bit DLL, obfuscating it with ConfuserEx, and pairing it with @chryzsh's Aggressor script for the SilentCleanup scheduled-task DLL-hijack UAC bypass. The summary states the chain; this page is worth reading for the specific, easily-missed build settings that make the DLL actually export its entry point.
How it works
The runner is compiled as an EXE first and verified to beacon. The DllExport NuGet package then exports the Main function so it can be invoked via rundll32.exe. A [DllExport] attribute is added immediately before Main, and Main's argument is cleared. The runner auto-selects an executable to spawn into, which the author notes avoids detection by Get-InjectedThread.
Preconditions / What to build
The build only works for 64-bit with a precise configuration — the author reports the code silently fails otherwise:
- Configuration Manager → Active Solution platform = x64
- Build = Release
- Runner project Properties → Output type = Class Library
- Build tab → check Optimize code
What to do
Invoke the finished DLL with:
rundll32.exe .\Runner.dll,MainThe working DLL is at \CSharpSetThreadContext\Runner\bin\Release\x64\Runner.dll — critically, only the one in the \x64\ subdirectory contains the generated entry point; the sibling under \Release\ does not, despite being built simultaneously.
For the bypass itself, run both the runner DLL and @chryzsh's edited C# binary through ConfuserEx (defeats Defender per the author). Then edit the Aggressor script to:
- not generate/upload the built-in Cobalt Strike payload
- change the function invoked after
\temp.dllfromStarttoMain - rename
Runner.dlltotemp.dll(or adjust the script to the name you choose)
Detection note: in a medium-integrity Cobalt Strike context, get privs shows the reduced token and the username has no asterisk; the bypass elevates to high integrity without an asterisk-triggering UAC prompt.
Caveats
- Evasion is validated against Windows Defender only — the author repeatedly qualifies "at least Defender" and makes no claim about other AV/EDR.
- The x64-directory entry-point quirk is unexplained; the author explicitly asks readers for explanations, so treat the recipe as empirical, not understood.
- The SilentCleanup bypass (@enigma0x3 / @mattifestation) is old and many UACME techniques are remediated; the author picks this one as still-working but doesn't state against which Windows build.
Organisations named here
- SilentCleanup UAC bypass
- UACME
Technologies named here
- Windows
- Cobalt Strike
- CSharp SetThreadContext
- DllExport
- ConfuserEx
- rundll32.exe
- Aggressor script
- Get-InjectedThread
- Windows Defender
- .NET
- NuGet
- PowerShell
Techniques named here
- UAC Bypass via SilentCleanup scheduled task DLL hijack
- Shellcode injection via SetThreadContext
- DLL export via rundll32
- AV evasion via obfuscation
- Token integrity level abuse
Related in Offensive Security