← Back to Feed CACHED · 2026-09-14 08:13:58 · CACHE_KEY CVE-2022-43680
CVE-2022-43680 · CWE-416 · Disclosed 2022-10-24

In libexpat through 2.4.9

ASSESSED — NOISGATE
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

A booby trap that only fires when the building is already on fire

CVE-2022-43680 is a use-after-free (CWE-416) in libexpat ≤ 2.4.9, the ubiquitous C XML parsing library embedded in Python, Apache httpd, PHP, Git, and hundreds of other packages. When XML_ExternalEntityParserCreate calls parserCreate and that inner function hits an allocation failure (out-of-memory), cleanup code incorrectly destroys a DTD structure shared with the root parser. Subsequent access to the freed DTD crashes the process. The fix — shipping in libexpat 2.5.0 (October 25, 2022) — simply moves the m_isParamEntity flag into parserCreate so cleanup knows not to free the shared DTD. Affected: all libexpat versions through 2.4.9.

The vendor CVSS of 7.5 HIGH is technically defensible on paper — the vector shows AV:N/AC:L/PR:N/UI:N — but it overstates practical risk. The entire chain depends on the target process being in an out-of-memory state at the exact moment it parses a crafted external entity. That is an attacker-unfriendly precondition: you either need a separate memory-exhaustion primitive or dumb luck. Impact is limited to availability only (C:N/I:N/A:H) — no code execution, no data leak. Red Hat independently rated this Moderate, and nearly four years later there is still no public PoC, no weaponized exploit, no in-the-wild activity, and no KEV listing. The 7.5 label is scanner noise; real-world risk is medium at best.

"DoS-only bug gated behind out-of-memory — noisy in scanners, quiet in the real world"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Identify a libexpat-consuming service

The attacker locates a network-reachable service that parses XML using libexpat ≤ 2.4.9. Common targets include Apache httpd with mod_dav, Python-based web apps using xml.parsers.expat, or any SOAP/REST endpoint accepting XML input. The service must process external entities (DTDs) for the vulnerable code path to be reached.
Conditions required:
  • Network-reachable XML parsing endpoint
  • libexpat ≤ 2.4.9 linked into the service
  • External entity processing enabled (not disabled via XML_SetEntityDeclHandler or similar)
Where this breaks in practice:
  • Many modern frameworks disable external entity processing by default (XXE hardening)
  • Python's defusedxml and similar wrappers block external entities entirely
Detection/coverage: Standard SCA scanners (Snyk, Trivy, Grype) flag the library version. No network-level signature exists because the trigger is environmental, not payload-specific.
STEP 02

Induce out-of-memory on target process

The bug only fires when parserCreate fails a memory allocation. The attacker must push the target process into an OOM state — e.g., by flooding it with large payloads, exploiting a separate memory leak, or targeting a container with tight memory limits (cgroups). This is a separate, prerequisite exploit that the CVSS vector does not capture.
Conditions required:
  • Ability to send enough data or requests to exhaust process memory
  • No upstream rate-limiting, WAF, or request-size caps blocking the flood
Where this breaks in practice:
  • Most production deployments sit behind load balancers, WAFs, and reverse proxies with request-size limits
  • Container orchestrators (Kubernetes) will OOM-kill and restart the pod before the attacker can chain the next step
  • OS-level memory overcommit means the OOM killer may terminate a different process entirely
Detection/coverage: Memory-pressure alerts, OOM-kill events in dmesg/journald, container restart metrics in Prometheus/Datadog.
STEP 03

Send crafted XML with external entity during OOM window

With the process already memory-starved, the attacker sends an XML document referencing an external entity. XML_ExternalEntityParserCreate calls parserCreate, which fails the m_protocolEncodingName allocation. The error-handling path calls XML_ParserFree on a parser whose m_isParamEntity flag hasn't been set yet, so it destroys the shared DTD.
Conditions required:
  • OOM condition active at the exact moment the XML is parsed
  • External entity reference in the payload reaches the vulnerable code path
Where this breaks in practice:
  • Timing window is extremely narrow — the OOM must persist through the allocation attempt
  • If OOM triggers before reaching the parser, the service returns a 503 or connection reset, not the vulnerable path
STEP 04

Process crashes (DoS achieved)

The root parser's DTD pointer is now dangling. Any subsequent access — including normal cleanup — dereferences freed memory and crashes the process. The result is a denial-of-service condition. There is no known path from this use-after-free to code execution; the freed structure is a DTD, not a function pointer table, and heap grooming to control the contents would require yet another primitive.
Conditions required:
  • Steps 1-3 succeeded in sequence
Where this breaks in practice:
  • Process restarts automatically in most production environments (systemd, supervisord, Kubernetes)
  • No confidentiality or integrity impact — attacker gains nothing beyond a crash
