Skip to content

Back to the catalog

Using a C# Shellcode Runner and ConfuserEx to Bypass UAC
hausec.comwriteup

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.

TL;DR Step-by-step guide to building a ConfuserEx-obfuscated C# shellcode-runner DLL to AV-evasively execute the SilentCleanup UAC bypass with Cobalt Strike.
Document

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,Main

The 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.dll from Start to Main
  • rename Runner.dll to temp.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