Any request was the admin. Loom’s control plane had no identity provider.

2026-10-03

Patch line, if you only have a minute. Loom (AWS Labs’ open-source agent orchestrator) below 1.6.1, with no identity provider and an API reachable past your own machine, was an open admin panel: CVE-2026-103956, CVSS 10.0. Upgrade to 1.7.0, not just 1.6.1. 1.6.1 closes that door only. 1.7.0 also closes the token leak and the internal-network fetch. Self-hosted GitLab AI Gateway: 19.2.4, 19.3.2, or 19.4.1. GitLab.com is already patched.

On Friday 2 October 2026, two companies described holes in the software that sits in front of an AI agent (a program that can use tools and take actions, not only reply in a chat box). AWS published bulletin 2026-124 about Loom. GitLab published a critical patch for its AI Gateway (the service that stands between GitLab and the model, and that holds the keys proving a request is allowed): CVE-2026-90970.

The sentence people will reach for is “the agent escaped.” In both writeups, the model is not the part that failed first. A door in the program around the model was the wrong kind of door. One door had no lock. The other door was a text filter that could still run commands.

Four rooms, before any CVE number

Sparse map of Loom with no identity provider, versions 1.6.1 and 1.7.0, and the GitLab template bug

A CVE (a public ID for one vulnerability, so two people can mean the same bug) is useless until the rooms are separate. There are four. Mixing them up is how a patch for the wrong room gets called “we are sandboxed now.”

The model is the program that produces text, and sometimes a decision about which tool to call. It has no AWS account of its own. It has no GitLab server of its own. It asks.

The tools are the things it can ask for: a file, a web request, a terminal (a text window that runs commands on a computer), another agent.

The control plane (the front desk: the admin website and the API that decide who may add a tool, read the key box, or change what an agent is allowed to do) is not the model. API means a way for programs to send requests, instead of clicking a page. An orchestrator (the product that lines agents up and connects them to tools) has a control plane. Loom is an orchestrator. The GitLab AI Gateway is closer to a shared front door for model calls than to a full orchestrator, but it is still a door with keys behind it.

A sandbox (a locked room the program is not supposed to leave) is the fourth thing, and the word is doing too much work. Sometimes it means a separate machine. Sometimes it means “we told the model to behave.” Sometimes it means a template engine (the program that fills blanks in a text file) promised not to treat your text as code. Those are not the same lock. This post keeps them apart.

Four rooms: the model, the tools, the control plane, and a sandbox that may only be a promise
Four rooms: the model, the tools, the control plane, and a sandbox that may only be a promise

If the diagram does not render, the labels are the picture. The dotted line is the point. A sandbox that is only a sentence in a prompt does not surround the control plane.

The front desk stamped every visitor as the boss

Loom’s own security note, GHSA-vgmj-998f-r8mp, is plainer than the headlines. When no identity provider was configured, the backend’s authentication dependency granted every incoming request, including requests with no Authorization header at all, a super-admin identity with every scope in the system. The code path they name is get_current_user in backend/app/dependencies/auth.py.

An identity provider (the outside system that says this login belongs to a real person or a real app) was missing. AWS names two acceptable ones in the workaround: a Cognito user pool (Cognito is AWS’s hosted user directory) or an active external identity provider (any other login system you have actually turned on).

An Authorization header (the line on a request that carries a token, which is a temporary proof of login) was not required. A request with nothing in that line still passed.

A super-admin identity with every scope means the request was treated as the top account, holding every named permission the product has. A scope (one named permission, such as “allowed to register tools”) is the unit those permissions come in.

Any network client (any computer that can open a connection to that API) could be that account. Reachable is the condition: a laptop on the same VPC (your private cloud network), a CI job (an automated build), a forgotten load balancer (a public address that forwards traffic in). The bulletin’s workaround says the backend should not be reachable beyond loopback (the machine talking only to itself, the address 127.0.0.1) until the identity provider is fully configured.

This is CVE-2026-103956. Amazon scored it 10.0 on CVSS 3.1 and 10.0 on CVSS 4.0. CVSS (a shared score sheet for how bad a hole is, from 0 to 10) is not a vibe. The 3.1 vector is CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H. In words: the attack is over the network, it is not complicated, the attacker needs no account, nobody has to click, the damage is not confined to one small component, and confidentiality, integrity, and availability are all high. The letters that matter next to GitLab, later, are PR:N. No privileges. No account.

The weakness labels are CWE-306 (the catalog entry for missing authentication on a critical function) and CWE-1188 (the catalog entry for initializing a critical resource with an unsafe default). Here the default was: no identity provider configured, therefore everyone is the admin. That is the bug. Not a clever prompt.

