Skip to content
Security Article

How Serverless SQL Injection Becomes Cloud Privilege Escalation

When application-level vulnerabilities meet over-privileged IAM roles, classic database exploits can compromise entire AWS environments.

Emeka Okafor
Emeka Okafor
Security Editor · Jul 9, 2026 · 5 min read
How Serverless SQL Injection Becomes Cloud Privilege Escalation

Developers often treat serverless functions as isolated, ephemeral execution environments. The common wisdom suggests that because there is no server to patch or maintain, the attack surface is dramatically smaller. But this abstraction is deceptive. In a serverless architecture, the security boundary shifts from the network and operating system to Identity and Access Management (IAM).

When an application-level vulnerability like SQL injection occurs inside an AWS Lambda function, it does not just expose the database. If that Lambda function has permissions to modify IAM states, a simple SQL injection can escalate into a full cloud account compromise. This is not a theoretical threat; it is a highly exploitable pattern that highlights the danger of over-privileged service roles.

The Anatomy of the Exploit: A Confused Deputy in Code

Consider a scenario where an organization uses a Lambda function to manage user access policies. The function queries a database to verify if a requested policy is marked as public before attaching it to a user.

The core vulnerability lies in how the database query is constructed. In a recent vulnerability lab demonstration, the Python handler for the Lambda function used direct string interpolation to build its SQL query:

def handler(event, context):
    target_policys = event['policy_names']
    user_name = event['user_name']
    
    for policy in target_policys:
        statement = f"select policy_name from policies where policy_name='{policy}' and public='True'"
        for row in db.query(statement):
            # If a row is returned, the policy is considered valid and public
            response = iam_client.attach_user_policy(
                UserName=user_name,
                PolicyArn=f"arn:aws:iam::{account_id}:policy/{row['policy_name']}"
            )

This is classic SQL injection. Because the user-supplied policy input is concatenated directly into the query string, an attacker can break out of the SQL syntax.

By passing the payload SecretAccessPolicy' OR 1=1 --, the query executed by the database becomes:

SELECT policy_name FROM policies WHERE policy_name='SecretAccessPolicy' OR 1=1 --' AND public='True'

The double dash comments out the remainder of the query, completely neutralizing the security check that restricts results to public policies. The OR 1=1 condition guarantees that the database returns rows, including the restricted SecretAccessPolicy.

Because the query returns a valid row, the Lambda function blindly proceeds to execute iam_client.attach_user_policy on behalf of the attacker, attaching the highly privileged policy to the attacker's low-privilege user account. This is a textbook confused deputy attack: the attacker lacks the permission to attach the policy directly, but they trick a privileged helper (the Lambda function) into doing it for them.

The Privilege Escalation Chain

In a real-world assessment, an attacker uses this vulnerability as a stepping stone to access sensitive resources. Using tools like Pacu, an open-source AWS exploitation framework, an attacker can automate the discovery and exploitation of these misconfigurations.

Pacu > run iam__enum_users_roles_policies_groups

This reconnaissance phase maps out the available roles and policies. If the attacker identifies a role that they can assume via sts:AssumeRole (such as an invoker role for the vulnerable Lambda), they can pivot. Once they assume the invoker role, they enumerate the Lambda functions in the target region, locate the vulnerable function, and invoke it with the malicious payload:

aws lambda invoke \
  --function-name Vulnerable-lambda-sqli-privesc-to-access-secret \
  --payload fileb://payload.json \
  output.txt \
  --region us-east-1

With the SecretAccessPolicy now attached to their user account, the attacker gains direct access to AWS Secrets Manager. They can then run a secrets enumeration module to extract sensitive credentials, such as database passwords or API keys, completely bypassing the original access controls.

Pacu > run secrets__enum --region us-east-1

The Developer Angle: Hardening the Serverless Boundary

Defending against this attack vector requires a defense-in-depth approach that addresses both the application code and the surrounding cloud infrastructure.

1. Parameterize Your Queries

First and foremost, never use string interpolation or concatenation for database queries. Even when using lightweight databases like SQLite inside a Lambda function, always use parameterized queries or an ORM that handles sanitization automatically.

# Secure parameterized query
statement = "select policy_name from policies where policy_name=? and public='True'"
for row in db.query(statement, (policy,)):
    # Proceed with policy attachment

2. Enforce the Principle of Least Privilege on Lambda Roles

The Lambda function in this scenario possessed the iam:AttachUserPolicy permission. This is an incredibly dangerous permission to grant to an application-facing service. If a Lambda function must manage permissions, restrict its execution role so that it can only attach a highly specific, pre-approved list of policies, rather than allowing wildcard access to all policies in the AWS account.

3. Implement IAM Permission Boundaries

To mitigate the impact of a compromised user account gaining unexpected policies, developers and cloud architects should implement IAM Permission Boundaries. A permission boundary is a managed policy that sets the maximum permissions that an identity-based policy can grant to an IAM entity.

Even if the vulnerable Lambda successfully attaches the SecretAccessPolicy to the attacker's user, the attacker will not be able to use those permissions if they fall outside the limits defined by the user's permission boundary.

4. Re-evaluate the Architecture

Dynamic policy attachment is often an architectural red flag. Instead of modifying IAM policies on the fly based on database state, consider using temporary credentials, AWS Security Token Service (STS), or Amazon Cognito group memberships to manage user access. Modifying static IAM policies programmatically at runtime introduces unnecessary statefulness and risk to what should be stateless, secure serverless workflows.

The Bottom Line

Serverless security is only as strong as the code running inside the container. When developers rely on cloud providers to secure the infrastructure, they must be doubly vigilant about the permissions they grant to their code. A single unparameterized query in a Lambda function can turn a minor code defect into a total keys-to-the-kingdom cloud compromise.

Sources & further reading

  1. [LAB] Lambda SQLi PrivEsc to Access Secret (AWS Red Teaming) — dev.to
  2. Cloud Red Teaming: AWS Initial Access & Privilege Escalation - Speaker Deck — speakerdeck.com
  3. AWS IAM Privilege Escalation – Methods and Mitigation — rhinosecuritylabs.com
  4. AWS Cloud Red Team Specialist [CARTS] - CyberWarFare Labs — cyberwarfare.live
Emeka Okafor
Written by
Emeka Okafor · Security Editor

Emeka has spent over a decade tracking threat actors, vulnerability disclosures, and the evolving landscape of application security, bringing a sharp continent-spanning perspective to his reporting. He's known for translating dense CVE advisories into clear, actionable context that developers and security teams alike actually read.

Discussion 0

Join the discussion

Sign in or create an account to comment and vote.

No comments yet

Be the first to weigh in.

Related Reading