I was updating my agent sandbox audit scripts over the weekend and stumbled into something that changed how I look at every SDK helper that shells out. AWS's Bedrock AgentCore Python SDK, the thing you use to run code in Amazon's Code Interpreter sandboxes, had the same injection bug fixed twice in the same function. Both fixes were bypassable, each in a different way.
This matters right now because BeyondTrust's Phantom Labs team published their full research on it yesterday (Sep 28), and as of this morning I can find no other write-up anywhere: zero threads on Hacker News, zero articles on Dev.to. If you run anything that calls install_packages(), this post is the three checks I'd run before you deploy another agent.
First, the shape of the bug:
# From the SDK's Code Interpreter client (simplified)
def install_packages(packages: list[str]):
# passes user-controlled strings into a shell command inside the sandbox
pkg_str = " ".join(packages)
cmd = f"pip install {pkg_str}"
run_in_sandbox(cmd) # CVE-2026-12530: delimiters were never neutralized
That's CWE-88, argument injection. Not a buffer overflow, not a memory bug: a function took free-form strings and built a command out of them.
What exactly went wrong in install_packages()?
The function exists for a good reason. AgentCore's Code Interpreter lets your agent install libraries at runtime, so the SDK wraps pip install for you. You hand it package names, it installs them inside an isolated sandbox.
The problem is what counts as a "package name". Between v1.1.3 and v1.6.0, the strings you passed were joined into a shell command with almost no validation. According to the advisory (GHSA-6rfw-mq36-jm8h) and AWS bulletin 2026-044-aws, a crafted string could break out of the argument position and run attacker-chosen commands inside the sandbox. BeyondTrust's research shows one path used a newline to smuggle a second command.
How bad is a sandbox command actually?
This is where I want to be careful, because the severity numbers look milder than the story feels.
NVD has both CVEs at CVSS 3.1 7.3 and CVSS 4.0 8.4, both marked Secondary and still Awaiting Analysis as of today. The CVSS vectors cap the impact at the sandbox boundary: the commands run inside the Code Interpreter sandbox, not on your workstation or your AWS account directly.
But the sandbox is not nothing. The first advisory also flags that a crafted name could redirect pip at an attacker-controlled package index and expose sandbox files and environment variables. That index redirect is the same "supply your own packages" trick behind the LiteLLM supply chain backdoor I covered earlier this month. And BeyondTrust's own research title goes further: "Package Name to Role Credentials in Code Interpreter". I have not reproduced the credential chain myself, so I'll treat that as their finding rather than a fact I verified. The reason I take it seriously is that their team also published a companion repo, agentcore-sandbox-breakout, demonstrating DNS and S3 exfiltration paths from these same sandboxes.
If your agent's sandbox holds anything interesting: env vars with keys, session tokens, your source code, the boundary matters.
Why did the first fix not hold?
Because injection fixes decay. The timeline:
| Date | Event |
|---|---|
| Apr 10 | v1.6.1 ships, first fix for CVE-2026-12530 (range >= 1.1.3, < 1.6.1) |
| Jun 17 | CVE-2026-12530 record published; finder Sergio Garcia credited |
| Jun 19 | GHSA-6rfw-mq36-jm8h published |
| Jul 17 | v1.18.1 ships, fix for the bypass now tracked as CVE-2026-16796 (range < 1.18.1), PR #581 |
| Jul 24 | GHSA-j6g5-3hh3-pgw8 published |
| Sep 28 | BeyondTrust publishes the full research |
The second bypass went through pip's "extras" syntax, the package[extra1,extra2] form, using command substitution where the first fix's validation didn't look. Same function, same sink, second round.
What does the finished fix look like?
The v1.18.1 release note says only "fix: tighten package specifier validation in install_packages()". No CVE number, no mention of injection. You'd never know from the changelog that this was round two.
The fix, per the linked commit and BeyondTrust's write-up, is shlex-based quoting plus a tighter allowlist on what extras syntax is even permitted. That is the correct shape: parse, don't concatenate. If you want to see what happens when a team keeps choosing concatenation, I wrote about two agent sandbox CVEs that landed the same day with the same root vibe.
How do I check my own project in 2 minutes?
Here is the preflight script I now run in CI for anything that depends on the SDK:
# agentcore_preflight.py - exit 1 if the installed SDK is in a vulnerable range
import importlib.metadata as md
FIXES = [("1.1.3", "1.6.1", "CVE-2026-12530"), ("0.0", "1.18.1", "CVE-2026-16796")] # 0.0 = no lower bound
def version_tuple(v):
return tuple(int(p) for p in v.split(".")[:3])
try:
installed = md.version("bedrock-agentcore")
except md.PackageNotFoundError:
raise SystemExit("bedrock-agentcore not installed")
v = version_tuple(installed)
for lo, hi, cve in FIXES:
if version_tuple(lo) <= v < version_tuple(hi):
raise SystemExit(f"VULNERABLE: {cve} affects bedrock-agentcore {installed} (fixed in {hi})")
print(f"OK: bedrock-agentcore {installed} clears both injection CVEs")
It hand-checks each CVE's actual range rather than trusting one blanket "before 1.18.1" claim, because the ranges genuinely differ: 12530 is >= 1.1.3 < 1.6.1, 16796 is < 1.18.1. Run it and then grep your code for the sink:
grep -rn "install_packages" . --include="*.py"
Every call site is a place where a string that started life as model output, a config file, or a user request becomes a command.
Where do these strings actually come from?
In most agent setups I've seen, the package list is not hardcoded. It comes from the model's plan ("I need pandas to analyze this"), or a plugin manifest, or a user request like "can you add requests". That means the trust boundary is wherever the LLM's output touches the parameter. If that framing is familiar, it's the same trap as the Cursor allowlist bypass that started with a file named curl: the string is the attack.
Should the SDK take the blame, or the caller?
Honest trade-off: the sandbox does contain the blast radius, and the maintainers did ship fixes within their release cadence once the issues were reported. The finder got credited in both advisories, which is how coordinated disclosure is supposed to look. But two rounds of the same function suggests the first fix was shaped like a patch, not like a design decision.
What should you take away from this?
Three things, held loosely:
-
Check your version today. v1.18.1 shipped Jul 17 and the current release is v1.24.0, so a plain
pip install -U bedrock-agentcoreclears both CVEs. If you pin versions (and in agent code you should), the preflight script above is your gate. - Treat every SDK function that builds shell commands as a sink. A supply chain backdoor in LiteLLM taught me that the agent gateway is infrastructure; this one teaches the same about the runtime helper functions.
- Watch the research clock, not the patch clock. The first advisory was published in June. BeyondTrust's deep research landed Sep 28, and until this morning nobody had written it up for practitioners. The 73-day gap between fix and public understanding is where unpatched-but-fixed risk lives.
The debate I'd love to have in the comments: is per-delimiter quoting ever a finished fix for injection, or should a function like install_packages() just refuse free-form strings entirely and accept only strict name[extras][version] tokens parsed before they ever touch a shell? The second is stricter and will annoy somebody. The first is friendlier and, in this SDK, it already failed twice.
And a second one: GHSAs in June and July, research in September, and the field was completely empty before this post. Should our watchlists surface "quiet advisory + loud research pending" as its own alert class?
Sources: BeyondTrust Phantom Labs research (Sep 28, 2026) · GHSA-6rfw-mq36-jm8h · GHSA-j6g5-3hh3-pgw8 · NVD records for CVE-2026-12530 and CVE-2026-16796 · aws/bedrock-agentcore-sdk-python releases. Scores were NVD Secondary values, Awaiting Analysis, verified Sep 29, 2026. The preflight script reflects the advisory ranges; I have not run it against a live vulnerable install.
Top comments (0)