diff --git a/CVE-CHANNEL.md b/CVE-CHANNEL.md index 06711e4..75ff5db 100644 --- a/CVE-CHANNEL.md +++ b/CVE-CHANNEL.md @@ -57,6 +57,60 @@ only one it may clear, which is also what makes a rule **withdrawable**: remove it, bump the stamp, and it stops being enforced everywhere within the hour. Publishing an empty `"rulesets": []` withdraws the lot. + +## Importing from a Nuclei template + +A CVE record describes a vulnerability, not a request, which is why this +channel is written by hand. A **Nuclei template** does contain the request — +path, query, headers, body — because that is what it sends. So for thousands of +CVEs the shape a virtual patch needs is already written down. + + go run ./tools/nuclei-importer -cves CVE-2025-31161,CVE-2024-4577 \ + -merge edgeguard-rules/cve.json > /tmp/next.json + cp /tmp/next.json edgeguard-rules/cve.json + bash tools/publish-cve-feed.sh --check + +**Imported rules land as `action: "log"`, and that is not timidity.** A template +decides whether a target was actually vulnerable by reading the RESPONSE — +words in the body, the content type, the status. A WAF decides before a +response exists, so the imported rule is strictly weaker than the template it +came from: the template confirms exploitation, the rule only recognises the +attempt. Watch what it would have caught in your own traffic, then change +`log` to `block` deliberately, per rule. + +Each imported rule carries an `origin` field saying a human did not write its +pattern and what it came from. Do not remove it — it is the difference between +a rule somebody reasoned about and one a converter guessed. + +### What it refuses to do + +It skips rather than guesses, and prints why: + +- **A CVE the engine already patches in code.** Both rules would fire and the + scores would add. Five are patched today: the two PHP2Shell ones, Log4Shell + (two) and Spring4Shell. +- **A request with nothing literal in it.** Templates are full of + `{{BaseURL}}` and `{{string_1}}`; a pattern that is mostly `.*` matches + ordinary traffic. +- **A request whose longest literal is under twelve characters**, or is the + site root. A rule on `/` matches everything. + +### What it corrects, and why you should check anyway + +It strips one leading path segment when three remain, because a template is +written against a laboratory where the application is mounted somewhere: the +phpunit template requests `/laravel52/vendor/phpunit/...` and `laravel52` is +that lab's directory. Without stripping it the rule is published, enabled, and +unable to match a real attack at `/vendor/phpunit/...`. + +It prefers a header value carrying an injection marker (`${jndi:`, `<%`, `$(`, +`../`) over a path, and ignores standard headers entirely whatever their value. +An earlier version preferred headers over everything and produced the pattern +`Content-Type: text/html` for a PHP RCE — a rule that says nothing about the +attack and something about every request. + +None of that makes the output right. Read every pattern before publishing. + ## Targets The usual ones (`uri`, `query`, `args`, `body`, `headers`, `user_agent`,