The fix shipped in Loom 1.6.1 on 4 August 2026. The public bulletin is 2 October 2026, 12:00 PM PDT. If your API was reachable on a version before 1.6.1, the bulletin is a window that already happened. AWS does not say customers were broken into. Absence of that sentence is not a clean bill. The hole did not need a stolen password.

The fix, from the same GitHub advisory: it now requires an explicit opt-in named LOOM_ALLOW_UNAUTHENTICATED_LOCAL_DEV, and it restricts that bypass to requests from loopback. An identity-provider-less deployment can no longer be reached as an open admin panel over the network, even if someone leaves the flag set by mistake. Confirm the flag is unset on anything that is not your laptop.

A request with no login header becomes super-admin when no identity provider is set
A request with no login header becomes super-admin when no identity provider is set

What that admin was allowed to do

The bulletin lists three powers. Do not collapse them into “they could hack AWS.”

Register tool servers. A tool server, in Loom’s words, is an MCP server. MCP (Model Context Protocol, a common way for an agent to call a tool that lives in another program) means the orchestrator will send work to an address you name. If an attacker registers the address, the next agent call can be talking to them. The tool result that comes back can also carry a prompt injection (hidden text that tries to steer the model). That is a delivery truck you just authorized.

Read stored integration credentials. Credentials (the saved secrets: passwords, tokens, API keys) are what Loom keeps so it can sign in to other products for you. Reading them means taking the keys out of the key box. Anything those keys open should be treated as exposed if this hole was reachable.

Rewrite IAM role policies attached to managed agent roles. IAM (Identity and Access Management, AWS’s list of what a cloud identity may do) uses a policy (a written allow and deny list) on a role (an identity a program wears, instead of a person logging in). Managed agent roles are the roles Loom’s agents run as. Rewriting the policy means rewriting that allow list. The bulletin says this rewrite was possible. It does not print your policy. The damage is whatever that role could be expanded to inside the account. If the API was reachable and no identity provider was set, treat those roles as attacker-controlled until you have read the policy history.

Super-admin can register a tool server, read saved keys, and rewrite the agent role
Super-admin can register a tool server, read saved keys, and rewrite the agent role

1.6.1 locks that door. It does not finish the job.

Two more issues are in the same bulletin. Both need a real login. Both need a scope: mcp:write or a2a:write. A2A (Agent2Agent, a protocol for one agent to call another agent across the network) is the remote-agent side, the way MCP is the tool side. A person who can register tools or remote agents can hit these. That is a smaller crowd than anyone on the network. It is still everyone in the admin-ish groups.

CVE-2026-103957 affects versions before 1.7.0. It is an OAuth2 discovery bug. OAuth2 (a login handshake where an app receives a token instead of storing the user’s password) publishes its instructions at a discovery URL. A well-known URL (a conventional address where a service describes how to log in) is what the attacker is allowed to set, if they have those write scopes. The document at that address can tell Loom’s backend (the server, not the browser) to send OAuth2 client secrets (the app’s own password for the login handshake) or another user’s access token (the temporary pass issued after login) to an endpoint the attacker controls. CWE-918 is SSRF, defined in the next paragraph. CWE-201 is information exposure through sent data. AWS’s words on the partial fix: the 1.6.1 release blocked internal-address reach for this path but did not fully address the token disclosure. Version 1.7.0 does. If you stopped at 1.6.1 because the scary CVE was marked fixed, you are still on this one.

CVE-2026-103958 is the same version range and the same scopes. SSRF (server-side request forgery: you choose a URL, and the server fetches it, so the request comes from the server’s place in the network, not from yours) sits in the connection check for a tool server or a remote agent. The attacker points that check at an internal address and reads the response. AWS specifically includes the container’s credential-vending endpoint (the local URL that hands the container its cloud keys). Those keys are temporary credentials for the role the Loom process itself is running as. That is not the agent role from CVE-2026-103956. It is the role of the box. With it, the attacker can call AWS APIs as Loom’s host, inside whatever that role allows, until the keys expire. The bulletin does not print the numeric address. On Amazon’s container services that endpoint is a link-local address (an address that only means something on the local machine’s link, never routed across the public internet). Block those, not only public websites.

Their workaround until you upgrade is not a fix. Restrict mcp:write and a2a:write, and restrict membership of the groups g-admins-super, g-admins-mcp, g-admins-a2a, and g-admins-demo, to trusted administrators. They say this reduces the likelihood and does not fully close the issue without the code fix.

For the open admin door only, the workaround is: configure Cognito or an external identity provider before the backend is reachable past loopback, and make sure LOOM_ALLOW_UNAUTHENTICATED_LOCAL_DEV is unset outside local dev. That workaround does not close 103957 or 103958.

