
No quarantine for Bedrock Mantle
- AWS
- AI
- Cloud security
- Research
When AWS finds one of your access keys leaking, it puts a quarantine policy on the user that denies bedrock:InvokeModel, among other things, to stop attackers doing obviously bad and expensive stuff. But it doesn't deny bedrock-mantle:CreateInference, which is yet another way of running LLM inference.
How LLMjacking works
You can put cloud compromises into two buckets. 1. is commodity stuff that runs at scale. 2. is targeted attacks going after something specific. The way a commodity breach works is pretty well documented. Steal a key, find out what it can do, and if one of those things is 'use Bedrock', rent the breached models out to strangers and leave the owner with the bill. It's so common, it's got a name, LLMjacking. The stolen access gets resold through tools like oai-reverse-proxy to people who want an uncensored chatbot and do not want to pay for it.
Ian Ahl caught a crew running a sexual-roleplay service on stolen Claude access, some of it producing genuinely illegal content. Maayan Bentor tracked an operation minting IAM users and calling PutUseCaseForModelAccess and CreateFoundationModelAgreement to unlock models before invoking them. More recently, Martin McCloskey watched a credential-testing platform probe more than 80 customers, and Crystal Morin showed the black market adding DeepSeek-V3 to the menu within days of its release.
The recurring soft spot in all of this is logging. Alessandro Brucato's Bedrock Slip writeup showed that the probe calls attackers use to check which models they can reach, the ones that fail with ValidationException, stopped showing up in CloudTrail. You cannot alert on what you cannot see. Hold that thought.
What the quarantine policy blocks
AWS watches public places where keys get committed, GitHub secret scanning and the like, and when it finds a live one it attaches a managed policy called AWSCompromisedKeyQuarantine to the owning IAM user. This happens fast. William Gamazo and Nathaniel Quist timed it at about two minutes from leak to quarantine.
The policy is a deny list. It has grown over time, from V1 to V2 to V3, as people found ways around it. Bleon Proko's Detention Dodger catalogued what V2 forgot to block. And in October 2024, after the LLMjacking stories piled up, AWS added five Bedrock actions to the deny list. You can watch the exact change land in the MAMIP policy history.
Here is what V3 denies today, pulled straight from the live policy in my account:
bedrock:InvokeModel
bedrock:InvokeModelWithResponseStream
bedrock:CreateModelInvocationJob
bedrock:CreateFoundationModelAgreement
bedrock:PutFoundationModelEntitlementNinety-nine denied actions in total, five of them Bedrock. Every single one starts with the bedrock: prefix.
The quarantine policy only helps if AWS applies it. Plenty of live leaked keys still carry a version AWS stopped updating in 2023. A deny list is only as good as its coverage, and its coverage is never complete.
Project Mantle
In December 2025, AWS shipped Project Mantle, a second inference endpoint for Bedrock that lives at bedrock-mantle.{region}.api.aws. It is the OpenAI-compatible one. It speaks the OpenAI Chat Completions and Responses APIs and the Anthropic Messages API, so you can point an existing OpenAI client at it and go. It is built for agent workloads, with server-side tools, background inference, and projects.
The important part for us is in the endpoints comparison. Inference on the classic runtime needs bedrock:InvokeModel, but inference on Mantle needs bedrock-mantle:CreateInference. Different service. Different prefix. It even has its own service authorization reference.
There is no aws bedrock-mantle CLI command, so you sign a plain HTTPS request with SigV4 using the signing name bedrock-mantle. Every account gets a default project, so there is nothing to set up. Ask it what it serves:
GET https://bedrock-mantle.ap-southeast-2.api.aws/v1/models{
"object": "list",
"data": [
{"id": "deepseek.v3.1", "object": "model", "owned_by": "system", "status": "available"},
{"id": "openai.gpt-oss-120b", "object": "model", "owned_by": "system", "status": "available"},
{"id": "qwen.qwen3-coder-480b-a35b-instruct", "object": "model", "owned_by": "system", "status": "available"},
...
]
}My account returned 38 open-weight models, including DeepSeek, Qwen, Mistral, GPT-OSS, GLM, Gemma, Kimi, and Nemotron. A plain chat completion looks exactly like OpenAI, and so does the response:
POST https://bedrock-mantle.ap-southeast-2.api.aws/v1/chat/completions
{"model":"deepseek.v3.1","messages":[{"role":"user","content":"Reply with exactly five words describing the ocean."}],"max_tokens":40}{
"object": "chat.completion",
"model": "deepseek.v3.1",
"choices": [
{"index": 0, "finish_reason": "stop",
"message": {"role": "assistant", "content": "Vast, mysterious, teeming with life."}}
],
"usage": {"prompt_tokens": 13, "completion_tokens": 11, "total_tokens": 24}
}The "bypass"
This is not a bypass. Everything is working as expected. My boss made me put it in the blog post as 'proof'.
I made an IAM user, gave it a long-lived AKIA access key, and ran two calls before touching quarantine. Classic Converse through bedrock:InvokeModel worked ✅. Mantle CreateInference through /v1/chat/completions worked ✅. Good, a normal user can do both.
Then I attached AWSCompromisedKeyQuarantineV3 to that user, exactly what AWS does when your key leaks, and waited for it to take effect. Same key, same two calls.
The InvokeModel call died the way it is supposed to:
AccessDeniedException: User: arn:aws:iam::...:user/mantle-quarantine-test is not
authorized to perform: bedrock:InvokeModel ... with an explicit deny in an
identity-based policy: arn:aws:iam::aws:policy/AWSCompromisedKeyQuarantineV3As you'd expect, the Mantle call returned HTTP 200 and a real answer.
POST https://bedrock-mantle.ap-southeast-2.api.aws/v1/chat/completions
{"model":"deepseek.v3.1","messages":[{"role":"user","content":"In one sentence, what is the capital of Australia?"}],"max_tokens":60}{
"object": "chat.completion",
"model": "deepseek.v3.1",
"choices": [
{"index": 0, "finish_reason": "stop",
"message": {"role": "assistant", "content": "The capital of Australia is Canberra."}}
],
"usage": {"prompt_tokens": 15, "completion_tokens": 8, "total_tokens": 23}
}Tokens billed to me. Yay!
Logging this is not common
Mantle inference calls are CloudTrail data events, on the resource type AWS::BedrockMantle::Project. Data events are off by default and cost extra. Bedrock's own model invocation logging does not capture them either.
Based on data from Plerion customers, the % of AWS accounts that have Mantle data event logging enabled is closer to 0% than it is to 10%. Seems not ideal.
What to do about it
So, AWS, time to update the quarantine policy again and add bedrock-mantle:CreateInference, and probably bedrock-mantle:CallWithBearerToken, to V3 (or will it be V4?).
But deny lists are still bad, regardless. They are reactive by design, they only ever cover the surfaces someone has already noticed, and AWS keeps shipping new surfaces. Mantle is about ten months old. The next endpoint will arrive with its own prefix and its own gap, and the policy will trail behind it exactly like it trailed behind this one.
If you are defending a real account, do not wait on the deny list. Turn on CloudTrail data events for AWS::BedrockMantle::Project so you can actually see Mantle inference. Or SCP deny the whole service if you aren't going to use it.
Or let Plerion watch your accounts for you, in near real time.
