Breaking Bots: Investigating SQL Injection via AWS Lex Chatbots
kangwijen

How I found the injection path
I found this pattern while building a challenge for ITSEC CTF 2025. I wanted players to exploit a realistic cloud-native attack path, not just another web form. After reading MorattiSec's write-up on red team persistence through AWS Lex chatbots The crow flies at midnight (opens in a new tab), I started looking at Lex as a legitimate attack surface rather than an abstraction layer that removes classic web vulnerabilities.
The challenge is a meeting booking chatbot backed by a relational database. The bot collects a meeting topic, time, and email from the user. The injection vector is the meeting topic slot, which is typed as AMAZON.FreeFormInput. Amazon Lex V2 does not execute SQL. The vulnerability exists entirely in how the Lambda fulfillment function processes that slot value.
Challenge setup
How Lex V2 processes input
Lex V2 classifies user messages into intents (the action the user wants to perform) and extracts slots (the specific data values needed to fulfill that intent). After slots are resolved, Lex invokes an AWS Lambda function and passes the session state, including all slot values, in a structured JSON event.
Most built-in slot types apply normalization. AMAZON.Date converts natural language like next Monday into an ISO date. AMAZON.Number converts five into 5. This normalization creates a natural expectation that Lex is cleaning input before it reaches your backend.
AMAZON.FreeFormInput breaks that expectation entirely. AWS documents it as follows:
AMAZON.FreeFormInputcan be used to capture free form input as-is from the end user. The resolved value is the entire input utterance.
This means whatever the user types is forwarded as-is to Lambda with no transformation, no filtering, and no character restriction.
Request flow
Rendering diagram...
Slot type comparison
| Slot Type | Example User Input | Lex Output Behavior | Security Impact |
|---|---|---|---|
AMAZON.Date | next Monday | Normalized to a standard date format (for example 2025-06-01) | Lower parsing ambiguity in backend logic |
AMAZON.Number | forty two | Normalized to numeric value (for example 42) | Lower type confusion risk |
AMAZON.FreeFormInput | ' OR '1'='1 | Passed through as-is, full utterance unchanged | High risk if concatenated into SQL |
The vulnerable Lambda code
The Lambda fulfillment handler reads the topic slot value from the Lex event and concatenates it directly into a SQL query string.
slots = event["sessionState"]["intent"]["slots"]
topic = slots["Topic"]["value"]["interpretedValue"]
time = slots["Time"]["value"]["interpretedValue"]
email = slots["Email"]["value"]["interpretedValue"]
# VULNERABLE: user-controlled values are concatenated into SQL
query = f"INSERT INTO bookings (topic, time, email) VALUES ('{topic}', '{time}', '{email}')"
cursor.execute(query)When a normal user inputs Team sync, the query becomes:
INSERT INTO bookings (topic, time, email) VALUES ('Team sync', '10:00', 'user@example.com')When an attacker inputs ' OR '1'='1 as the topic, the query becomes:
INSERT INTO bookings (topic, time, email) VALUES ('' OR '1'='1', '10:00', 'user@example.com')Exploitation
The first signal during testing was the raw MySQL error being returned by the bot after I entered a topic starting with a single quote. The response exposed:
Error: (1064, "You have an error in your SQL syntax ...')This confirms two issues at once. User input was reaching SQL construction unmodified, and backend database errors were being leaked directly to the chat UI.
Before the SQLi verification step, I also tested a one-liner fuzz payload for SQLi, XSS, command injection, and SSRF. Most fields were handled better than expected. Date, time, and email rejected malformed values. Recipient name and meeting topic were more permissive, which made them the strongest candidates for deeper SQLi testing.
An error-based SQLi payload path then confirmed the backend database behavior. In the challenge environment, this progressed to schema enumeration and targeted extraction because the vulnerable query path was reachable through the permissive slot and the SQL error was reflected in the bot response.
Fixing the vulnerable query
Use the correct slot type first
Use strict built-in or custom slot types whenever possible. In this challenge, time and email should use constrained slot types, while only truly open-ended fields should use AMAZON.FreeFormInput. Reducing free-form capture narrows the injection surface before the payload reaches Lambda.
Use parameterized queries for all database writes
The correct fix is to bind slot values as parameters rather than interpolating them into the query string. With parameterized queries, the database engine treats the value as data regardless of its content.
# SAFE: slot value is bound as a parameter, not concatenated
query = "INSERT INTO bookings (topic, time, email) VALUES (%s, %s, %s)"
cursor.execute(query, (topic, time, email))With this in place, inputting ' OR '1'='1 as topic produces the literal string ' OR '1'='1 stored in the database column, not an altered query structure.
Why the injection worked
The vulnerability in this challenge exists because AMAZON.FreeFormInput passes user text to Lambda as-is and the Lambda function constructs SQL through string concatenation. Neither Lex nor Lambda prevents this on its own.
If you want the full exploitation chain for the challenge, including payload progression and the exact flag recovery flow, read the official writeup here: ITSEC Asia Summit 2025 Cloud Challenge: rabbit-hole (opens in a new tab)
References
- MorattiSec (Medium). The crow flies at midnight: exploring red team persistence via AWS Lex chatbots (opens in a new tab)
- Amazon Lex V2: What is Amazon Lex V2? (opens in a new tab)
- Amazon Lex V2: Built-in slot types (includes AMAZON.FreeFormInput) (opens in a new tab)
- Amazon Lex V2: AMAZON.FreeFormInput (opens in a new tab)
- Amazon Lex V2: Slots (opens in a new tab)
- Amazon Lex V2: AWS Lambda input event format for Lex V2 (opens in a new tab)
- OWASP SQL Injection Prevention Cheat Sheet (opens in a new tab)
- OWASP Input Validation Cheat Sheet (opens in a new tab)
- ITSEC Asia Summit 2025 Public Challenges: rabbit-hole (opens in a new tab)