Like a bouncer who checks the jacket but ignores the fake badge clipped on with extra tape
CVE-2026-33691 is a whitespace-normalization gap in OWASP CRS file-upload detection. In CRS versions earlier than 3.3.9 and 4.25.0, rules 933110, 933111, and in v4 also 944140, can miss dangerous upload names like photo. php or shell.jsp because the regex sees the space and fails to match the blocked extension. The vulnerable population is any app behind CRS that accepts file uploads and relies on these rules to catch executable extensions.
The vendor's MEDIUM 6.8 is reasonable as a lab score, but for enterprise patch triage it still overstates broad operational risk. This is a *defense bypass* in a rule set, not a direct server-side code execution bug: the attacker also needs a reachable upload feature, weak application-side validation, and a backend or hosting path that turns the whitespace-padded name into an executable file, which the advisory notes is especially practical on Windows and much less so on typical Linux deployments.
4 steps from start to impact.
Find an Internet-facing upload workflow
Burp Suite or ffuf to enumerate file-upload endpoints on an application sitting behind a CRS-backed WAF. The target must actually process user-supplied filenames through the affected CRS rules rather than bypassing them with custom exclusions or alternate ingress paths.- Unauthenticated or low-friction access to an upload endpoint
- Application traffic is inspected by OWASP CRS
- Affected rules are enabled for the upload path
- Many enterprises do not expose arbitrary upload forms publicly
- Some upload paths are authenticated or role-restricted
- Custom WAF exclusions or alternate API gateways may mean the vulnerable rules are never in play
Bypass the filename extension rule
curl or Burp Repeater, the attacker submits a multipart upload with a whitespace-padded filename such as photo. php or shell.jsp , matching the public GitHub advisory and regression-test examples. Because the rule only lowercases the filename and did not remove whitespace before regex evaluation, the dangerous extension check can miss.- CRS version is earlier than
3.3.9or4.25.0 - Target path uses rules
933110/933111or944140 - The request reaches the WAF unchanged
- Some upstream components normalize or rewrite filenames before CRS sees them
- Application-side validation may reject whitespace or rename the file
- Custom local rule edits may already have added
t:removeWhitespace
Land an executable upload on the backend
. php is usually just a non-executable filename oddity unless the application trims it.- Backend or application normalizes/strips whitespace from filenames, or otherwise maps the upload to an executable extension
- Uploaded content is stored in a web-accessible or executable location
- The application does not independently enforce safe extensions/MIME/type policy
- Most mature apps rename uploads to opaque IDs
- Object storage or storage outside webroot breaks the chain
- Many Linux stacks do not execute filenames containing the padded whitespace literally
Trigger code execution or malicious file use
.php/.jsp resource, the attacker can call it with curl, a browser, or a simple web-shell client. At that point the impact stops being a WAF issue and becomes whatever the application host allows, from web-shell execution to integrity loss in a single app tier.- Executable interpreter mapping exists for the landed file type
- The file is reachable after upload
- No downstream controls block execution
- Non-executable upload stores turn this into a harmless bypass with no code path
- EDR, application allowlists, or hardened web-server mappings may stop the follow-on stage
- Blast radius is usually limited to the specific app and upload path
The supporting signals.
| In-the-wild status | No evidence found of active exploitation campaigns. CISA KEV does not list CVE-2026-33691 as of this assessment. |
|---|---|
| Proof-of-concept availability | Public exploit guidance is effectively available in the vendor advisory and PRs: example payloads include photo. php, photo.php , test. php.jpg, and shell.jsp , plus regression tests in PRs #4546, #4547, and #4548. |
| EPSS | User-supplied EPSS is 0.00031 (~0.03% next-30-day exploitation probability). That is very low; an exact percentile was not retrieved from primary sources during this assessment. |
| KEV status | Not KEV-listed. No emergency override applies from KEV evidence. |
| CVSS baseline mismatch | GitHub CNA: 6.8 MEDIUM with AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N; NVD also publishes a higher 7.5 HIGH view. The gap comes from how much environment-dependent exploitation friction each scorer assumes. |
| Affected versions and rules | Affected versions are < 3.3.9 and < 4.25.0. The GitHub advisory names rules 933110 (PL1), 933111 (PL3), and 944140 (PL1), and states all paranoia levels up to PL4 are affected. |
| Fixed versions | Patched upstream in 3.3.9 and 4.25.0 by adding t:removeWhitespace. Debian shows modsecurity-crs fixed in 3.3.9-1 for sid/forky, while older Debian releases mark this as a minor issue rather than pushing a DSA. |
| Exposure reality | This is not a directly fingerprintable Internet service flaw. Exposure equals the subset of Internet-facing apps behind CRS that also offer file uploads and still depend on WAF-side filename blocking as a meaningful control. |
| Disclosure timeline | Reported to the project on 2026-03-10, GitHub advisory published 2026-03-28, NVD publication on 2026-04-02. |
| Reporter / remediation | Reported by @HackingRepo (KP3-260311); remediation work is credited to fzipi in the CRS project. |
noisgate verdict.
The decisive downgrading factor is that this is only a *WAF detection bypass* until the environment adds multiple extra weaknesses: an exposed upload path, no meaningful application-side validation, and backend filename normalization that turns the padded name into something executable. That sharply narrows both the reachable population and the real blast radius compared with a true pre-auth RCE or auth bypass in the app or server itself.
Why this verdict
- Defense-bypass, not direct code execution: CRS fails to catch a bad filename pattern, but the attacker still needs the application and hosting stack to mishandle the uploaded file after the WAF lets it through.
- Environment dependency cuts the population hard: the vendor advisory explicitly says practical exploitation is common on Windows-style whitespace normalization and much harder on Linux unless the application trims filenames itself.
- Reachability is narrower than the CVSS baseline implies: only Internet-facing or attacker-reachable upload endpoints behind affected CRS versions are in scope, and many enterprise uploads are authenticated, renamed, stored outside webroot, or pushed to object storage.
- Threat intel is cold: no KEV listing, no public campaign evidence, and the supplied
EPSS 0.00031is near the floor.
Why not higher?
This is not a one-request server takeover. The attacker must chain the CRS miss with a weak upload implementation and a backend that converts the padded filename into an executable or otherwise dangerous artifact, which is a lot of real-world friction.
Why not lower?
It is still remotely testable and requires no local foothold. In estates with legacy IIS/Windows upload handlers or sloppy filename normalization, this bypass can be the last step that turns an otherwise blocked web-shell upload into a real compromise.
What to do — in priority order.
- Rename uploads server-side — Force every upload to a random safe name and store the original filename only as metadata. This breaks the exploit chain regardless of CRS behavior; for a
LOWverdict there is no fixed SLA date, so fold this into normal app-hardening work rather than emergency change control. - Store uploads outside webroot — Make uploaded files non-executable by design and serve them indirectly through the application or object storage. That removes the main impact path entirely; for
LOW, treat this as backlog hygiene in the next routine platform hardening cycle. - Add local
t:removeWhitespaceto affected rules — If you cannot update CRS immediately, patch the local copies of rules933110,933111, and944140as the vendor advisory recommends. ForLOW, there is no mitigation SLA, so do it in the next ordinary WAF policy maintenance window. - Reject whitespace-padded executable names in the app — Application-side filename validation should reject suspicious patterns like spaces around dangerous extensions and should not trust multipart
filename=values. ForLOW, implement during normal developer backlog work, prioritizing any public upload workflow on Windows-backed apps.
- Raising CRS paranoia level doesn't help; the advisory says all paranoia levels up to
PL4are affected. - Relying on EDR alone is too late; EDR may catch web-shell execution, but it does not prevent the upload bypass itself.
- MIME-type checks by themselves are weak if the app still permits executable storage paths or trusts client-supplied metadata.
Crowdsourced verification payload.
Run this on the WAF or reverse-proxy host where CRS rule files are actually deployed, not from a scanner box. Invoke it as sudo bash check-cve-2026-33691.sh; root is recommended so it can read packaged rule locations under /etc, /usr/share, or /opt.
#!/usr/bin/env bash
# check-cve-2026-33691.sh
# Detects whether deployed OWASP CRS rules for CVE-2026-33691 include t:removeWhitespace.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
SEARCH_DIRS=(
"/etc/modsecurity"
"/etc/crs4"
"/usr/share/modsecurity-crs"
"/usr/share/owasp-modsecurity-crs"
"/opt/owasp-crs"
"/opt/coreruleset"
)
found_dirs=()
for d in "${SEARCH_DIRS[@]}"; do
if [ -d "$d" ]; then
found_dirs+=("$d")
fi
done
if [ ${#found_dirs[@]} -eq 0 ]; then
echo "UNKNOWN - no common CRS directories found"
exit 2
fi
find_rule_context() {
local rule_id="$1"
local hit file line start end
while IFS= read -r hit; do
file="${hit%%:*}"
line_rest="${hit#*:}"
line="${line_rest%%:*}"
if [[ "$line" =~ ^[0-9]+$ ]]; then
start=$(( line > 12 ? line - 12 : 1 ))
end=$(( line + 25 ))
sed -n "${start},${end}p" "$file"
return 0
fi
done < <(grep -R -n -E "id:?${rule_id}([, ]|$)" "${found_dirs[@]}" 2>/dev/null)
return 1
}
rule_status() {
local rule_id="$1"
local ctx
if ! ctx="$(find_rule_context "$rule_id")"; then
echo "NOTFOUND"
return 0
fi
if grep -q "t:removeWhitespace" <<< "$ctx"; then
echo "PATCHED"
else
echo "VULNERABLE"
fi
}
r933110="$(rule_status 933110)"
r933111="$(rule_status 933111)"
r944140="$(rule_status 944140)"
# Need the PHP rules to make a determination.
if [ "$r933110" = "NOTFOUND" ] || [ "$r933111" = "NOTFOUND" ]; then
echo "UNKNOWN - affected rule IDs 933110/933111 not found in deployed CRS content"
exit 2
fi
# v4 installs normally include 944140; v3 does not.
if [ "$r944140" = "NOTFOUND" ]; then
if [ "$r933110" = "PATCHED" ] && [ "$r933111" = "PATCHED" ]; then
echo "PATCHED - v3-style rule set appears fixed (933110/933111 include t:removeWhitespace)"
exit 0
else
echo "VULNERABLE - v3-style rule set missing t:removeWhitespace in 933110 and/or 933111"
exit 1
fi
else
if [ "$r933110" = "PATCHED" ] && [ "$r933111" = "PATCHED" ] && [ "$r944140" = "PATCHED" ]; then
echo "PATCHED - v4-style rule set appears fixed (933110/933111/944140 include t:removeWhitespace)"
exit 0
else
echo "VULNERABLE - v4-style rule set missing t:removeWhitespace in one or more affected rules (933110/933111/944140)"
exit 1
fi
fi
If you remember one thing.
LOW verdict, the noisgate mitigation SLA does not apply and the noisgate remediation SLA also does not impose a hard date, so this is backlog hygiene unless your review shows CRS is the only thing stopping executable uploads on a real exposed app.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.