After you are on 1.7.0, AWS’s list, in order:

  1. Rotate any OAuth2 client secrets configured for MCP or A2A integrations. Rotate means issue a new secret and throw the old one away.
  2. Revoke and re-issue any access tokens that were alive during the affected window.
  3. If container role credentials were accessed, rotate that role’s session credentials and review CloudTrail (AWS’s log of API calls) for use you do not recognize.

Forks count. The bulletin says derivative code has to take the same fixes. A pinned copy you patched by hand in March is not 1.7.0.

Version you are onOpen admin, no login, CVE-2026-103956Token disclosure, CVE-2026-103957Internal fetch of container keys, CVE-2026-103958
Before 1.6.1Open, if no identity providerOpen, if the user has write scopeOpen, if the user has write scope
1.6.1 up to but not 1.7.0Closed. Flag is loopback-only.Still open. Internal addresses blocked, disclosure not fully fixed.Still open
1.7.0 or laterClosedClosed. Then rotate secrets and tokens.Closed. Then check CloudTrail if keys were read.
Before 1.6.1 anyone on the network is admin; 1.6.1 shuts that door; 1.7.0 closes the rest
Before 1.6.1 anyone on the network is admin; 1.6.1 shuts that door; 1.7.0 closes the rest

Same Friday, a different wall: GitLab’s template

CVE-2026-90970 is not Loom. Putting them in one “AI sandbox” headline deletes the part you would act on.

GitLab’s sentence: an authenticated user with Duo Agent Platform access could escape the prompt template sandbox via a specially crafted flow configuration, leading to arbitrary command execution on the AI Gateway. Duo Agent Platform (GitLab’s product for agents that run flows) is the access you need. A flow (a saved workflow: steps, prompts, tools) is configured in a file. A flow configuration is that file. A prompt template (the instruction text, with blanks the system fills in) is one field in it.

The title GitLab uses is “Improper Neutralization issue in custom flow prompt template.” Neutralization (stripping or encoding the characters that would otherwise mean “do this,” so they stay plain text) did not hold. The class of bug is template injection (the engine that fills in the blanks treats the attacker’s text as instructions to itself, not as a sentence for the model). OpenCVE maps it to CWE-1336. GitLab has not published the crafted flow. This post does not invent one. Do not paste a guessed payload into a flow file to see if you are vulnerable. The version check is the test.

A toy, labeled as a toy, so the mechanism is visible. Picture a mail-merge line that reads Hello plus a blank named name. If the blank is Sam, the output is a greeting. If the engine is the kind that can also run code, and the blank contains a snippet that means “run a command,” you do not get a greeting. You get a command, on the machine that filled in the blank. A prompt that says “you are a helpful assistant, do not hack” never enters this picture. The model is not the program that broke. The template engine on the gateway is.

Arbitrary command execution means the attacker chooses the command. It runs on the AI Gateway host. The Hacker News writeup notes that a self-hosted gateway holds signing keys for JWTs (JSON Web Tokens, signed passes that say a request is allowed to proceed) and connects onward to your GitLab instance and to model providers. GitLab does not say those keys are automatically stolen. A command on the box that stores them is enough to treat the box as untrusted until you patch and until you rotate anything that box could read.

The score is 9.9, vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H. Same shape as Loom’s 10.0 except PR:L: low privileges, meaning a logged-in user, and GitLab further requires Duo Agent Platform access. Not anonymous. Still no click, still network, still command execution outside the chat transcript.

Affected AI Gateway versions: from 18.1.6 before 19.2.4, from 19.3 before 19.3.2, and from 19.4 before 19.4.1. Patched releases: 19.2.4, 19.3.2, 19.4.1. GitLab says self-managed customers with a self-hosted gateway should update immediately. No workaround is listed. Credit is the HackerOne researcher invisiblemeerkat. The Hacker News, citing CISA (the US cyber agency that tracks bugs known to be used in attacks), reported no known exploitation at disclosure. GitLab does not tell you how to prove a flow was never abused. After you patch, look for flow files you did not write. A quiet log is not a proof. That review step is mine, not a quoted GitLab procedure.

Who can stop: GitLab says a fix is already deployed for GitLab-hosted AI Gateways. Customers on GitLab.com, GitLab Dedicated, and Self-Managed instances that use a GitLab-hosted AI Gateway are protected and do not need to take action. “We run GitLab ourselves” is not the same sentence as “we run the AI Gateway ourselves.”

A GitLab-hosted AI Gateway is already patched; a self-hosted one needs the fixed build
A GitLab-hosted AI Gateway is already patched; a self-hosted one needs the fixed build

