From a Forum Image Upload to a PR in OpenAI's Monorepo in 72 Hours. The Last Hop Was the Agent.

TV
Thiago Victorino
9 min read
From a Forum Image Upload to a PR in OpenAI's Monorepo in 72 Hours. The Last Hop Was the Agent.

“The entire timeline from initial discovery to access to OpenAI repo access took place in less than 72 hours.” That sentence is from Hacktron’s disclosure of how three researchers went from a bug in an image decoder to a pull request inside OpenAI’s internal monorepo. Read the chain hop by hop: libheif image decoder, a Debian package missing a security backport, ImageMagick, a Discourse image upload, community.openai.com, an OpenAI SSO identity flaw, a ChatGPT/Codex account, the GitHub connected integration, internal OpenAI repos.

Every hop but the last one is an ordinary web vulnerability. A decoder bug, a stale package, a forum. The last hop is new. An account on a coding agent, with a GitHub connector attached, turned a forum compromise into a write inside a private codebase. The agent was the blast radius.

The chain, on the clock

Hacktron’s timeline for the intrusion is all on 25 July 2026, in UTC. Between 05:00 and 06:00 the researchers had remote code execution and admin on the Discourse instance. Between 08:00 and 10:00 they filed the report through Bugcrowd. Between 13:30 and 15:30 they opened a harmless proof-of-concept PR in the internal monorepo, and testing stopped around 15:30. At 22:49:45 OpenAI confirmed the fix, roughly 14 hours after submission. The Discourse fix was ready on 27 July and the advisory, GHSA-vhm9-85gw-x335, went out on 28 July. The internal PR link is redacted at OpenAI’s request, so the disclosure describes it without showing it.

Hacktron is explicit about where the escalation lived: “the vulnerability to escalate is not Discourse-specific. It is an OpenAI SSO issue that turned the forum compromise into access to ChatGPT and Codex.” The forum was the entry. The identity layer carried the compromise upward, and the agent’s connector was where it became a write.

On 1 September 2026 OpenAI paid a $6,500 bounty. Per the disclosure, OpenAI’s note reads: “testing against the Discourse-hosted community.openai.com was explicitly excluded from our bug bounty program. The award recognizes the OpenAI-side finding.” Those are OpenAI’s words as quoted by Hacktron. The forum was out of scope and the identity flaw was in scope, which is a fair summary of where the real problem sat.

The part that scales

The OpenAI chain was one instance of a wider campaign. Hacktron writes that the campaign “took two-months, cost less than $3,000 in tokens in total, and was conducted by three researchers. Adapting the exploit to each new company usually took only one or two days.” On detection: “We are not aware of any company that detected the activity except Shopify, even after thousands of images were sent and their image processors repeatedly crashed.”

Two details in that account matter for anyone budgeting a defence. The first is cost: under $3,000 in tokens for a two-month, multi-company campaign. The second is the capability jump inside the campaign. Per Hacktron, Opus 4.8 “struggled across several sessions” with ASLR, while Opus 5 succeeded hours after its release and produced a working ARM64 exploit within 3 hours. GPT-5.6 Sol handled the blind-target case. The agent ran in an autonomous /goal loop against a Discourse Cloud instance that had been proxied to look like a CTF, because the model refused remote exploitation. The refusal existed. The researchers routed around it by changing what the target looked like.

So the cost of building the ordinary hops fell, and the hop that made them matter was an agent account that was not being treated as a credential.

The same shape, from the inside

Accomplish’s disclosure looks at the agent from the other direction. Where Hacktron reached the agent from outside and used it as an exit, Oren Yomtov started inside the Codex sandbox and asked what it could be made to do. Two escapes, both reported on August 12, 2026, both fixed within eight days, patched in Codex Desktop 26.818.21641+ and Codex CLI 0.149.0+. (Accomplish sells the mitigation it describes, so its account of how it built that product is marketing. The timeline and the version numbers are on the page.)

Overpatch is the first. In Codex CLI’s workspace-write mode, from the page, apply_patch “grants write access to the parent folder of each path in the patch”. A patch entry under /tmp therefore grants write to /. A write to a symlinked .zshrc means the next terminal the user opens runs unsandboxed.

Heapjack is the second, and the more instructive one. Codex Desktop bundles a Node REPL MCP server, installed into ~/.codex/config.toml. Untrusted code running inside it takes a V8 heap snapshot, brute-forces the per-run token from the shared heap, forges a request over the shared stdout pipe, and the native parent runs open outside the sandbox. In Accomplish’s words: “All of it ran at read-only, the strictest sandbox mode… Codex never asked to approve anything.”

Read-only mode. Zero prompts. Accomplish’s diagnosis fits both bugs: “Both bugs have the same shape. The thing doing the enforcement was sitting inside the thing being enforced.” The per-run token lived in the same heap as the untrusted code. The patch permission was derived from the paths the patch itself supplied.

