Skip to content

fix(appsec): urldecode before lowercase on raw zones - #1872

Open
mazzma12 wants to merge 2 commits into
masterfrom
fix/appsec-transform-order-raw-zones
Open

fix(appsec): urldecode before lowercase on raw zones#1872
mazzma12 wants to merge 2 commits into
masterfrom
fix/appsec-transform-order-raw-zones

Conversation

@mazzma12

@mazzma12 mazzma12 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

transform: [lowercase, urldecode] lowercases before decoding, so a percent-escape survives the fold and decodes to an uppercase letter afterwards, which the lowercase match then misses. Swapped to [urldecode, lowercase].

These 5 rules use zones Coraza does not pre-decode (COOKIES, HEADERS, URI_FULL, RAW_BODY), so a single percent-encoding is enough to evade them. Checked against the Coraza engine: for CVE-2026-46725, Cookie: t3_ceselector_x=%4F:8:"stdClass" does not match today and matches after the swap. PHP urldecodes $_COOKIE, so that payload still reaches unserialize().

Same root cause as crowdsecurity/alert-context-rules#1107. A second PR covers the remaining 82 files, which use pre-decoded zones and need double encoding to bypass.

🤖 Generated with Claude Code

https://claude.ai/code/session_018kCVups4eddfjUonQomPcs

Copilot AI lite review requested due to automatic review settings September 2, 2026 14:04
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

Hello @mazzma12,

✅ The new VPATCH Rule is compliant, thank you for your contribution!

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

Hello @mazzma12,

Scenarios/AppSec Rule are compliant with the taxonomy, thank you for your contribution!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The behavior change should be accompanied by appsec regression tests that specifically cover the percent-encoded-alpha bypass case that this PR fixes.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR hardens several CrowdSec appsec virtual patching rules against a percent-encoding evasion where lowercase ran before urldecode, allowing encoded bytes to decode into uppercase characters after case-folding and bypass lowercase-based matches.

Changes:

  • Reorders transform steps from [lowercase, urldecode] to [urldecode, lowercase] for rules operating on raw/un-decoded Coraza zones (e.g., URI_FULL, COOKIES, HEADERS, RAW_BODY).
  • Applies the fix across five CVE virtual patch rules where a single percent-encoding could previously evade detection.
File summaries
File Description
appsec-rules/crowdsecurity/vpatch-CVE-2026-63030.yaml Reorders URI/body transform pipeline to decode before lowercasing for REST route confusion / SQLi detection.
appsec-rules/crowdsecurity/vpatch-CVE-2026-46725.yaml Reorders cookie transform pipeline to decode before lowercasing for TYPO3 ceselector object injection detection.
appsec-rules/crowdsecurity/vpatch-CVE-2026-41940.yaml Reorders cookie transform pipeline to decode before lowercasing for WHM auth-bypass cookie pattern detection.
appsec-rules/crowdsecurity/vpatch-CVE-2024-8181.yaml Reorders URI transform pipeline to decode before lowercasing for Flowise auth-bypass endpoint detection.
appsec-rules/crowdsecurity/vpatch-CVE-2019-5418.yaml Reorders header transform pipeline to decode before lowercasing for Rails Accept-header traversal detection.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 8 to +10
transform:
- lowercase
- urldecode
- lowercase

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in c99a962: second request GET /%41pi/v1/apikey?/api/v1/ping. Verified against the Coraza engine — misses with [lowercase, urldecode], matches with [urldecode, lowercase].

Comment on lines 9 to 12
transform:
- lowercase
- urldecode
- lowercase
- trim

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in c99a962: fourth request with whostmgrsession=%3a%51SJN_sFdKZtCi2o_ and status_code_4 == 403. %51 decodes to Q, which fails ^:[a-z0-9_]+,?$ under the old order. The existing negative case (status_code_3 == 200) still returns 200 after the swap.

Comment on lines 8 to +10
transform:
- lowercase
- urldecode
- lowercase

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, the existing O%3A... payload matched under both orders. Added in c99a962: second request with T3_ceselector_1=%4F%3A28%3A..., which misses with the old order and matches with the new one.

- and:
- zones: [URI_FULL]
transform: [lowercase, urldecode]
transform: [urldecode, lowercase]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in c99a962: second request POST /?rest_route=/%42atch/v1 with the same JSON body, so the RAW_BODY half of the conjunction still holds. %42 decodes to B, so contains: batch/v1 fails under the old order.

@mazzma12

mazzma12 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

All four review comments addressed in c99a962.

One rule has no regression request: vpatch-CVE-2019-5418 matches on ../, which contains no letters, so the transform order cannot affect it (%2F and %2f decode identically). It is in this PR for consistency only, not as a live bypass.

I also changed the single-request tests from type: status to a per-request type: dsl matcher. hubtest treats any nuclei finding as a pass, so simply appending a request would not have failed the test if that request stopped matching — the status_code_N assertions make it a real regression guard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants