Like a fire alarm that only malfunctions during a power outage you're already dealing with
CVE-2022-43680 is a use-after-free in libexpat through version 2.4.9. When XML_ExternalEntityParserCreate creates a child parser that shares the parent's DTD, and a subsequent memory allocation inside parserInit fails (OOM), XML_ParserFree incorrectly destroys the shared DTD because the m_isParamEntity flag hasn't been set yet. The parent parser is left holding a dangling pointer. Exploiting this yields a crash — denial of service only — with no path to code execution or data disclosure per the CVSS vector (C:N/I:N/A:H). Affected: libexpat ≤ 2.4.9. Fixed in libexpat 2.5.0. Distro backports: Debian Buster 2.2.6-2+deb10u6, Bullseye 2.2.10-2+deb11u5, Bookworm 2.5.0-1+deb12u4; RHEL/CentOS patches shipped via expat package updates in RHSA advisories.
NVD rates this HIGH / 7.5, but that score is misleading for prioritization. The CVSS vector (AV:N/AC:L) treats attack complexity as Low, yet the actual trigger — an out-of-memory condition on the target — is something the attacker almost never controls remotely. You can't just send a crafted XML document and trigger this; you need the host to be memory-starved *at the moment* the external entity parser is created. Red Hat correctly downgraded this to Moderate, noting it "is not possible to exploit on every possible system." The impact ceiling is a crash, not RCE. With no PoC, no KEV listing, no in-the-wild exploitation, and an EPSS of ~2.5%, this is a backlog-priority patch, not an emergency.
4 steps from start to impact.
Identify XML-parsing service using libexpat
XML_ExternalEntityParserCreate, meaning the application must be configured to process external entities — many hardened deployments disable XXE processing entirely.- Network-reachable service accepting XML input
- Service uses libexpat ≤ 2.4.9
- Application processes external entities (XXE not disabled)
- Most modern XML configurations disable external entity processing by default
- Many services use libxml2 or other XML parsers, not libexpat
- Service may not be network-facing
dpkg -l libexpat1 or rpm -q expat. Qualys QID 354821, Nessus plugin for libexpat version check. No network-level signature because exploitation requires OOM coincidence.Send XML with external entity reference
XML_ExternalEntityParserCreate. This creates a child parser that shares the parent's DTD. The XML itself is not malicious in a traditional sense — it's a legitimate-looking document that exercises the external entity path.- Ability to send arbitrary XML to the service
- Service resolves external entities
- WAFs and XML gateways commonly block or strip external entity declarations
- Application-level XML processing may reject DTDs entirely
Target must enter out-of-memory state
parserInit fails a memory allocation — specifically copyString(encodingName, ...). This requires the host to be in or near an out-of-memory condition at the precise moment the child parser is being initialized. The attacker does not control this condition via the XML payload alone. They would need a separate memory exhaustion vector or hope the system is already resource-starved.- Target host is in or near OOM condition during parser creation
- Attacker has separate memory exhaustion capability OR system is already degraded
- OOM conditions are rare on properly provisioned servers
- Linux OOM killer typically terminates processes before reaching this state
- An attacker with memory exhaustion capability already has a DoS vector, making this bug redundant
- Timing the OOM with the exact parser initialization is extremely unreliable
Crash via use-after-free
XML_ParserFree destroys the shared DTD. The parent parser subsequently accesses the freed DTD memory, causing a crash. The impact is process termination — a denial of service. There is no known technique to leverage this use-after-free for code execution; the freed structure is a DTD, and the dangling pointer dereference leads to a segfault, not a controllable write.- Steps 1-3 all succeed simultaneously
- Process restart mechanisms (systemd, supervisord, container orchestrators) typically recover the service within seconds
- No evidence this can be escalated beyond DoS
defusedxml or set XML_SetExternalEntityRefHandler to a no-op. In Apache: ensure mod_dav is not exposed or disable entity expansion. This eliminates the code path to XML_ExternalEntityParserCreate entirely. No mitigation SLA for MEDIUM — go straight to the 365-day remediation window for the actual patch.expat / libexpat1 package to the fixed version. This is the definitive remediation. Per the noisgate remediation SLA for MEDIUM, complete within 365 days. Given that patches have been available since late 2022, most environments should already be patched — verify and close. apt list --installed 2>/dev/null | grep -i expat or rpm -qa | grep expat to audit.Restart=always, Kubernetes with restart policy, Docker with --restart=unless-stopped). This doesn't prevent the crash but limits the DoS window to seconds. This is defense-in-depth, not a primary control.- WAF XXE rules — While WAF rules for XXE (OWASP CRS 200007/200008) block many external entity payloads, they don't address this CVE specifically because the vulnerability requires an OOM state, not a specially crafted XML payload. The XML itself can be benign-looking.
- Memory limits / cgroups — Setting memory limits on containers or cgroups might seem relevant, but lower memory limits actually *increase* the likelihood of OOM conditions, potentially making the bug *easier* to reach, not harder.
- Network segmentation — Reducing attack surface is always good, but libexpat is embedded in so many tools (Git, Python, PHP) that internal services are equally likely to link against it. Segmentation doesn't address the library-level defect.
The supporting signals.
| In-the-wild exploitation | None observed. Not listed in CISA KEV. No campaigns, threat actor TTPs, or incident reports reference this CVE. Zero GreyNoise or Shadowserver hits related to this specific bug. |
|---|---|
| Proof-of-concept | No public PoC exists. No results on pocindex.io (404), no dedicated GitHub repos (e.g., CVE-2022-43680), no ExploitDB or Metasploit modules. The upstream GitHub issue #649 contains a technical description but no weaponized reproduction script. The OOM prerequisite makes PoC construction impractical. |
| EPSS | 0.02457 (2.46%) — roughly 80th percentile. Indicates low predicted exploitation probability over the next 30 days. Consistent with the OOM gate making real-world exploitation unreliable. |
| KEV status | Not listed. CISA has not added this CVE to the Known Exploited Vulnerabilities catalog as of October 2026. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — Network vector, low complexity (contested — OOM prerequisite is not captured), no privileges, no user interaction, availability-only impact. The vector overstates practical risk by encoding attack complexity as Low. |
| Affected versions | libexpat ≤ 2.4.9. Embedded in CPython's pyexpat, Apache mod_dav, D-Bus, CMake, Git, PHP, and many other packages that bundle or link against expat. |
| Fixed versions | libexpat 2.5.0 (released 2022-10-25). Distro backports: Debian Buster 2.2.6-2+deb10u6, Bullseye 2.2.10-2+deb11u5, Bookworm 2.5.0-1+deb12u4. RHEL 8/9 patched via expat package errata. Ubuntu fixed across Bionic, Focal, Jammy, Kinetic. Amazon Linux patched via ALAS advisories. |
| Disclosure timeline | Reported 2022-10-24. Fix committed same day (commit 5290462a). libexpat 2.5.0 released 2022-10-25. Distro patches rolled out through late 2022 and early 2023. |
| Reporter | Rhodri James, maintainer of libexpat. Discovered during code review of DTD lifetime management. |
| Scanning / exposure | No Shodan/Censys/FOFA dork specifically identifies libexpat-exposed services. The library is a dependency, not a directly exposed service. Vulnerability scanners detect it via package version checks on authenticated scans only. |
Sources.
Why this verdict
- OOM prerequisite collapses attack reliability. The CVSS vector marks Attack Complexity as Low, but the actual trigger requires the target host to be in an out-of-memory state at the exact moment a child parser is initialized. This is not attacker-controlled via the XML payload. Effective AC is High, which alone drops the base score from 7.5 to ~5.9.
- Impact ceiling is DoS only — no RCE, no data leak. The use-after-free dereferences a DTD structure that segfaults the process. There is no known write-what-where primitive, no heap spray technique documented, and the CVSS vector itself confirms C:N/I:N. For a library-level crash, process supervisors (systemd, container restart policies) restore service in seconds.
- No exploitation evidence whatsoever. No KEV listing, no PoC, no in-the-wild campaigns, EPSS at 2.46%. Four years post-disclosure with zero weaponization strongly suggests the OOM gate is a genuine barrier, not merely an unexploited opportunity.
- Role multiplier: libexpat is ubiquitous — it ships in nearly every Linux distribution and is embedded in Python, Apache, Git, PHP, and more. High-value roles include identity providers parsing SAML XML, API gateways, CI/CD pipelines, and database management consoles. However, even on these high-value targets, the chain outcome is *process crash with automatic restart*, not domain takeover, fleet compromise, or data exfiltration. A transient DoS on an IdP is disruptive but not catastrophic, and the OOM prerequisite means it cannot be triggered on demand. The blast radius is *host-level, transient* — not fleet-scale or identity-scale. The DoS-only outcome does not meet the high-value-role floor threshold of 'domain takeover / fleet compromise / mass data egress / supply-chain pivot.'
- Red Hat's Moderate rating is the right call. Red Hat explicitly noted this 'is not possible to exploit on every possible system' and rated Moderate. Their analyst judgment aligns with the friction analysis: the NVD's 7.5 is a mechanical score that doesn't capture the OOM precondition.
Why not higher?
The vulnerability does affect a near-universally-deployed library and the CVSS attack vector is unauthenticated/remote. If the OOM gate were removed — if an attacker could trigger this with a crafted XML document alone — this would be a clear HIGH. But the OOM prerequisite is load-bearing: it makes exploitation probabilistic and uncontrollable, which is the entire difference between a theoretical and a practical risk.
Why not lower?
libexpat's install base is enormous and includes security-critical infrastructure. A use-after-free in a C library *could* theoretically be chained with a heap-shaping technique for code execution, even though none has been demonstrated in four years. Dropping to LOW would imply the bug is purely cosmetic; it isn't — it's a real memory-safety defect in a foundational parsing library. MEDIUM correctly captures 'real bug, impractical to exploit, low-impact outcome.'
Crowdsourced verification payload.
Run this script on each target host as any user with read access to the package database. No root required. Invoke: bash check_cve_2022_43680.sh. Works on Debian/Ubuntu (dpkg) and RHEL/CentOS/Fedora (rpm) systems.
#!/usr/bin/env bash
# check_cve_2022_43680.sh — Detect libexpat vulnerable to CVE-2022-43680
# Target: Linux hosts with dpkg or rpm package managers
# Privileges: none (read-only package query)
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -euo pipefail
VULN_MAJOR=2
VULN_MINOR=4
VULN_PATCH=9
compare_version() {
local ver="$1"
local major minor patch
major=$(echo "$ver" | cut -d. -f1)
minor=$(echo "$ver" | cut -d. -f2)
patch=$(echo "$ver" | cut -d. -f3 | sed 's/[^0-9].*//')
patch=${patch:-0}
if (( major > VULN_MAJOR )); then
return 0 # patched
elif (( major == VULN_MAJOR && minor > VULN_MINOR )); then
return 0
elif (( major == VULN_MAJOR && minor == VULN_MINOR && patch > VULN_PATCH )); then
return 0
fi
return 1 # vulnerable
}
VERSION=""
# Try dpkg first (Debian/Ubuntu)
if command -v dpkg-query &>/dev/null; then
VERSION=$(dpkg-query -W -f='${Version}' libexpat1 2>/dev/null || true)
if [[ -n "$VERSION" ]]; then
# Extract upstream version (strip epoch and debian revision)
UPSTREAM=$(echo "$VERSION" | sed 's/^[0-9]*://;s/-[^-]*$//')
# Check for distro backport indicators (deb10u6+, deb11u5+, etc.)
if echo "$VERSION" | grep -qE 'deb10u([6-9]|[1-9][0-9])'; then
echo "PATCHED — libexpat1 $VERSION (Debian Buster backport)"
exit 0
elif echo "$VERSION" | grep -qE 'deb11u([5-9]|[1-9][0-9])'; then
echo "PATCHED — libexpat1 $VERSION (Debian Bullseye backport)"
exit 0
fi
if compare_version "$UPSTREAM"; then
echo "PATCHED — libexpat1 $VERSION (upstream >= 2.5.0)"
exit 0
else
echo "VULNERABLE — libexpat1 $VERSION (affected by CVE-2022-43680)"
exit 1
fi
fi
fi
# Try rpm (RHEL/CentOS/Fedora)
if command -v rpm &>/dev/null; then
VERSION=$(rpm -q --queryformat '%{VERSION}' expat 2>/dev/null || true)
if [[ -n "$VERSION" && "$VERSION" != *"not installed"* ]]; then
RELEASE=$(rpm -q --queryformat '%{RELEASE}' expat 2>/dev/null || true)
if compare_version "$VERSION"; then
echo "PATCHED — expat $VERSION-$RELEASE (>= 2.5.0)"
exit 0
else
# RHEL backports: check if release string indicates patched
if echo "$RELEASE" | grep -qE 'el[89]'; then
echo "UNKNOWN — expat $VERSION-$RELEASE (RHEL backport; verify via 'rpm -q --changelog expat | grep CVE-2022-43680')"
exit 2
fi
echo "VULNERABLE — expat $VERSION-$RELEASE (affected by CVE-2022-43680)"
exit 1
fi
fi
fi
echo "UNKNOWN — could not detect libexpat/expat package on this system"
exit 2