Breachwire.riscent

Breach report

CamoLeak: GitHub Copilot Chat turned into a silent private-repo exfiltration channel

AI & LLM BreachesCritical (CVSS 9.6)CVE-2025-591452025-10
The bottom lineInvisible instructions hidden in a pull request made GitHub Copilot Chat leak private source code and secrets one character at a time through GitHub's own image proxy.
Category
AI & LLM Breaches
Type
CVE · Named incident (GitHub Copilot Chat)
Date
2025-10
Severity
Critical (CVSS 9.6)
OWASP
LLM01 Prompt Injection; LLM02 Sensitive Information Disclosure
CWE / CVE
CWE-77 CVE-2025-59145

What happened

GitHub Copilot Chat answers questions about a repository using the context of whoever is chatting with it — including their private repositories. A researcher disclosed a vulnerability, tracked as CVE-2025-59145 and nicknamed CamoLeak, that abused that trusted access to silently steal source code, API keys, and other secrets from private repositories.

The attack hid malicious instructions inside a pull request using GitHub's invisible Markdown comment syntax — text that never renders in the web interface but is still ingested by Copilot as if it were a legitimate command. When a victim asked Copilot Chat about the pull request, the assistant executed the attacker's instructions against the victim's own permissions. To move the stolen data out, the attacker had pre-computed a dictionary of valid, signed URLs for GitHub's Camo image proxy, each pointing at a 1×1 transparent pixel on the attacker's server and each representing one encoded character. As Copilot emitted the images, the outbound requests flowed through GitHub's own trusted infrastructure and looked like ordinary image loading. The flaw carried a CVSS score of 9.6. GitHub remediated it by disabling image rendering in Copilot Chat.

Root cause

  • LLM01 Prompt Injection (indirect). Copilot could not separate trusted developer intent from untrusted repository content. Hidden text in a pull request was treated as instruction — a trust-boundary failure identical in shape to SQL injection.
  • LLM02 Sensitive Information Disclosure. Because Copilot ran with the querying user's private-repo access, injected instructions could read and encode secrets the attacker had no rights to see, then smuggle them out through an allowlisted proxy channel.

How it would have been caught

An AI red-team pass with an indirect-injection probe — using a tool such as Garak, PyRIT, or Promptfoo — planted in pull-request metadata would have surfaced the behavior pre-release. So would an output-side check that flags model-emitted image URLs, since the exfiltration rode entirely on auto-fetched images. The reproduction is direct: seed a PR with a hidden-comment instruction, ask Copilot about it, and watch for any outbound request encoding repository content.

How to prevent it

  • Treat repository content (PR bodies, comments, diffs) as untrusted data delivered in labelled, source-tagged blocks, never as instructions.
  • Deny or neutralize model-authored images and links to non-allowlisted destinations — and recognize that a trusted first-party proxy is still an exfiltration channel if its URLs can encode data.
  • Scope the assistant's data access to the current task rather than the full breadth of the user's private repositories.

The Breachwire test (red → green)

Plant a hidden-comment instruction in a pull request and confirm the exploit fires — repository content leaves via encoded image fetches (the RED control proving the vector is real). Apply the containment above, then confirm the same payload no longer exfiltrates, while a benign question about the PR still returns a correct, cited answer (the positive control that stops a break-everything "fix" from faking success).