Ein Phishing-Response-Playbook in 12 Schritten: End-to-End in Krasper Suite
Konkretes, schrittweises Walkthrough eines echten Phishing-Response-Playbooks, verdrahtet end-to-end in Krasper Suite, vom Alert-Ingress bis zum Audit-Record. Zwölf Nodes, drei Conditional-Branches, ein Dry-Run-Gate bevor irgendetwas Produktion anfasst.

TL;DR: Der vorherige Beitrag dieser Serie argumentierte für die vier Design-Entscheidungen, die SOAR-Playbooks am Leben halten: reaktiver Datenfluss, typisierter Node-Vertrag, First-Class-Branching, Dry-Run. Dieser zeigt, wie diese Entscheidungen in einem funktionierenden Playbook aussehen. Zwölf Nodes, eine nach der anderen durchgegangen, jeweils mit Daten-Shape, Failure-Mode und Design-Absicht. Am Ende handlet das Playbook den Long-Tail echter Phishing-Variation, an dem scripted Automatisierungen kollabieren.
Phishing bleibt in den meisten Organisationen der höchstvolumige Initial-Access-Vektor. Es ist auch die Incident-Kategorie, in der die Kosten schlechter Automatisierung am sichtbarsten sind: ein einzelnes kaputtes Playbook kann entweder echte Bedrohungen verpassen oder, schlimmer, destruktive Aktion auf einem False-Positive ausführen (legitime CEO-Mail quarantänen, Sales-Laptop mitten im Deal-Call isolieren). Es gut zu machen ist vor allem eine Frage disziplinierter Verdrahtung; an der Logik selbst scheitert es selten.
Dieser Walkthrough folgt einem Playbook end-to-end, so wie es im Designer von Krasper Suite lebt. Die konkrete Form lässt sich an jede Umgebung anpassen; worum es hier geht, ist die Disziplin dahinter.
Inhalt
- Ingress: Alerts vom Mail-Security-Signal
- Header-Parsing und Message-Dekomposition
- IOC-Extraktion: URLs, Domains, Hashes, Sender
- Threat-Intelligence-Enrichment
- Sender-Reputation und Domain-Age-Signale
- Empfänger-Resolution und Asset-Mapping
- Phishing-Confidence-Klassifizierung (der zentrale Switch)
- High-Confidence-Branch: Quarantäne über Mailboxen
- URL-Click-Detection auf betroffenen Endpoints
- Endpoint-Isolation (mit explizitem Failure-Handling)
- Multi-Channel-Notification mit rollenbewusstem Routing
- Ticket-Erstellung, Audit-Trail und Post-Mortem-Record
Plus: Dry-Run-Disziplin, was die Screenshots tatsächlich zeigen, und der eine Branch, den die meisten Teams zu verdrahten vergessen.
Schritt 1: Ingress
Die erste Node empfängt Alerts vom Mail-Security-Signal. Im Designer ist das ein einzelner Input-Port mit deklariertem Schema: das Alert-Objekt, so wie es vom Mail-Gateway-Adapter auf den gemeinsamen Event-Bus publisht wird.
┌──────────────────────────────────┐ │ Alert ingress │ │ │ │ in: (event bus subscription) │ │ out: alert: MailAlert │ └──────────────────────────────────┘
[Screenshot: Alert-Ingress-Node ausgewählt auf dem Playbook-Canvas, Seitenpanel zeigt das MailAlert-Schema]
Wichtig: Der Alert ist bereits typisiert. Es gibt keinen "parse dieses JSON"-Schritt am Anfang des Playbooks. Diese Verantwortung gehört dem Mail-Gateway-Adapter, der die Form garantiert, bevor der Alert je den Bus erreicht. Playbook-Autor:innen schreiben gegen einen Vertrag, nicht gegen Rohpayloads.
Ist der Upstream-Adapter nicht verfügbar, erreicht kein Alert diese Node; keine Execution startet; nichts schlägt still fehl. Das ist im Health-View der Plattform zu entdecken, nicht im Playbook selbst.
Schritt 2: Header-Parsing und Message-Dekomposition
Die erste Transformation-Node zerlegt das Mail-Objekt in die Felder, die Downstream-Nodes brauchen: headers, subject, body_text, body_html, attachments[]. Reine Funktion: keine externen Calls, keine Seiteneffekte, kein Failure-Mode außer fehlerhaftem Input (der zum Error-Port routet und einen Analyst-Alert auslöst).
┌──────────────────────────────────┐ │ Decompose message │ │ │ │ in: alert: MailAlert │ │ out: message: ParsedMail │ │ err: ParseError → analyst │ └──────────────────────────────────┘
Der Grund, das in einer eigenen Node statt inline in Schritt 3 zu haben, ist Testbarkeit. Der Parser hat seine eigene Test-Suite. Ändert das Mail-Gateway upstream einen Feldnamen, bricht genau eine Node, mit genau einem Fix, isoliert reviewbar.
Schritt 3: IOC-Extraktion
Diese Node extrahiert Indicators of Compromise aus der geparsten Message: URLs im Body, Domains aus diesen URLs abgeleitet, Hashes für Anhänge, die Sender-Adresse, die Reply-To-Adresse falls abweichend.
┌──────────────────────────────────┐
│ Extract IOCs │
│ │
│ in: message: ParsedMail │
│ out: iocs: IOCSet { │
│ urls[], domains[], │
│ hashes[], senders[] │
│ } │
└──────────────────────────────────┘
[Screenshot: extrahierte IOC-Liste im Dry-Run-Inspector, zeigt die urls/domains/hashes-Arrays so wie sie zur Runtime aufgelöst wurden]
Zwei Design-Anmerkungen, die bei Scale zählen.
Erstens: URL-Extraktion normalisiert vor der Deduplizierung. http://x.com, https://x.com/ und https://X.COM kollabieren zu einer kanonischen URL. Ohne das duplizieren Downstream-Lookups Kosten, und der Classifier zählt denselben Indikator doppelt.
Zweitens: Attachment-Hashing nutzt nur SHA-256. Ältere Hash-Algorithmen erzeugen Noise ohne Wert; die Threat-Intelligence-APIs, die der nächste Schritt aufruft, akzeptieren SHA-256 universell. Eine Hash-Familie, ein Code-Pfad, eine Drift-Quelle weniger.
Schritt 4: Threat-Intelligence-Enrichment
Die nächste Node ist die erste, die externe Infrastruktur ruft. Das IOC-Set fan-outet zu einem Threat-Intelligence-Adapter (typischerweise ein Dienst wie VirusTotal oder ein Äquivalent), der pro Indikator ein Urteil zurückgibt: known-malicious, known-clean, unknown.
┌──────────────────────────────────┐ │ Threat-intel lookup │ │ │ │ in: iocs: IOCSet │ │ out: verdicts: VerdictMap │ │ err: TIError → degraded path │ │ timeout: 8s │ └──────────────────────────────────┘
Drei Plattform-Garantien greifen hier automatisch: der Call trägt einen Idempotency-Key (damit ein Replay keine API-Quota dupliziert), das Timeout ist beschränkt (damit ein langsamer Upstream nicht die gesamte Execution stallt), und der Error-Port ist Pflicht (damit ein Outage beim Threat-Intel-Provider das Playbook nicht stillschweigend auf "jeder IOC ist unknown" runterstuft).
Der Error-Port routet zu einem Degraded-Path, nicht zu einem Hard-Abort. Ist die Threat-Intel-API nicht verfügbar, läuft das Playbook trotzdem. Es klassifiziert mit den Signalen, die es hat (Sender-Reputation, Domain-Age, Mail-Header), und flaggt das Verdict im Audit-Record als intel_unavailable. Die:der Analyst:in, die:der das Ticket öffnet, weiß, dass die Confidence niedriger ist, weil das Enrichment unvollständig war.
Eine weitere Nuance, die zu verdrahten lohnt: Ergebnisse dieser Node werden für ein beschränktes Fenster gecacht (typischerweise wenige Stunden), gekeyed am Indikator selbst, nicht am Alert. Eine Phishing-Kampagne, die hundert nahezu identische Messages am selben Morgen landet, soll keine hundert kostenpflichtigen Lookups für dieselbe URL produzieren. Der Cache-Layer ist Teil des Adapters, nicht des Playbooks (Autor:innen müssen also nicht darüber nachdenken), aber zu wissen, dass er existiert, ist der Unterschied zwischen einer Automatisierung, die ökonomisch skaliert, und einer, die in der zweiten Woche still durch die Threat-Intel-API-Quota brennt.
Schritt 5: Sender-Reputation und Domain-Age
Parallel zum Threat-Intel-Call laufen zwei günstigere Enrichments: die Reputation der Sender-Domain (von einem separaten Adapter) und das Registrierungsalter der Sender-Domain (via WHOIS-Style-Lookup). Die Runtime parallelisiert das, weil beide weder voneinander noch vom Threat-Intel-Ergebnis abhängen.
┌────────────────────────┐ ┌────────────────────────┐ │ Sender reputation │ │ Domain age │ │ │ │ │ │ in: iocs.senders │ │ in: iocs.domains │ │ out: rep_score │ │ out: age_days │ └────────────────────────┘ └────────────────────────┘
Domain-Age zählt, weil frisch registrierte Domains stark mit Phishing-Kampagnen korrelieren. Eine Domain, die vor drei Tagen registriert wurde und Ihren CFO bittet, Geld zu überweisen, verdient mehr Misstrauen als eine, die vor fünfzehn Jahren registriert wurde. Das Age-Signal allein reicht nicht; kombiniert mit den anderen Signalen hebt es die Confidence deutlich.
Schritt 6: Empfänger-Resolution und Asset-Mapping
Der Mail-Alert listet Empfänger:innen per E-Mail-Adresse. Containment-Entscheidungen brauchen mehr: welche:r User:in, welche Rolle, welcher Endpoint, wo in der Org. Diese Node löst jede:n Empfänger:in gegen das gemeinsame kanonische Asset-Modell auf.
┌──────────────────────────────────┐
│ Resolve recipients │
│ │
│ in: message.recipients[] │
│ out: targets: ResolvedTarget[] { │
│ user_id, role, │
│ endpoints[], │
│ privileged: bool │
│ } │
└──────────────────────────────────┘
[Screenshot: resolved Targets im Dry-Run-Inspector, jede:r Empfänger:in gemappt auf user_id, role, endpoints, privileged-Flag]
Das privileged-Flag ist abgeleitet: true, wenn die:der User:in in einer Gruppe ist, die erhöhtes Containment rechtfertigt (Finance-Approver, Executive-Assistants, IT-Administrator:innen). Das Playbook behandelt privilegierte User:innen in Schritt 11 anders, also muss diese Resolution vor dem zentralen Switch passieren.
Kann ein:e Empfänger:in nicht aufgelöst werden (externe:r Empfänger:in, frisch provisionierte:r User:in noch nicht im Asset-Modell), wird das Target mit resolved: false ins Array aufgenommen statt verworfen. Downstream-Nodes können auf den Resolution-Status filtern; nichts verschwindet still.
Schritt 7: Phishing-Confidence-Klassifizierung
Das ist der zentrale Entscheidungspunkt. Eine Classifier-Node nimmt jedes Enrichment-Signal und emittiert ein confidence: high | medium | low-Urteil plus die strukturierte Begründung dahinter.
┌──────────────────────────────────────────────────────┐
│ Classify confidence │
│ │
│ in: verdicts, rep_score, age_days, targets, message │
│ out: classification: { │
│ confidence: "high" | "medium" | "low", │
│ signals: [...], │
│ intel_complete: bool │
│ } │
└──────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────┐
│ switch confidence │
│ case high ──▶ Step 8 │
│ case medium ──▶ analyst │
│ case low ──▶ trend tag │
│ default ──▶ analyst │
└────────────────────────────┘
Der Classifier selbst ist absichtlich erklärbar. Er ist ein gewichtetes Regelset, und die Signale, die zu jedem Urteil beigetragen haben, werden im Output festgehalten, sodass sich nichts hinter einem Blackbox-ML-Score verbirgt. Öffnet die:der Analyst:in das resultierende Ticket, sieht sie:er warum das Playbook den Alert so klassifiziert hat. Diese Auditierbarkeit zählt genauso viel wie Genauigkeit: ein High-Confidence-Verdict, das die:der Analyst:in nicht auf seine Inputs zurückverfolgen kann, erodiert das Vertrauen in die gesamte Automatisierung.
Konkret: die Signal-Gewichte sind versions-kontrolliert zusammen mit dem Playbook selbst. Eine Änderung an "frisch registrierte Domains als höheres Risiko behandeln" ist ein reviewbarer Diff, keine Konfigurations-Drift in einer Produktions-Datenbank. Fragt das Audit "warum wurde dieser Alert am 14. März quarantänet", ist die Antwort ein konkreter Commit-Hash mit den Classifier-Gewichten zum Execution-Zeitpunkt, festgehalten im Audit-Record.
Der default-Branch ist verdrahtet, obwohl jeder Confidence-Wert einer der drei Cases sein sollte. Das ist der häufigste "haben wir vergessen"-Branch, und der, der vor zukünftigen Classifier-Änderungen schützt, die einen vierten Case einführen, für den niemand den Switch updated. Entwickelt sich der Classifier in sechs Monaten dahin, confidence: "requires-human-review" zu emittieren, routet das Playbook standardmäßig zur:zum Analyst:in, statt fail-closed auf eine Weise zu greifen, die den Alert still verschluckt.
Schritt 8: Quarantäne über Mailboxen (High-Confidence-Branch)
Im High-Confidence-Branch ist die erste Aktion, die Message in jeder erreichten Mailbox zu quarantänen. Die Quarantäne-Node ruft den Mail-Plattform-Adapter, idempotent auf dem (campaign_id, message_id)-Paar, damit Re-Execution keine Effekte dupliziert.
┌──────────────────────────────────┐ │ Quarantine mail │ │ │ │ in: message.id, targets │ │ out: quarantine_result │ │ err: → escalate to analyst │ │ dry-run: synthetic success │ └──────────────────────────────────┘
[Screenshot: Dry-Run-Trace zeigt die Quarantäne-Node mit synthetischem Success und dem festgehaltenen "would have quarantined N messages"-Intent]
Das ist die erste destruktive Node im Playbook. Die Dry-Run-Disziplin zählt hier am meisten: jede Modifikation an diesem Branch wird vor dem Merge gegen den Dry-Run-Korpus getestet, denn eine Regression, die legitime Mail at Scale quarantänet, ist ein eigener Produktions-Incident.
Schritt 9: URL-Click-Detection
Parallel zur Quarantäne prüft das Playbook, ob ein:e Empfänger:in bereits eine der malicious URLs geklickt hat. Das ist eine Query gegen die Endpoint-Telemetrie (Proxy-Logs, Browser-Telemetrie, Endpoint-Agent) für die betroffenen User:innen im Fenster zwischen Mail-Ankunft und Playbook-Execution.
┌──────────────────────────────────┐ │ URL-click detection │ │ │ │ in: iocs.urls, targets, │ │ message.received_at │ │ out: clicks: ClickEvent[] │ └──────────────────────────────────┘
Ist das Array leer, fährt das Playbook mit Notification fort. Enthält es Einträge, eskaliert der nächste Schritt zu Endpoint-Containment für die betroffenen Hosts.
Dieser Schritt liest nur; er produziert keinen destruktiven Output und wirft kein Idempotenz-Bedenken auf. Es ist die Art Node, die Autor:innen manchmal in einen größeren kombinierten Schritt inlinen, und das lohnt sich zu vermeiden. Pure-Query-Nodes gehören in eigene Boxen, weil sie sich am einfachsten über andere Playbooks wiederverwenden lassen.
Schritt 10: Endpoint-Isolation (mit explizitem Failure-Handling)
Für jeden Endpoint, an dem ein Click erkannt wurde, ruft das Playbook den Endpoint-Adapter, um ihn vom Netz zu isolieren. Dieser Schritt scheitert unter realen Bedingungen am wahrscheinlichsten: der Endpoint kann offline sein, der Agent unerreichbar, der Adapter rate-limited.
┌──────────────────────────────────┐ │ Isolate endpoint (for each) │ │ │ │ in: endpoint_id │ │ out: isolation_result │ │ err: IsolationFailed → │ │ force-priority alert │ │ timeout: 30s │ │ dry-run: synthetic success │ └──────────────────────────────────┘
Der Error-Port hier routet nicht zu "log and continue". Er routet zu einem Force-Priority-Alert an die:den On-Call-Analyst:in mit angehängtem Endpoint-Kontext, denn ein gescheitertes Containment auf einer bekannten Kompromittierung ist ein Event mit höherer Dringlichkeit als der ursprüngliche Alert. Das Playbook tut nicht so, als sei eine gescheiterte Isolation gelungen; es eskaliert explizit, und der Audit-Record reflektiert sowohl den Versuch als auch sein Ergebnis.
Schritt 11: Multi-Channel-Notification mit rollenbewusstem Routing
Betroffene User:innen müssen benachrichtigt werden, aber Channel und Ton hängen von der:dem User:in ab. Die Notification-Node fan-outet pro Target mit rollenbewusstem Routing:
- Standard-User:innen: Notification über den freigegebenen internen Channel (Mail oder Chat), erklärt was passiert ist und was zu tun ist.
- Privilegierte User:innen (Finance, Executive, IT-Admins): dieselbe Notification plus sofortiger Force-Priority-Alert über den On-Call-Channel, denn die Konsequenz einer Credential-Kompromittierung ist höher.
- Externe Empfänger:innen: keine automatische Notification, an einen Menschen zur Prüfung eskaliert.
┌────────────────────────────────────────────────┐ │ Notify (for each target) │ │ │ │ in: target, message, classification │ │ out: notify_result │ │ err: NotifyFailed → analyst │ │ dry-run: synthetic success │ │ │ │ routing: │ │ target.privileged == true → on-call + user │ │ target.privileged == false → user only │ │ target.resolved == false → analyst review │ └────────────────────────────────────────────────┘
Der Notification-Text wird zur Runtime nicht von einem LLM generiert, zumindest nicht im destruktiven Branch. Templates sind vor-freigegeben, versioniert, und werden mit den alert-spezifischen Feldern gerendert. Das hält die Notification auditierbar und konsistent und verhindert, dass eine Modell-Regression alarmierende oder verwirrende Messages an nicht-technische User:innen at Scale produziert.
Schritt 12: Ticket-Erstellung, Audit-Trail und Post-Mortem-Record
Die finale Node erzeugt das Incident-Ticket und schreibt den Post-Mortem-Record. Das Ticket enthält die vollständige Klassifizierung (mit beitragenden Signalen), den Action-Trace (was quarantänet wurde, was isoliert wurde, wer benachrichtigt wurde) und Links auf den Roh-Alert und den Playbook-Execution-Trace.
┌──────────────────────────────────┐ │ Create incident + audit record │ │ │ │ in: classification, actions, │ │ targets, execution_trace │ │ out: incident_id │ │ err: → emergency mail to SOC │ │ dry-run: synthetic success │ └──────────────────────────────────┘
[Screenshot: Ticket-Detail-View zeigt die Klassifizierungs-Signale, den Action-Trace und den Link zurück auf die Playbook-Execution]
Der Execution-Trace ist der Teil, in den die meisten Teams zu wenig investieren. Zu rekonstruieren, was das Playbook tat (sechs Wochen später, wenn das Audit läuft), verlangt, dass Input, Output, Branch-Entscheidung und externer Call jeder Node mit stabiler Execution-ID persistiert sind. Ohne diese Disziplin bleibt das Playbook praktisch eine Blackbox; mit ihr wird es zu einer belastbaren Automatisierung, der Auditor:innen Node für Node folgen können.
Dry-Run-Disziplin, bevor irgendetwas davon ausgeliefert wird
Keiner der obigen zwölf Schritte wird ausgeliefert, ohne Dry-Run gegen einen kuratierten Alert-Korpus zu bestehen. Der Korpus enthält: einen repräsentativen High-Confidence-Phishing-Alert mit privilegierten Empfänger:innen, einen Medium-Confidence-Alert mit gemischten Empfänger:innen, einen Low-Confidence-Alert, einen Alert bei dem Threat-Intel timeoutet, und einen Alert mit nicht-auflösbarer:m externer:m Empfänger:in. Jede Änderung am Playbook läuft in CI gegen alle fünf; jede Abweichung vom erwarteten Execution-Trace lässt den Build fehlschlagen.
Das ist der Unterschied zwischen einem Playbook, dem das Team vertraut, und einem, um das das Team herumarbeitet. Die obigen Branches sind nicht theoretisch: sie sind die Cases, die der Korpus bei jeder Änderung exerciert.
Der eine Branch, den die meisten Teams zu verdrahten vergessen
Lesen Sie Schritt 7 erneut. Der default-Branch am zentralen Switch routet zur:zum Analyst:in. Lesen Sie Schritt 10 erneut. Der Error-Port an der Isolation routet zu einem Force-Priority-Alert. Lesen Sie Schritt 4 erneut. Der Error-Port am Threat-Intel routet zu einem Degraded-Path, der das Playbook trotzdem zu Ende führt.
Über alle drei hinweg gilt: der Failure-Mode ist verdrahtet, benannt und auditiert. Das trennt eine Automatisierung, der Operator:innen vertrauen, von einer, die sie nach dem ersten 3-Uhr-morgens-Page still umgehen, weil das Playbook nichts tat. Es kostet ein paar Nodes mehr auf dem Canvas und bringt dafür ein Playbook, das tatsächlich unbeaufsichtigt läuft.
Abschluss
Das ist die Form des Ganzen: zwölf Nodes, drei Conditional-Branches und vier explizite Error-Pfade, alle hinter einem einzigen Dry-Run-Check, bevor irgendetwas Produktion erreicht. So sieht end-to-end Phishing-Response aus, wenn man die Verdrahtungs-Disziplin ernst nimmt.
Der nächste Beitrag dieser Serie geht eine Höhenstufe hoch, von einem einzelnen Playbook zu den Metriken, die zeigen, ob Ihre Playbooks im Aggregat die Nadel tatsächlich bewegen. Wir schauen uns Coverage, Time-to-Contain, False-Positive-Rate und Automation-Share an, die Zahlen, die einer:einem CISO wichtig sind, und wie man sie instrumentiert.
Weiterlesen
- NIST SP 800-61r3: Computer Security Incident Handling Guide
- MITRE ATT&CK: Initial Access: Phishing (T1566)
- ENISA: Phishing Threat Landscape, jährlicher Bericht
- CISA: Stop Ransomware, Leitfaden zu Initial-Access-Defense
Unternehmensinfrastruktur abzusichern?
Vereinbaren Sie ein technisches Briefing. Kein Sales-Pitch, nur Architekten und Ihr Team.