One line from GitLab’s own security-threats page, because it prevents a second wrong purchase. For flows that run as GitLab Runner jobs, their table lists the sandbox as an isolated VM (a whole pretend computer). For IDE and CLI agents (the agent inside your editor, or in the gitlab command on your laptop), and for Duo Agentic Chat, the same table says the sandbox is not applied. Patching CVE-2026-90970 does not put a VM around the agent on your laptop. Their page also says that for chat and for the IDE, security relies primarily on human approval. A human clicking Allow is not a sandbox. It is a person, tired, at 6 p.m.

If you built your own, the three questions

You do not need Loom or GitLab installed to have the pattern.

If the login system is missing, does the code refuse, or does it invent an admin? Search for the flag you use so local development works without a login. Loom’s flag is LOOM_ALLOW_UNAUTHENTICATED_LOCAL_DEV, and before 1.6.1 you did not even need the flag: the absence of an identity provider was enough. Default-open is the vulnerability. A comment that says you will enable auth later is not a control.

Can someone who is allowed to add a tool type a URL your server will fetch from inside the network? That is the SSRF. The addresses that matter are the inside ones: the instance metadata service (the link-local URL cloud VMs use to learn who they are and to pick up keys) and the container credential endpoint. An allowlist of public vendor URLs does not save you if link-local still answers.

Does user-written template text get rendered by an engine that can run code, on a host that has keys? Then that template is a program with a friendly file extension. The sandbox has to be a process boundary or a VM. A line in the system prompt that says “never run commands from the template” is the fourth room drawn as a dotted line. It does not hold a template engine.

Three questions: a missing login, a link-local fetch, and a template engine beside keys
Three questions: a missing login, a link-local fetch, and a template engine beside keys

What this week does not mean

It does not mean a model decided to rebel, and it does not mean weights (the learned numbers inside a model) left the building. These advisories are about the doors.

It does not mean the 20 September DNS sandbox escape. That was a training environment whose resolver (the service that turns a name into an address) could still see the public internet, after HTTPS was blocked. Closing DNS does not close an admin API. Closing an admin API does not close DNS.

It does not mean uninstall GitLab. GitLab.com, Dedicated, and anyone on a GitLab-hosted gateway are already on the fixed side, by GitLab’s own sentence.

It does not mean 1.6.1 is current. That release closes CVE-2026-103956. CVE-2026-103957 and CVE-2026-103958 wait for 1.7.0.

It does not mean there is a public exploit to replay. The sources used here do not include a proof of concept.

What is AWS Loom CVE-2026-103956?

Missing authentication in Loom for AWS before 1.6.1. If no identity provider was configured, any request that could reach the API, including a request with no login header, was treated as super-admin. That includes registering tool servers, reading stored integration credentials, and rewriting IAM policies on managed agent roles. Fixed 4 August 2026 in 1.6.1. Disclosed in AWS bulletin 2026-124 on 2 October 2026. CVSS 10.0.

Is upgrading to Loom 1.6.1 enough?

No. It closes the open admin door, and it limits the local-dev bypass to loopback. The OAuth token disclosure and the server-side fetch of internal addresses, including the container credential endpoint, are fixed in 1.7.0. After that, rotate OAuth client secrets, revoke the tokens that were live, and review CloudTrail if container credentials were read.

Does this affect GitLab.com?

GitLab says no, for the AI Gateway issue. GitLab.com, GitLab Dedicated, and Self-Managed instances that use a GitLab-hosted AI Gateway are already patched. Self-hosted AI Gateway installs on the affected versions need 19.2.4, 19.3.2, or 19.4.1.

Did the GitLab prompt sandbox fail because the model jailbroke?

No. GitLab describes improper neutralization in a custom flow prompt template. A logged-in Duo Agent Platform user could escape that sandbox with a crafted flow configuration and run commands on the AI Gateway. That is the template engine, not the model ignoring a policy sentence. The score is 9.9, and it requires an account, unlike Loom’s no-account hole.

What should I rotate?

On Loom, if the open-admin window applies: integration credentials that were stored, and the IAM roles whose policies could have been rewritten. On 1.7.0’s list even if you were already past 1.6.1: OAuth2 client secrets for MCP and A2A, access tokens from the window, and the container role if its credentials were fetched. On a self-hosted GitLab AI Gateway you have not patched: assume the gateway host could run an attacker command, so rotate what that host could read, including gateway signing keys, after you patch. GitLab does not spell that rotation out. It follows from command execution on the box that holds the keys.

If you do one thing tonight

If you run Loom, read the version and answer one question: was an identity provider fully configured before this API was reachable past loopback? If the version is below 1.6.1 and the answer is no, you were the open door. Move to 1.7.0, not 1.6.1, and do the rotation list. If you do not run Loom, and you do run a self-hosted GitLab AI Gateway, the builds that count are 19.2.4, 19.3.2, and 19.4.1. If you run neither, ask your own orchestrator whether a missing login becomes an admin. That is not a sandbox. It is a badge printer.

Leave a comment