Breaking the AWS MCP Python Sandbox
أساليب الهجوم وتقنياته2026-10-28, 07:46
AWS MCP Server is a managed remote Model Context Protocol (MCP) server that lets AI agents call AWS tools. The
aws___run_script tool accepts a short Python script and runs it in an AWS Lambda sandbox. The script can combine multiple AWS API calls, filter responses, and process the result in a single request.The sandbox restricts imports, built-in functions, attribute access, and file operations. However, these restrictions can be bypassed to achieve system command execution inside the Lambda environment.
The submitted code passed through two layers of protection:
- Static AST validator — inspected the script's Abstract Syntax Tree (AST) and rejected suspicious operations before execution, such as reading dunder attributes (
obj.__class__), callinggetattr(), or importingoperatorto useoperator.attrgetter(). - Runtime layer — replaced built-ins with restricted implementations or proxy objects. For example,
type()was replaced with_safe_type(), which returned restricted proxy classes that blocked access to sensitive attributes.open()was also restricted, whilegetattr()was blocked.
Exploitation chain:
- Bypassing the string restriction. The validator rejected dunder names written as plain strings, so the name was assembled at runtime using
chr():chr(95)2 + "class" + chr(95)2. - Obtaining a class. Defining a class normally was not possible in the sandbox, but one could be created through
collections.namedtuple(), which internally usedtype(...)and allowed__match_args__to be reassigned. - Recovering a real function.
__match_args__was set to__reduce_ex__. Calling this method withpickleprotocol4returned a recipe for reconstructing the object. Its first element wascopyreg.__newobj__— a real Python function, not a proxy. - Accessing global names. A function object was needed for
isinstance()to verify that the object was a function. The proxy returned by_safe_type()for functions did not inherit from the immutable function type; instead, it was built from scratch and was therefore mutable. Its__match_args__was set to("__globals__",), allowing a class pattern to extract__globals__from the real function. - Breaking into imports.
__globals__contained the original__builtins__dictionary, including the real__import__function. This was used to loadosandsubprocess.
Results:
• System command execution inside the Lambda container as the Lambda user:
uid=993(sbx_user1051)
• Access to environment variables containing the Lambda execution role credentials: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN
• Filesystem read access, including access to the sandbox's own source codeLimitations
The recovered credentials belonged to an IAM role in an AWS-owned account, not a customer account. They were explicitly denied for AWS API calls other than
sts:GetCallerIdentity, which is always available. No access to other sessions or customer data was found — the escape was confined to the Lambda container created for the session.المورّدون
المنتجات
المزيد