Tech Stack
Backend — FastAPI
Python 3.12 met FastAPI. 18 router-modules voor alle pentest-categorieën.
Volledig async/await voor parallelle tool-uitvoering. Swagger UI beschikbaar
op /docs.
Frontend — Alpine.js + Tailwind
Single-page applicatie zonder build-stap. Alpine.js voor reactieve UI-logica, Tailwind CSS voor styling. WebSocket voor real-time updates zonder polling.
Database — SQLite
Zero-configuratie SQLite opgeslagen in ./results/pentest.db
(host-mount). Bevat scans, fasen, bevindingen, terminal-output en agents.
Direct opvraagbaar buiten Docker.
Graafanalyse — BloodHound CE
BloodHound Community Edition met Neo4j als graaf-backend. Automatische collector-integratie, optionele API-import en Path Finding voor aanvalspadvisualisatie.
Docker Services
| Service | Image | Poort | Doel |
|---|---|---|---|
pentest-api |
Lokale build (Python 3.12-slim) | 8000 | FastAPI backend + SPA frontend + alle pentest-tools |
pentest-n8n |
n8nio/n8n:latest | 5678 | Workflow-automatisering voor notificaties (optioneel) |
pentest-bloodhound |
specterops/bloodhound:latest | 8080 | BloodHound CE webinterface en API |
pentest-neo4j |
neo4j:4.4 | 7474 / 7687 | Graaf-database backend voor BloodHound |
pentest-postgres-bh |
postgres:15-alpine | intern | PostgreSQL backend voor BloodHound CE (geen publieke poort) |
Scan Engine
De scan-manager (scan_manager.py) orkestreert de 14 fasen en zorgt voor
conditionele uitvoering, parallelle checks per fase en fallback chains.
Uitvoeringspatroon
# Elke check volgt hetzelfde patroon:
@router.post("/check-name", response_model=CheckResult)
async def my_check(target: TargetBase) -> CheckResult:
result = CheckResult(check_id="category_check_name",
check_name="...", risk_level=RiskLevel.INFO)
try:
stdout, stderr, rc = await run(["nxc", "smb", target.dc_ip,
*target.nxc_auth_args()])
result.add_finding(title, detail, RiskLevel.HIGH, evidence=stdout)
except Exception as e:
result.set_error(str(e))
return result
Fase-orkestratie
- Parallel per fase — Alle checks binnen één fase draaien gelijktijdig via asyncio
- Sequentieel per fase — Fasen worden na elkaar uitgevoerd; laterale beweging pas na recon
- Conditioneel — Exchange-fase start alleen als Exchange tijdens recon gedetecteerd is; SCCM idem
- Fallback chains — Bevindingen triggeren vervolgaanvallen: bijv. SMB Signing uit + LLMNR aan → relay-setup
- Timeouts — Elk tool-commando heeft een configureerbare timeout (standaard 5 minuten)
Databaseschema
SQLite opgeslagen in ./results/pentest.db.
Tabel: scans | ||
|---|---|---|
| Kolom | Type | Beschrijving |
id | TEXT PK | UUID van de scan |
label | TEXT | Gebruikersvriendelijke naam |
dc_ip | TEXT | IP-adres van de domeincontroller |
domain | TEXT | Domeinnaam (bijv. corp.local) |
status | TEXT | running / completed / failed |
da_progress | REAL | DA Progress score (0.0–100.0) |
finding_counts | TEXT | JSON: teller per risiconiveau |
started_at | DATETIME | Starttijdstip van de scan |
completed_at | DATETIME | Eindtijdstip van de scan |
agent_id | TEXT FK | Gekoppelde agent (NULL = lokaal) |
Tabel: findings | ||
|---|---|---|
| Kolom | Type | Beschrijving |
id | INTEGER PK | Interne identificatie |
scan_id | TEXT FK | Koppeling aan scans.id |
check_id | TEXT | Unieke check-identifier (bijv. smb_signing_disabled) |
risk_level | TEXT | critical / high / medium / low / info |
title | TEXT | Korte samenvatting van de bevinding |
detail | TEXT | Uitgebreide beschrijving met context |
evidence | TEXT | Ruwe tool-output als bewijs |
created_at | DATETIME | Tijdstip van bevinding |
Tabel: phases | ||
|---|---|---|
| Kolom | Type | Beschrijving |
id | INTEGER PK | Interne identificatie |
scan_id | TEXT FK | Koppeling aan scans.id |
phase_name | TEXT | Naam van de fase (bijv. recon, users) |
status | TEXT | pending / running / completed / failed |
started_at | DATETIME | Starttijdstip van de fase |
completed_at | DATETIME | Eindtijdstip van de fase |
Tabel: terminal_lines | ||
|---|---|---|
| Kolom | Type | Beschrijving |
id | INTEGER PK | Interne identificatie |
scan_id | TEXT FK | Koppeling aan scans.id |
check_id | TEXT | Check waarbij de output hoort |
line | TEXT | Ruwe tool-outputregel |
created_at | DATETIME | Tijdstip van de outputregel |
Tabel: agents | ||
|---|---|---|
| Kolom | Type | Beschrijving |
id | TEXT PK | UUID van de agent |
name | TEXT | Gebruikersvriendelijke naam |
token | TEXT UNIQUE | Authenticatietoken (uniek per agent) |
online | BOOLEAN | True = actieve WebSocket-verbinding |
hostname | TEXT | Auto-discovery: hostnaam van de agent-machine |
os | TEXT | Auto-discovery: besturingssysteem |
ips | TEXT | Auto-discovery: IP-adressen (JSON-array) |
domain | TEXT | Auto-discovery: gedetecteerd domein |
dc | TEXT | Auto-discovery: gedetecteerde domeincontroller |
Agent Systeem
Remote agents voeren dezelfde pentest-API uit op een machine in het doelnetwerk. De portal stuurt check-verzoeken naar de agent via een WebSocket-tunnel.
Portal (uw server)
┌──────────────────────────────────┐
│ agent_manager.py │
│ ├── WebSocket server │◄──── WS verbinding (outbound vanuit agent)
│ ├── asyncio.Future routing │
│ └── request proxying │
└──────────────────────────────────┘
│ HTTP proxy via WS tunnel
▼
Agent (in doelnetwerk)
┌──────────────────────────────────┐
│ agent.py │
│ ├── Lokale HTTP server (8001) │
│ ├── WS → Portal verbinding │
│ └── Voert pentest-tools uit │──► AD domeincontroller
└──────────────────────────────────┘
- Agent en portal draaien dezelfde Docker-image — geen aparte agent-binary
- Alle check-verzoeken van de portal worden via het WebSocket-kanaal naar de agent doorgezet
- De agent heeft directe netwerktoegang tot AD; de portal heeft die niet nodig
- Token-rotatie: verwijder de agent in de portal en maak een nieuwe aan
Voorgeïnstalleerde Pentest Tools
| Tool | Versie | Gebruik |
|---|---|---|
| Impacket | 0.12.0 | DCSync, secretsdump, wmiexec, GetUserSPNs, PtH, MS-RPC |
| NetExec (nxc) | latest | SMB/LDAP/WinRM enumeratie en uitvoering (opvolger CrackMapExec) |
| certipy-ad | 4.8.2 | ADCS ESC1–ESC13 detectie en exploitatie |
| BloodHound Python | 1.7.0 | AD graafdata collectie |
| hashcat | latest | Hash cracking (Kerberoast, AS-REP, NTLMv2, Timeroast) |
| PetitPotam | latest | NTLM coercion via MS-EFSRPC |
| Coercer | latest | Multi-protocol NTLM coercion |
| pywhisker | latest | Shadow Credentials aanvallen |
| gMSADumper | latest | gMSA wachtwoord-extractie |
| targetedKerberoast | latest | ACL-gebaseerde Kerberoasting |
| kerbrute | latest | Kerberos gebruikersenumeratie en brute force |
| sprayhound | latest | Slim wachtwoord-spray (BloodHound-aware) |
| enum4linux-ng | latest | SMB/NetBIOS enumeratie |
| ldapdomaindump | latest | LDAP domeindata dump |
| dploot | latest | DPAPI en SCCM credential dumps |
| sccmhunter | latest | SCCM-detectie en exploitatie |
| adidnsdump | latest | DNS zone-enumeratie via LDAP |
| nmap | latest | Netwerkscan en poortenumeratie |
| searchsploit | latest | Exploit-database zoekfunctie (ExploitDB) |
Uitbreidbaar
Een nieuwe check toevoegen is eenvoudig en volgt een vast patroon:
Router-bestand aanmaken
Maak routers/my_check.py aan met een FastAPI router en een endpoint dat een CheckResult teruggeeft.
Router registreren
Voeg de router toe aan main.py via app.include_router().
Toevoegen aan PHASES
Voeg het endpoint toe aan de juiste fase in scan_manager.py's PHASES lijst.
DA-score toewijzen
Voeg het check_id toe aan het SCORES-dict in da_progress.py voor gewogen bijdrage aan de DA Progress-balk.