This is a bad seam in a custom loading dock, not a hole in every wall
CVE-2022-36760 is an HTTP request smuggling flaw in Apache HTTP Server's mod_proxy_ajp. It affects Apache HTTP Server 2.4.0 through 2.4.54, but only when the server is actually using mod_proxy_ajp to forward requests over ajp:// to a backend such as Tomcat. Plain Apache web serving, and even Apache reverse proxying over normal HTTP/HTTPS instead of AJP, are out of scope.
The 9.0 CVSS score overstates enterprise risk because it prices the *maximum* downstream impact, not the real deployment funnel. Apache itself labeled the issue moderate, which is closer to reality: exploitation needs a specific reverse-proxy architecture, a parser discrepancy that is not trivial to weaponize, and a backend where smuggled requests meaningfully bypass security logic. That's dangerous when present, but it is nowhere near a universal pre-auth internet RCE.
4 steps from start to impact.
Find an Apache frontend that actually uses AJP
mod_proxy_ajp enabled and ajp:// backends configured. Generic Apache exposure is not enough; the vulnerable path lives in the HTTP-to-AJP translation layer. Tooling here is usually recon plus manual fingerprinting, not a one-shot internet census.- Internet-reachable Apache HTTP Server
mod_proxyandmod_proxy_ajploaded- A backend application server reachable through AJP
- Most Apache deployments are not using AJP at all
- AJP is typically an internal backend protocol, so exposure must be inferred from app architecture rather than directly scanned from the edge
- Version-only scanners overcount because config matters as much as package version
httpd < 2.4.55, but config-aware validation must confirm proxy_ajp_module plus ajp:// usage or this finding is mostly noise.Deliver an ambiguous request
- Ability to send raw malformed HTTP requests
- A request pattern that survives Apache normalization and reaches
mod_proxy_ajp
- The CVSS vector itself marks attack complexity as high
- Request smuggling success is highly sensitive to exact frontend/backend behavior
- WAFs, reverse proxies, and intermediary normalizers often break the crafted payload before it becomes a useful backend desync
Content-Length or desync-like requests, but there is no universally reliable signature specific to this CVE.Abuse the backend trust boundary
- Persistent or reusable backend connection behavior
- A backend route or policy that behaves differently once the request is smuggled
- Many backends do not expose a high-value internal route even if smuggling works
- Application auth still matters; smuggling is not automatic app compromise
- Blast radius is often limited to one app path or one virtual host
Turn desync into real impact
- Frontend and backend enforce different security assumptions
- The targeted application has a sensitive route or trust decision behind Apache
- No built-in code execution primitive
- Modern app auth, tenant isolation, and backend authorization checks can sharply reduce impact
- Many enterprises already migrated away from AJP after earlier Tomcat/AJP risk episodes
The supporting signals.
| In-the-wild status | No strong exploitation signal. CISA KEV does not list CVE-2022-36760; in this review I found no authoritative public evidence of widespread active exploitation. |
|---|---|
| Proof-of-concept availability | Low commodity availability. I did not find a mainstream Metasploit or Exploit-DB module for this CVE; exploitation appears to rely on custom desync crafting with generic tooling like Burp/raw sockets rather than turnkey automation. |
| EPSS | 0.0031 from the user-provided intel, which is low and consistent with niche exposure plus higher exploit complexity. |
| KEV status | Not KEV-listed as of this assessment. That removes one of the strongest patch-priority amplifiers. |
| CVSS vector reality check | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H means unauthenticated remote reachability on paper, but it also admits High attack complexity. The C/H/I/H/A/H tail assumes a best-case victim architecture where smuggling crosses a meaningful trust boundary. |
| Affected footprint | Upstream affected: Apache HTTP Server 2.4.0 through 2.4.54. Operationally affected: only systems with both mod_proxy and mod_proxy_ajp enabled and actually proxying to ajp:// backends. |
| Fixed versions | Upstream fix: 2.4.55. Backports exist: Ubuntu marks fixes in packages including 2.4.52-1ubuntu4.3 for Jammy and 2.4.41-4ubuntu3.13 for Focal, so raw upstream version comparisons create false positives. |
| Scanner/exposure caveat | This CVE is hard to census from the internet. Censys/Shodan-style scans can see Apache, but they cannot reliably prove mod_proxy_ajp is in the request path because AJP is backend behavior, not an edge banner. |
| Disclosure and reporting | Disclosed: 2023-01-17. Reported to Apache: 2022-07-12. Finder: ZeddYu_Lu from Qi'anxin Research Institute of Legendsec at Qi'anxin Group. |
| Vendor severity mismatch | Important nuance: Apache's own advisory and oss-security post call this issue moderate, even though the common CVSS baseline circulated for the CVE is 9.0. |
noisgate verdict.
The decisive factor is deployment narrowness: this only matters on Apache estates still using mod_proxy_ajp as a frontend-to-backend bridge, which is a much smaller population than 'all Apache servers'. The second driver is that successful exploitation yields request desync into a backend trust boundary, not a universal one-packet compromise, and the observed exploitation signal is weak.
Why this verdict
- Baseline starts too high: the circulated
9.0score models worst-case backend impact, but Apache itself labeled the issue *moderate*, which already hints the field reality is narrower than the CVSS headline. - Requires a niche attacker path: unauthenticated remote access is not enough by itself; the attacker needs an Apache frontend that is specifically using
mod_proxy_ajpto an AJP backend. That sharply cuts reachable population versus generic Apache exposure. - Implies a post-routing trust-boundary flaw, not instant takeover: the value comes from smuggling requests past frontend assumptions into the backend. That can be serious, but only if the backend exposes a route, cache behavior, or auth decision worth abusing.
- High-complexity parser abuse drags the score down: the exploit path depends on getting a precise frontend/backend interpretation mismatch. Modern WAFs, intermediary normalizers, and small config differences routinely break request smuggling payloads.
- Threat intel is quiet: no KEV listing and a low EPSS (
0.0031) are both downward pressure. If attackers loved this at scale, those signals would usually be louder.
Why not higher?
There is no strong evidence of widespread in-the-wild exploitation, no KEV listing, and no sign of commodity exploit operationalization. More importantly, the vulnerable population is not 'all Apache servers' but the much smaller subset still bridging to AJP backends with exploitable trust assumptions behind them.
Why not lower?
This still deserves more than backlog trivia because it is *unauthenticated and remote* once the right proxy chain exists. In estates that do use AJP, the vulnerability can punch through frontend controls and reach backend-only behavior, which is real security boundary erosion even without a native RCE primitive.
What to do — in priority order.
- Inventory AJP use — Find every Apache host that both loads
proxy_ajp_moduleand referencesajp://inProxyPass,BalancerMember, or included configs. For a MEDIUM verdict there is no mitigation SLA — go straight to the 365-day remediation window, but do this discovery early so you can separate real exposure from version-only scanner noise. - Disable
mod_proxy_ajpwhere unused — If the module is loaded but no live app requires AJP, remove the module and related proxy rules to collapse exposure entirely. There is no mitigation SLA for MEDIUM, so execute in the next normal change window rather than treating it like an emergency outage event. - Migrate AJP backends to HTTP/HTTPS where feasible — Replace
ajp://backend links withhttp://orhttps://reverse proxying when the application stack supports it; that removes this entire bug class from the Apache-to-app hop. For MEDIUM, plan this within the normal engineering backlog and complete it inside the 365-day remediation window. - Constrain backend trust assumptions — Review apps behind Apache that trust the frontend for path filtering, admin-route hiding, or IP-based decisions. Even before patching, reduce reliance on the proxy as the only enforcement point so a smuggled request has less value; with MEDIUM there is no mitigation SLA, but this is worthwhile hardening during routine app reviews.
- Tune detections for desync behavior — Alert on malformed length handling, anomalous backend route access, and frontend/backend log mismatches for AJP-backed apps. This will not prevent exploitation, but it improves visibility while you work through the remediation window.
- A version-only scanner finding does not prove exploitable exposure, because systems not using
mod_proxy_ajpor using distro backports may be flagged anyway. - A generic perimeter WAF is not a dependable fix for request smuggling; parser discrepancies often exist precisely because different layers normalize requests differently.
- Simply hiding the Tomcat/AJP backend from the internet does not solve this CVE, because Apache is the component ferrying the attacker-controlled request into that internal hop.
Crowdsourced verification payload.
Run this on the target Apache host as root or with read access to the Apache config tree and permission to run apachectl -M/httpd -M. Save as check_cve_2022_36760.sh and run sudo bash check_cve_2022_36760.sh or sudo bash check_cve_2022_36760.sh /usr/sbin/httpd if autodetection fails.
#!/usr/bin/env bash
# check_cve_2022_36760.sh
# Detects practical exposure to CVE-2022-36760 on Apache HTTP Server.
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
APACHE_BIN="${1:-}"
find_apache_bin() {
local c
for c in apache2 httpd apachectl apache2ctl; do
if command -v "$c" >/dev/null 2>&1; then
command -v "$c"
return 0
fi
done
return 1
}
ver_ge() {
# returns 0 if $1 >= $2
[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]
}
have_cmd() { command -v "$1" >/dev/null 2>&1; }
if [ -z "$APACHE_BIN" ]; then
if ! APACHE_BIN="$(find_apache_bin)"; then
echo "UNKNOWN - could not find apache/httpd binary"
exit 2
fi
fi
if [ ! -x "$APACHE_BIN" ]; then
echo "UNKNOWN - specified binary not executable: $APACHE_BIN"
exit 2
fi
APACHE_DIRS=(/etc/apache2 /etc/httpd /usr/local/apache2/conf)
CONFIG_HITS=""
MODULES_OUT=""
VERSION_RAW=""
VERSION=""
# Best effort version discovery
VERSION_RAW="$($APACHE_BIN -v 2>/dev/null | awk -F/ '/Server version/ {print $2}' | awk '{print $1}')"
if [ -z "$VERSION_RAW" ] && have_cmd apachectl; then
VERSION_RAW="$(apachectl -v 2>/dev/null | awk -F/ '/Server version/ {print $2}' | awk '{print $1}')"
fi
if [ -z "$VERSION_RAW" ] && have_cmd apache2ctl; then
VERSION_RAW="$(apache2ctl -v 2>/dev/null | awk -F/ '/Server version/ {print $2}' | awk '{print $1}')"
fi
VERSION="$VERSION_RAW"
# Module listing
if "$APACHE_BIN" -M >/dev/null 2>&1; then
MODULES_OUT="$($APACHE_BIN -M 2>/dev/null)"
elif have_cmd apachectl && apachectl -M >/dev/null 2>&1; then
MODULES_OUT="$(apachectl -M 2>/dev/null)"
elif have_cmd apache2ctl && apache2ctl -M >/dev/null 2>&1; then
MODULES_OUT="$(apache2ctl -M 2>/dev/null)"
fi
HAS_PROXY_AJP=0
if printf '%s' "$MODULES_OUT" | grep -qi 'proxy_ajp_module'; then
HAS_PROXY_AJP=1
fi
# Config search for ajp usage
for d in "${APACHE_DIRS[@]}"; do
if [ -d "$d" ]; then
HITS=$(grep -RInE 'ajp://|proxy_ajp_module|LoadModule[[:space:]]+proxy_ajp_module' "$d" 2>/dev/null || true)
if [ -n "$HITS" ]; then
CONFIG_HITS+="$HITS"$'\n'
fi
fi
done
HAS_AJP_CONFIG=0
if [ -n "$CONFIG_HITS" ]; then
HAS_AJP_CONFIG=1
fi
# If AJP is not in use, this CVE is not practically reachable on this host.
if [ "$HAS_PROXY_AJP" -eq 0 ] && [ "$HAS_AJP_CONFIG" -eq 0 ]; then
echo "PATCHED - no evidence of mod_proxy_ajp or ajp:// backend usage"
exit 0
fi
# Upstream version fixed in 2.4.55+
if [ -n "$VERSION" ] && ver_ge "$VERSION" "2.4.55"; then
echo "PATCHED - Apache version $VERSION is upstream-fixed and AJP usage was detected"
exit 0
fi
# Ubuntu/Debian backport handling
if have_cmd dpkg-query; then
PKG_VER="$(dpkg-query -W -f='${Version}' apache2 2>/dev/null || true)"
CODENAME="$(. /etc/os-release 2>/dev/null; echo ${VERSION_CODENAME:-})"
if [ -n "$PKG_VER" ]; then
case "$CODENAME" in
jammy)
if dpkg --compare-versions "$PKG_VER" ge "2.4.52-1ubuntu4.3"; then
echo "PATCHED - Ubuntu Jammy apache2 package $PKG_VER includes the fix/backport"
exit 0
fi
;;
focal)
if dpkg --compare-versions "$PKG_VER" ge "2.4.41-4ubuntu3.13"; then
echo "PATCHED - Ubuntu Focal apache2 package $PKG_VER includes the fix/backport"
exit 0
fi
;;
bionic)
if dpkg --compare-versions "$PKG_VER" ge "2.4.29-1ubuntu4.26"; then
echo "PATCHED - Ubuntu Bionic apache2 package $PKG_VER includes the fix/backport"
exit 0
fi
;;
xenial)
if dpkg --compare-versions "$PKG_VER" ge "2.4.18-2ubuntu3.17+esm8"; then
echo "PATCHED - Ubuntu Xenial apache2 package $PKG_VER includes the fix/backport"
exit 0
fi
;;
esac
# Debian and derivative packages often backport fixes without obvious upstream version numbers.
echo "UNKNOWN - AJP usage detected and apache2 package version is $PKG_VER; verify distro backport status in vendor advisory"
exit 2
fi
fi
# RPM-based distros also commonly backport httpd fixes.
if have_cmd rpm && rpm -q httpd >/dev/null 2>&1; then
RPM_VER="$(rpm -q --qf '%{VERSION}-%{RELEASE}\n' httpd 2>/dev/null || true)"
echo "UNKNOWN - AJP usage detected and RPM package httpd-$RPM_VER may contain a backport; check vendor errata"
exit 2
fi
# Source builds or simple upstream packages: if older than 2.4.55 and AJP is active, treat as vulnerable.
if [ -n "$VERSION" ] && ! ver_ge "$VERSION" "2.4.55"; then
echo "VULNERABLE - Apache version $VERSION with active mod_proxy_ajp/ajp:// usage"
exit 1
fi
echo "UNKNOWN - insufficient data to prove patched state, but AJP usage was detected"
exit 2
If you remember one thing.
httpd < 2.4.55 alert as a fire drill and first identify the hosts that actually use mod_proxy_ajp with ajp:// backends. For confirmed AJP frontends, either remove AJP from the path or apply the vendor-fixed/backported package during the normal cycle; for a MEDIUM verdict there is no noisgate mitigation SLA — go straight to the 365-day remediation window, and the noisgate remediation SLA is ≤ 365 days. If your scan population turns out to have no live AJP use, close the finding as configuration-inapplicable noise rather than spending patch capital like this is another internet-wormable Apache bug.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.