Put the two disclosures side by side and the picture is complete. From outside, an SSO-linked agent account with a connector is a write path into your repositories. From inside, the sandbox’s own enforcement can be reached by the code it is enforcing. We covered a related case of an authorised feature used as an exit in When the Agent Sandbox’s Intended Feature Becomes the Exit. The September pair adds a complete external chain and a read-only bypass with an eight-day fix.

Control one: a token scoped to one resource

Cloudflare’s granular authorization for Workers is a permission model, and the post describes it without adoption numbers, so treat it as a design to copy rather than a trend to follow. The post’s own framing: “the last thing you want is for an agent to make a change in production, just because it was granted more access than it needs.”

Four roles, three scopes. The roles form a ladder:

RoleWhat it can do
Metadata Read-OnlyLists, settings, observability. No content.
Content Read-OnlyCode and data. No modify.
EditorRead and write content and settings. Cannot create or delete.
AdminFull control, including delete and granting access.

Each role applies at one of three scopes: Developer Platform, Product, or Resource, where Resource means a single Worker. API tokens scope the same way, and the post says such a token can be “given to your agent”.

Two design choices in that model are the lesson. Routes and custom domains need a separate “Workers Routes” permission on top of Editor, because a route change “could redirect production traffic or take the application offline”. Durable Object data needs Editor. And a 403 now links to the exact missing permission, which removes one reason to reach for an over-scoped token. Legacy roles are not deprecated, and per the post D1, R2 and KV are to follow.

Map that onto the Hacktron chain. An agent account whose GitHub connector held a Content Read-Only equivalent, scoped to one repository, would have turned a monorepo PR into a read of one repo. The SSO flaw would still have been a serious finding. The blast radius would have been a fraction of what it was.

Control two: credentials the agent cannot reach

Google’s Agent Substrate on GKE is the runtime version of the same idea. From the announcement: “Hardware-isolated Cloud Hypervisor microVMs or gVisor sandboxes, paired with egress proxies that enforce granular network policies and inject credentials outside the reach of the agents themselves”. The density and latency figures are Google’s own: 10x density, sub-500ms resume, over 500 suspend/resume activations per second, over 1,000 dormant agents per host. GA support for production is available via allowlist.

The phrase that matters is “outside the reach of the agents themselves”. Heapjack worked because the token was in the agent’s heap. Overpatch worked because the write permission was derived from paths the patch itself supplied. By the announcement’s own phrase, a credential injected by the egress proxy sits outside the agent’s reach: not in its heap, not in a file it reads. Whatever the agent’s code does, the secret it would need to forge a request is on the other side of a boundary it did not build.

That is the rule Accomplish stated and Google built: enforcement must not live inside the thing it enforces. Cloudflare’s ladder applies it to what a token may do. Agent Substrate applies it to where the token is kept. We argued earlier, in Why Agent Security Is an Architecture Problem, that blast radius is an architectural property. These two controls are what that property looks like as shipped configuration. The earlier containment primitives are in The Agent Perimeter Got Specific.

Do this now

Inventory every coding-agent account in your identity provider and list the connectors attached to each one. Treat each connector as a credential with the union of the account holder’s permissions, because that is how Hacktron’s chain used it. Then apply the two controls.

First, replace every broad connector token with one scoped to a single resource at the lowest rung that still lets the agent do its job. Use Cloudflare’s four rungs as the template, whether or not you run Workers: metadata read, content read, edit without create or delete, admin. Put production-affecting operations, the equivalent of Workers Routes, behind a separate grant.

Second, move credentials out of the agent’s reach. If the token sits in a config file the agent process can read, or in the same heap as code the agent executes, assume the enforcement can be reached by the thing it is enforcing. An injecting proxy is the reference shape.

Third, write down who reviews a PR opened by an agent account, because in this chain a harmless proof-of-concept PR was opened in a monorepo through one. A leaked token that stays live is a related failure we described in Build History Keeps Its Own Copy of Your Token, and the authorization boundary nobody set is the subject of Your Support Agent Can Now Reset 2FA.

The ordinary hops will keep being ordinary. Image decoders will have bugs and distributions will miss backports. What you control is whether the last hop is a read of one repository or a write in all of them.


This analysis synthesizes Hacking OpenAI (Hacktron AI, September 2026), Escaping the OpenAI Codex sandbox, twice (Accomplish, September 2026), Give every teammate and agent the right level of access to your Workers (Cloudflare, September 2026), and Agent Substrate available on GKE (Google Cloud, September 2026).

Victorino Group helps engineering teams scope agent connectors and move credentials out of the agent’s reach before the next chain finds them. Let’s talk.

All articles on The Thinking Wire are written with the assistance of Anthropic's Opus LLM. Each piece goes through multi-agent research to verify facts and surface contradictions, followed by human review and approval before publication. If you find any inaccurate information or wish to contact our editorial team, please reach out at editorial@victorinollc.com . About The Thinking Wire →

If this resonates, let's talk

We help companies implement AI without losing control.

Schedule a Conversation