Detection/coverage: Crash dumps, core files, SIGSEGV in application logs.
03 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNone observed. No CISA KEV listing, no reports from Mandiant, CrowdStrike, or other threat-intel vendors. Nearly 4 years post-disclosure with zero documented exploitation.
Proof-of-ConceptNo public PoC. The upstream GitHub issue (#649) describes the bug mechanically but no standalone exploit code has been published. The OOM precondition makes PoC construction non-trivial.
EPSS Score0.02445 (2.4%) — 83rd percentile. Elevated relative to the average CVE but still well below the ~6%+ threshold where exploitation becomes common.
KEV StatusNot listed in CISA KEV as of 2026-09-14.
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — 7.5 HIGH. Note: the vector does not model the OOM precondition, which effectively raises Attack Complexity beyond Low in practice.
Affected Versionslibexpat ≤ 2.4.9 (all prior versions). Embedded copies in Python (CPython), Apache httpd, PHP, Git, and many other packages are transitively affected.
Fixed Versionlibexpat 2.5.0 (released 2022-10-25). Distro backports: RHEL 8/9 via expat-2.2.5-11.el8 / expat-2.5.0-1.el9, Debian Bullseye via 2.2.10-2+deb11u5, Ubuntu 22.04 via 2.4.7-1ubuntu0.1.
Scanning / Exposurelibexpat is installed on virtually every Linux host, but direct network exposure depends on whether an XML-parsing service (httpd, Python app, etc.) is reachable. No Shodan/GreyNoise signatures specific to this CVE exist because exploitation is not payload-detectable.
Disclosure Date2022-10-24 (NVD published). Fix released next day (2022-10-25).
Researcher / CreditReported via the libexpat project. Upstream fix by Sebastian Pipping in PRs #616 and #650.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.0/10)

The single most decisive factor is the out-of-memory precondition: the entire chain requires the target process to already be memory-exhausted, a condition the attacker must induce via a separate, unrelated attack primitive — effectively doubling the exploit chain length and narrowing the reachable population to near-zero in production environments with standard resource controls. Impact is capped at denial-of-service with no path to code execution, confidentiality breach, or integrity violation.

HIGH Vulnerability mechanics and OOM precondition
HIGH No exploitation in the wild after ~4 years
MEDIUM Absence of RCE path (heap-grooming viability not formally proven impossible)

Why this verdict

  • OOM precondition is a hidden Attack Complexity multiplier. The CVSS vector says AC:L, but in practice the attacker needs the process to fail a malloc at a specific point in the parser lifecycle. This is closer to AC:H, which alone would drop the base score from 7.5 to 5.9.
  • Impact ceiling is Availability-only. C:N/I:N/A:H means even a successful exploit yields a process crash — no data exfiltration, no lateral movement, no persistence. In containerized or systemd-managed environments the service auto-restarts in seconds.
  • No weaponization in ~4 years. Zero public PoCs, zero KEV entries, zero threat-intel reports. The EPSS of 2.4% is modest and consistent with a bug that researchers understand but attackers ignore.
  • Role multiplier: libexpat is embedded everywhere — Python runtimes, Apache httpd, Git — so it touches high-value roles (CI/CD runners, web frontends, identity-adjacent services). However, the impact in ALL roles is capped at a transient crash. A CI runner crash delays a build; it does not leak secrets or pivot supply chains. A web server crash behind a load balancer causes a blip, not an outage. Because the blast radius is host-local, transient, and availability-only even in high-value roles, the floor does not elevate above MEDIUM.
  • Red Hat independent assessment agrees. Red Hat rated this Moderate despite the 7.5 CVSS, explicitly citing the OOM precondition as the reason.

Why not higher?

Promoting to HIGH would require either a credible code-execution path or evidence of real-world exploitation — neither exists. The use-after-free targets a DTD structure, not a vtable or function pointer, and no researcher has demonstrated heap grooming to achieve control. Four years of silence from attackers confirms the theoretical risk has not translated to practical threat.

Why not lower?

Dropping to LOW or IGNORE would undercount the sheer ubiquity of libexpat across the Linux ecosystem. While the OOM gate makes remote exploitation difficult, it is not impossible — especially in memory-constrained containers with aggressive cgroup limits. The bug is real, the fix is trivial, and leaving unpatched libexpat on thousands of hosts is unnecessary hygiene debt.

05 · Compensating Control

What to do — in priority order.

  1. Disable external entity processing in XML parsers — Set XML_SetExternalEntityRefHandler to a no-op or use wrapper libraries like Python's defusedxml that block external entities by default. This eliminates the vulnerable code path entirely. Deploy across your fleet as part of normal code-hardening — no emergency mitigation SLA applies for a MEDIUM verdict.
  2. Enforce container memory limits with restart policies — Ensure all containerized workloads have explicit memory limits (resources.limits.memory in Kubernetes) and automatic restart-on-OOM. This makes the OOM precondition self-correcting — the container dies and restarts before the attacker can chain the UAF.
  3. Set request-size limits on XML-accepting endpoints — Configure reverse proxies (nginx client_max_body_size, Apache LimitRequestBody) to cap XML payloads at a reasonable size (e.g., 10 MB). This makes it harder for an attacker to induce OOM via payload flooding.
  4. Update libexpat to ≥ 2.5.0 — The definitive fix. On RHEL/CentOS: yum update expat. On Debian/Ubuntu: apt update && apt upgrade libexpat1. On Python-centric stacks, ensure CPython ≥ 3.11.1 or ≥ 3.10.9 which bundle expat 2.5.0. No mitigation SLA — go straight to the 365-day remediation window per noisgate MEDIUM policy.
What doesn't work
  • WAF XML-injection rules — the trigger is not a malicious payload pattern; it is an environmental condition (OOM) combined with a normal external-entity reference. No WAF signature can detect or block it.
  • Network segmentation — libexpat runs inside the application process, not as a separate service. Segmenting the network does not change whether the library is vulnerable.
06 · Verification

Crowdsourced verification payload.

Run on each target Linux host as any user with read access to the package database. Example: bash check_cve_2022_43680.sh. No root required. Works on RHEL/CentOS, Debian/Ubuntu, and Alpine.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2022_43680.sh — Detect CVE-2022-43680 (libexpat UAF)
# Run on target Linux host. No root needed.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -euo pipefail

VULN_THRESHOLD="2.5.0"

version_gte() {
  # Returns 0 if $1 >= $2 using sort -V
  printf '%s\n%s' "$2" "$1" | sort -V -C
}

get_expat_version() {
  # Try package managers first
  if command -v rpm &>/dev/null; then
    ver=$(rpm -q --qf '%{VERSION}' expat 2>/dev/null || true)
    if [[ -n "$ver" && "$ver" != *"not installed"* ]]; then
      echo "$ver"; return 0
    fi
  fi
  if command -v dpkg-query &>/dev/null; then
    ver=$(dpkg-query -W -f='${Version}' libexpat1 2>/dev/null || true)
    if [[ -n "$ver" ]]; then
      # Strip epoch and Debian revision for comparison
      ver=$(echo "$ver" | sed 's/^[0-9]*://; s/-[^-]*$//')
      echo "$ver"; return 0
    fi
  fi
  if command -v apk &>/dev/null; then
    ver=$(apk info -e expat 2>/dev/null | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)
    if [[ -n "$ver" ]]; then
      echo "$ver"; return 0
    fi
  fi
  # Fallback: check libexpat.so
  if ldconfig -p 2>/dev/null | grep -q libexpat; then
    ver=$(strings $(ldconfig -p | grep libexpat | awk '{print $NF}' | head -1) 2>/dev/null | grep -oP 'expat_[0-9]+\.[0-9]+\.[0-9]+' | head -1 | sed 's/expat_//' || true)
    if [[ -n "$ver" ]]; then
      echo "$ver"; return 0
    fi
  fi
  return 1
}

VER=$(get_expat_version) || { echo "UNKNOWN — could not determine libexpat version"; exit 2; }

echo "Detected libexpat version: $VER"
echo "Fixed in: >= $VULN_THRESHOLD"

if version_gte "$VER" "$VULN_THRESHOLD"; then
  echo "PATCHED — libexpat $VER is >= $VULN_THRESHOLD"
  exit 0
else
  echo "VULNERABLE — libexpat $VER is < $VULN_THRESHOLD (CVE-2022-43680)"
  exit 1
fi
07 · Bottom Line

If you remember one thing.

TL;DR
This is a MEDIUM finding under the noisgate reassessment — no mitigation SLA applies, so go straight to the 365-day noisgate remediation SLA. Roll the libexpat / expat package update into your next quarterly OS patching cycle; there is no reason to cut an emergency change window. If you run containerized workloads, confirm your pods have explicit memory limits and restart policies — that is good hygiene regardless. If your scanners are lighting up thousands of hosts (they will, because libexpat is everywhere), batch the update across your fleet over the next quarter and close the tickets. Do not let this CVE's 7.5 CVSS score bump higher-priority work off your sprint.

Sources

  1. NVD — CVE-2022-43680 Detail
  2. libexpat GitHub Issue #649
  3. GitHub Advisory GHSA-4hjv-8mmr-jxwv
  4. Red Hat CVE-2022-43680
  5. Expat 2.5.0 Release Announcement
  6. libexpat 2.5.0 Changelog
  7. F5 Advisory K000139525
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.