# REPRO-2026-00303: AWS API MCP Server security policy bypass via startup initialization failure ## Summary Status: published Severity: high CVSS: Unknown CWE: CWE-455 Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00303 CVE: CVE-2026-16584 ## Package Name: awslabs.aws-api-mcp-server Ecosystem: pip Affected: >=0.2.13 and <1.3.47; equivalently 0.2.13 through 1.3.46. Fixed: 1.3.47, released on PyPI on 2026-07-22. ## Root Cause ## Summary CVE-2026-16584 is a fail-open initialization bug in `awslabs.aws-api-mcp-server`. Versions before the fix load the read-operations index at server startup for later per-request security-policy decisions. If that startup load fails, the vulnerable server catches the exception, sets `READ_OPERATIONS_INDEX = None`, and continues serving MCP requests. Later `call_aws` requests skip the configured deny/elicitation policy path when the index is absent, allowing an AWS operation that a local security policy was configured to deny to reach the AWS call boundary. ## Impact - **Package/component affected:** `awslabs.aws-api-mcp-server` in the `awslabs/mcp` repository, specifically `src/aws-api-mcp-server/awslabs/aws_api_mcp_server/server.py` and the security-policy path using `core/metadata/read_only_operations_list.py` and `core/security/policy.py`. - **Affected versions:** Submitted claim states `>=0.2.13` and `<1.3.47`. This run anchored the vulnerable build to fixed commit `ab1bbebc097d674c1cdd4bd75a8f313be18473bf^` (`70d8c4cb82601a0e48328e34bf7a81b5a6fba073`) and the fixed negative control to `ab1bbebc097d674c1cdd4bd75a8f313be18473bf`. - **Risk level and consequences:** High. A local actor or MCP client using a server configured with a security policy can cause denied or gated AWS CLI operations to be executed by the server process after the read-operations index fails to initialize. IAM permissions still bound what AWS accepts, but the server-level denyList/elicitList control is bypassed for the process lifetime. ## Impact Parity - **Disclosed/claimed maximum impact:** Authorization/policy bypass for configured denyList or elicitList entries, allowing denied/gated AWS CLI operations to reach the AWS-call boundary after startup initialization failure. - **Reproduced impact from this run:** Full authorization bypass at the claimed CLI/MCP server boundary. The vulnerable server started via the real `awslabs.aws_api_mcp_server.server` stdio entrypoint, logged read-operations index initialization failure, accepted an MCP `call_aws` request for a command present in `denyList`, and performed an HTTP POST to a local fake STS endpoint. The fixed commit failed closed during startup and never reached the fake AWS endpoint. - **Parity:** `full` - **Not demonstrated:** No real AWS account mutation was attempted. The proof uses a local fake AWS HTTP endpoint as the downstream AWS-call boundary to avoid external side effects; the boundary request body and botocore-signed headers demonstrate that the real server execution path reached the AWS client call. ## Root Cause The root cause is fail-open handling of a required security-policy dependency. In vulnerable commit `70d8c4cb82601a0e48328e34bf7a81b5a6fba073`, `main()` always tries to load `READ_OPERATIONS_INDEX = get_read_only_operations()`, but catches any exception, logs `Failed to load read operations index`, sets `READ_OPERATIONS_INDEX = None`, and continues to `server.run(...)`. Later, `call_aws_helper()` only invokes `check_security_policy(ir, READ_OPERATIONS_INDEX, ctx)` when `READ_OPERATIONS_INDEX is not None`. With the index set to `None`, a normal denyList check is skipped unless the separate read-only or mutation-consent modes are enabled. The fix commit is `ab1bbebc097d674c1cdd4bd75a8f313be18473bf` (`fix(aws-api-mcp-server): Security Policy Bypass via Startup Initialization Failure`). It removes the fail-open fallback and changes request-time behavior so an absent `READ_OPERATIONS_INDEX` is treated as a hard denial: `Execution of this operation is denied because the security policy enforcement data failed to initialize.` In the startup path, initialization failure is re-raised, so the stdio MCP server does not finish initialization. ## Reproduction Steps 1. Run `bundle/repro/reproduction_steps.sh`. 2. The script: - Uses `bundle/project_cache_context.json` when present and checks out/reuses `/pruva/project-cache/repo`. - Resolves vulnerable commit `ab1bbebc097d674c1cdd4bd75a8f313be18473bf^` and fixed commit `ab1bbebc097d674c1cdd4bd75a8f313be18473bf`. - Creates isolated source trees for each commit and installs the real package in a Python virtual environment. - Starts the real server via `python -m awslabs.aws_api_mcp_server.server` over MCP stdio. - Writes a real `~/.aws/aws-api-mcp/mcp-security-policy.json` with `denyList: ["aws sts get-caller-identity"]`. - Forces read-operations index initialization failure with an invalid proxy for `servicereference.us-east-1.amazonaws.com` while preserving localhost access. - Sends an MCP `call_aws` tool request for `aws sts get-caller-identity --endpoint-url http://127.0.0.1:`. - Runs two vulnerable attempts and two fixed negative-control attempts. 3. Expected evidence: - Vulnerable attempts show `mcp_initialized=true`, `startup_failure_observed=true`, `fake_aws_boundary_reached=true`, and `policy_denial_observed=false`. - Fixed attempts show startup failure, `mcp_initialized=false`, and `fake_aws_boundary_reached=false`. ## Evidence - `bundle/logs/reproduction_steps.log` contains the full script output, resolved commit IDs, patch-presence excerpts, and per-attempt summaries. - `bundle/logs/repro/summary.json` contains structured results. In the latest run it recorded: - `vulnerable_all_bypassed: true` - `fixed_all_fail_closed: true` - `bundle/logs/repro/vulnerable_attempt_1.json` and `bundle/logs/repro/vulnerable_attempt_2.json` show both vulnerable attempts reached the fake AWS boundary. Each includes a captured POST with body `Action=GetCallerIdentity&Version=2011-06-15`, botocore `Authorization` headers, and a successful MCP tool response for `AWS Security Token Service / GetCallerIdentity`. - `bundle/logs/repro/vulnerable_attempt_1_server.log` and `bundle/logs/repro/vulnerable_attempt_2_server.log` show the server logged `Failed to load read operations index`, then continued to start FastMCP over stdio and handled `call_tool call_aws`. - `bundle/logs/repro/fixed_attempt_1.json` and `bundle/logs/repro/fixed_attempt_2.json` show no fake AWS calls. - `bundle/logs/repro/fixed_attempt_1_server.log` and `bundle/logs/repro/fixed_attempt_2_server.log` show the fixed server raised `RuntimeError: Error retrieving the service reference document...` during startup and closed the MCP connection. - `bundle/repro/runtime_manifest.json` records the runtime-backed entrypoint as `cli_command`, with target path reached and proof artifacts listed. ## Recommendations / Next Steps - Upgrade `awslabs.aws-api-mcp-server` to version `1.3.47` or later, containing fix commit `ab1bbebc097d674c1cdd4bd75a8f313be18473bf`. - Keep the security-policy fail-closed invariant: if read-operations metadata cannot be loaded, do not serve requests and do not execute AWS CLI operations. - Add regression tests that force `get_read_only_operations()` to fail and assert both startup refusal and request-time denial for absent policy-enforcement data. - Ensure operational monitoring alerts on `Failed to load read operations index` or equivalent startup failures. ## Additional Notes - The reproduction script was run successfully twice consecutively after fixing an initial brittle grep preflight. - The proof is idempotent: it creates temporary HOME directories, local fake AWS endpoints, and isolated source archives on each run. - The fake AWS endpoint is intentionally local and side-effect free. It proves the downstream AWS-call boundary is reached without using real cloud credentials or changing AWS resources. ## Reproduction Details Reproduced: 2026-07-27T05:43:09.881Z Duration: 1792 seconds Tool calls: 213 Turns: Unknown Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00303 pruva-verify CVE-2026-16584 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00303&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00303/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-16584 - Source: https://github.com/advisories/GHSA-29w2-fq35-v728 ## Artifacts - bundle/repro/reproduction_steps.sh (reproduction_script, 12773 bytes) - bundle/repro/rca_report.md (analysis, 7569 bytes) - bundle/repro/proof_summary.json (other, 1016 bytes) - bundle/repro/runtime_manifest.json (other, 955 bytes) - bundle/logs/reproduction_steps.log (log, 33595 bytes) - bundle/logs/repro/summary.json (other, 29409 bytes) - bundle/logs/repro/vulnerable_attempt_1.json (other, 9125 bytes) - bundle/logs/repro/vulnerable_attempt_2.json (other, 9125 bytes) - bundle/logs/repro/fixed_attempt_1.json (other, 5235 bytes) - bundle/logs/repro/fixed_attempt_2.json (other, 5235 bytes) - bundle/logs/repro/vulnerable_attempt_1_server.log (log, 4242 bytes) - bundle/logs/repro/fixed_attempt_1_server.log (log, 7861 bytes) - bundle/logs/repro/patch_presence.log (log, 2529 bytes) - bundle/repro/validation_verdict.json (other, 764 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00303 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00303/artifacts/bundle/repro/reproduction_steps.sh - Web: https://pruva.dev/reproductions/REPRO-2026-00303 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev