behind-the-curtain-part-02

What’s Behind the Curtain? AWS Bedrock AgentCore Runtime Tear Down – Part II

Dan Gansel September 15, 2026

Recap

In Part I, we explored the AgentCore Runtime microVM from the inside and discovered we weren’t alone – four platform binaries were running alongside our code, and one of them was quietly shipping logs to an AWS-internal S3 bucket. We left off with a question: what can we learn from these internal components, and how far can we push them?

Stealing AWS Internal Logs via MMDS MITM

With access to the Logger binary and a few extra privileges, we could have gone the dynamic analysis route. Instead, we tried something more creative.

The Logger fetches its presigned S3 URL from MMDS over plaintext HTTP every ~0.5 seconds. It doesn’t validate the MMDS token it receives, and it doesn’t validate the URL target – it PUTs to whatever URL the metadata service returns. What if we could redirect that?

The route table showed MMDS traffic routed to eth0:

image-43

The ip route show results

Simple route table changes didn’t work – rerouting to a different interface just hit “Connection refused.” But from the escape container with CAP_NET_ADMIN, we had access to iptables. The technique – which we call MMDS Hijack – works in five steps:

1. Cache the original MMDS values (including the presigned log URL and KMS key)

2. Start a fake MMDS server on localhost which serves the cached values for most paths

3. Configure researcher-controlled values – our own S3 bucket and KMS key for the log URL and KMS key paths

4. Redirect MMDS traffic via iptables DNAT to the fake server and reject access to the real MMDS, forcing the Logger to reconnect through the redirect

5. Wait – the Logger dutifully reads the new configuration and ships its internal logs to our bucket

image-48-1024x582

MMDS Hijack architecture: fake metadata service redirects platform logger output to attacker-controlled S3 bucket

It worked. The Logger, now reading our fake MMDS, began writing its internal log stream to our S3 bucket. We captured the full platform boot sequence, lifecycle state transitions, TLS certificate provisioning, and proxy initialization logs.

Some of the captured logs:

image-45

Captured logs showing internal data about proxy functionality

The logs revealed new: the platform proxy is the component that handles every inbound invocation of your agent. It requests TLS certificates and a symmetric signing key from MMDS at boot, then terminates all incoming connections. We could also see it verifying signed tokens for an AWS-internal domain – cell…kepler…/invocations – the first sign of how invocations actually reach your code:

image-44-1024x61

Proxy logs showing signed token verification

If we wanted to understand how AWS routes requests to your agent – and what controls protect that path – the proxy was the next layer to peel.


The Platform Proxy – Invocation Flow and JWT Forging

Proxy Reconnaissance

The natural first move was to poke at the proxy directly – try to intercept its traffic, inspect the requests it handles, understand what it validates. The problem: the proxy is extremely fragile. Any disruption kills it, and with it the entire microVM. The control plane runs aggressive health checks, and a dead proxy means a dead session.

However, capturing the logs paid off. They identified the proxy as being built on Pingora – Cloudflare’s open-source proxy framework written in Rust:

image-40

Proxy logs confirming the Pingora framework

That detail mattered because Pingora supports graceful upgrades via SIGQUIT – a way to replace a running server binary without dropping connections or triggering a crash:

Pingora documentation about graceful upgrades

We used this property to intercept TLS traffic without killing the proxy – creating an SSL hook and using SIGQUIT to gracefully replace the running proxy with an instrumented version.

Captured Invocation Request

With the TLS intercept in place, we captured a full invocation request from AWS’s internal cell service:

image-46

Captured invocation request headers

Two headers stood out:

workloadaccesstoken – an AWS opaque token for brokering access to AgentCore Identity and first-party services. AWS documentation describes it as a security-by-design token that agent identities cannot directly retrieve.

Decoding the WorkloadAccessToken revealed it uses the AWS Encryption SDK v2 with AES_256_GCM, references a KMS key ARN, and contains the customer account ID:

image-38

Decoded WorkloadAccessToken key insights

x-aws-guest-auth – a JWT with a much simpler structure:

image-39

Decoded JWT data

The legitimate JWT carries eight fields, but the proxy logs showed it requesting only the group ID and symmetric key from MMDS for validation:

Proxy logs before JWT validation

Examining the proxy’s validation logic confirmed it only enforces two claims: exp (expiration) and gid (group ID). The remaining fields are informational metadata passed through without validation. Testing confirmed this – a minimal JWT containing just exp and gid, signed with the MMDS symmetric key, was accepted by the proxy and returned a full LLM response.

image-41

Successful invocation using a minimal JWT

image-42

LLM response

