Detect pathname expansion with consecutive `?`
Motivation
CRS currently doesn't detect shell pathname expansions like /???/??t specifically. This can enable bypasses.
Proposed solution
Add a new rule / update existing rule to match consecutive ? used in shell pathname expansion.
Spent a bit more time looking into this.
We do already have rule 932190 @ PL3 to help. It has this search pattern:
/(?:[?*]+[a-z/]+|[a-z/]+[?*]+)
The problem is, that matches against natural language like:
Any plans for today/tomorrow? (Match: /tomorrow? )
I think we can craft a new pattern that will safely work at PL2 (at least to start with) that will also avoid issues with natural language use of / and ?
E.g.:
Good:
-
Preference on using zsh/bash?
Bad (never used in natural language):
-
/b??h -
/b?s?
The last character looks to be important. In natural language, only the last character makes sense to be a question mark. In any other position, a question mark makes a word suspicious immediately. If we adjust the rule to ignore question marks in the last position, it's probably easy to bypass, but it should catch simple probing at PL1.
This should be expanded to:
/(?:[?*]+[a-z/]+|[a-z/]+[?*]+|[a-z]+[?*]+[a-z/]+|[?*]+[a-z/]+[?*]+)
That won't do anything. The added expressions are more restrictive than the original ones, so a match will always occur in one of the first two branches and the other branches will never be evaluated.
That won't do anything. The added expressions are more restrictive than the original ones, so a match will always occur in one of the first two branches and the other branches will never be evaluated.
curl -H "x-format-output: txt-matched-rules" "https://sandbox.coreruleset.org/?test=%2F%3Fi%3F%2Ff%3Fe%3F"
This should be expanded to:
/(?:[?*]+[a-z/]+|[a-z/]+[?*]+|[a-z]+[?*]+[a-z/]+|[?*]+[a-z/]+[?*]+)
I made a typo in the above, but this fixes the above:
/(?:[a-z]+[?*]+[a-z/?*]+|[?*]+[a-z]+[?*a-z/]+)
Could be improved. 😊
That's pretty good! Could you open a PR with a couple of tests?
@theseion #4246
Ok, reading the glob manpage. I think we might be covered.
- tested with square brackets
/[e][t][c]/[r][m][t]:
curl -ig -H "x-format-output: txt-matched-rules" -H "x-crs-paranoia-level: 3" --data 'payload=;/[e][t][c]/[r][m][t]' https://sandbox.coreruleset.org
HTTP/1.1 403 Forbidden
Date: Fri, 22 Aug 2025 12:25:31 GMT
Content-Type: text/plain; charset=iso-8859-1
Transfer-Encoding: chunked
Connection: keep-alive
X-Unique-ID: aKhhu_LBpCBJ9rXwm6FBiQAAAAM
x-backend: apache-latest
x-crs-last-commit: none
932130 PL1 Remote Command Execution: Unix Shell Expression Found
932240 PL2 Remote Command Execution: Unix Command Injection evasion attempt detected
942430 PL2 Restricted SQL Character Anomaly Detection (args): # of special characters exceeded (12)
942431 PL3 Restricted SQL Character Anomaly Detection (args): # of special characters exceeded (6)
949110 PL1 Inbound Anomaly Score Exceeded (Total Score: 16)
980170 PL1 Anomaly Scores: (Inbound Scores: blocking=16, detection=16, per_pl=5-8-3-0, threshold=5) - (Outbound Scores: blocking=0, detection=0, per_pl=0-0-0-0, threshold=4) - (SQLI=6, XSS=0, RFI=0, LFI=0, RCE=10, PHPI=0, HTTP=0, SESS=0, COMBINED_SCORE=16)
Other possible combinations we have covered, from what I see.
@theseion I think trying to implement the changes wasn't exactly beneficial for this case, since 932190 is at PL3 and targets only ARGS. But the suggestion I made could be useful for implementing maybe at PL2?