JWT Forging Scenario

Both the gid and the symmetric signing key are available from MMDS without any container escape.

This created a forging scenario:

1. Read MMDS to obtain the gid and symmetric signing key

2. Craft a minimal JWT with just exp and gid

3. Send the forged JWT with a prompt to the platform proxy at localhost:8443

4. The proxy validates the JWT against MMDS values and launches a new container session

image-47-1024x595

JWT forging flow

The implications are notable in a specific unlikely scenario: an agent behind a WAF, vulnerable to SSRF with POST capability. The attacker could read MMDS (but not the credentials section due to the WAF), forge a JWT, and invoke new container sessions directly through the internal proxy. These forged invocations bypass the Bedrock API gateway entirely – producing zero CloudTrail events – and would bypass other WAF rules.

Forged requests also exposed the possibility of header spoofing: the proxy passed through internal headers like x-aws-proxy-ip and x-aws-proxy-port without sanitization. x-aws-proxy-ip was purely informational – spoofing it had no effect on routing. But x-aws-proxy-port controls backend routing – setting it to a port with no listener returned a 502, proving the proxy attempted the connection.

Remediation

After review, AWS now enforces mTLS on platform proxy communication, making this scenario no longer possible out of the box. The proxy requires a client certificate that isn’t available to the container session:

Proof of mTLS enforcement


Full Invocation Flow – Architecture Reconstruction

From the captured traffic, logs, binary analysis, and other data, we reconstructed the end-to-end invocation architecture. This is an inferred flow based on observed behavior:

image-49-1024x568

Reconstructed invocation architecture

The flow works as follows:

1. Your endpoint sends an HTTP/2 request to bedrock-agentcore.us-east-1.amazonaws.com

2. The request is routed by ARN to a cell – an internal endpoint like cell...kepler-analytics... (not publicly accessible)

3. The cell forwards to the platform proxy at localhost:8443 with WorkloadIdentityToken and x-aws-guest-auth headers

4. The proxy validates the JWT, strips the x-aws-guest-auth header, and forwards the request to the container session as HTTP/1.1

The HTTP/2-to-HTTP/1.1 version mismatch at the proxy boundary led us to test for HTTP Desync. We successfully concatenated two responses – the proxy appended its internal response to the user’s response – though no practical exploitation was possible based on the observed behavior.

image-40

HTTP Desync evidence

Conclusion

In this post, we redirected the platform logger’s output to our own S3 bucket by spoofing the MMDS, then used the proxy’s graceful upgrade mechanism to intercept live invocation traffic. The captured requests revealed a JWT-based authentication scheme – and from within the microVM, we were able to forge minimal tokens that the proxy accepted. AWS has since enforced mTLS on the proxy, closing that path. We also reconstructed the end-to-end invocation architecture from the captured traffic.

Next time, we’ll test the network boundaries of the microVM – probing cross-VM communication, investigating how VPC mode attaches to a customer’s VPC, and uncovering a routing behavior that let internal platform traffic bypass customer VPC Endpoint policies.


This research was presented at Cloud Village, DEF CON, by Dan Gansel of Upwind Security.

Contents

Further Reading

behind-the-curtain-part-01

What’s Behind the Curtain? AWS Bedrock AgentCore Runtime Tear Down – Part I

Introduction When you deploy an AI agent to AWS Bedrock AgentCore Runtime, your code runs inside a Firecracker microVM - but it doesn't run alone. In this three-part series, we tear down the platform internals, document what we found, and assess how well the isolation holds up. Setting the Stage AWS Bedrock AgentCore Runtime is…
upwind-code

Upwind Code Expands Enterprise Coverage to Azure DevOps and Bitbucket Cloud

Modern development organizations rarely keep all their code in one place. Teams may use different version control platforms because of acquisitions, business-unit preferences, regional requirements, or existing development workflows. But when code is spread across multiple providers, application security coverage can become fragmented too. Today, Upwind Code adds support for Azure DevOps and Bitbucket Cloud.…
Blue-agent

Upwind Blue Agent – Increasing the Scope and tooling to a new level of incident response

Cloud attacks do not stay within the boundaries of a single security tool. An intrusion can begin with an API request, execute a process inside a Kubernetes workload, modify a file, contact an external host, use a cloud identity, and change cluster state, all as part of the same incident. But the evidence needed to…
Add the Upwind RSS Feed to Slack
Connect the Upwind RSS Feed to your Slack.
Follow the how-to here.
Threat RSS
Add the Upwind RSS Feed to Slack
Connect the Upwind RSS Feed to your Slack.
Follow the how-to here.
Main RSS