Back to Intelligence

CVE-2025-71407: Critical libxml2 Stack Buffer Overflow in Nokogiri (CVSS 9.8) — Detection and Remediation Guide

SA
Security Arsenal Team
August 25, 2026
9 min read

In the last three days, NVD published three CRITICAL, network-vector CVEs affecting libxml — the XML parsing stack that sits underneath an enormous share of the internet's application infrastructure. The headline for defenders in 2026 is CVE-2025-71407 (CVSS 9.8): Nokogiri before 1.18.3 contains a stack buffer overflow in libxml2 when reporting DTD validation errors. Because Nokogiri is the dominant XML/HTML parsing gem in the Ruby ecosystem — Rails apps, background job processors, API gateways, SAML and SOAP integrations — this is not a niche library issue. Any Ruby application that parses attacker-controlled XML with DTD validation enabled is in scope.

The same NVD batch also includes CVE-2022-51000 and CVE-2024-58378, both describing older vendored-libxml2 exposures in Nokogiri's packaged CRuby builds. Those are only relevant if your fleet is still pinned to gem versions from 2022–2024 — and if it is, you have bigger patch-management problems than any single CVE. This post focuses on the currently actionable item: CVE-2025-71407, its exploitation surface, how to hunt for abuse, and how to remediate.

Technical Analysis

Affected Products and Versions

ComponentAffected VersionsFixed Version
Nokogiri (CRuby, vendored libxml2)< 1.18.31.18.3+

Key scoping detail that trips teams up: Nokogiri ships in two flavors. The packaged (precompiled) gems for CRuby vendor their own copy of libxml2 — this is the default for most gem install nokogiri deployments and most containers built from standard Ruby base images. Deployments that compile Nokogiri against the system libxml2 inherit the system library's patch state instead, so your exposure depends on both the gem version and how it was built. Check both.

How the Vulnerability Works (Defender's View)

CVE-2025-71407 is a stack buffer overflow in libxml2's DTD validation error-reporting path. The attack chain from a defender's perspective:

  1. Attacker delivers a crafted XML document containing a malicious DTD to any application endpoint that parses XML — file upload features, SOAP/XML-RPC endpoints, SAML response handlers, EDI integrations, RSS/import pipelines, or CI tooling that consumes XML artifacts.
  2. The application parses with validation enabled (Nokogiri::XML with DTD validation, or document.validate). When libxml2 reports a validation error, the malformed DTD content triggers the overflow in the error-reporting code path.
  3. Impact: At minimum, a stack overflow corrupts the process and crashes it — a remotely triggerable denial of service against any worker parsing attacker input. Depending on stack layout and mitigations (stack canaries, ASLR), memory-corruption primitives of this class can be leveraged further, which is why NVD scored this 9.8 with a network attack vector and no required privileges or user interaction.

The exploitation requirement — an application that parses untrusted XML with DTD validation — sounds narrow until you inventory how much enterprise middleware does exactly that. SAML, SOAP, and legacy B2B integrations are the usual suspects.

Exploitation Status

As of this writing, CVE-2025-71407 is newly published by NVD. There is no confirmed in-the-wild exploitation and no CISA KEV listing yet — but CVSS 9.8 network-vector memory corruption in a library this ubiquitous moves fast. Historically, libxml2 advisories are followed within weeks by PoC publication and scanner integration. Treat this as patch-now, not watch-and-wait. The two older CVEs in the same batch (CVE-2022-51000, CVE-2024-58378) are only a present-day concern if you're running unpatched gem versions from those eras — the fix for all three is the same: current Nokogiri.

Detection & Response

Memory corruption inside a library doesn't leave clean IOCs — you won't find a malicious binary or registry key. What you can hunt for: (1) the crash telemetry a stack overflow produces, (2) post-exploitation behavior from a compromised Ruby process, and (3) inventory exposure so you know what to patch first.

YAML
---
title: Ruby Process Spawning Shell or System Utility
description: Detects Ruby interpreter processes spawning shells or system utilities, consistent with post-exploitation activity after compromise of an XML-parsing Ruby application (e.g. libxml2 memory corruption via CVE-2025-71407).
references:
  - https://nvd.nist.gov/vuln/detail/CVE-2025-71407
  - https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/02/14
id: 3f7c1a92-6b84-4d19-9e25-8a4c2f71d033
status: experimental
tags:
  - attack.execution
  - attack.t1059
logsource:
  category: process_creation
  product: linux
detection:
  selection_parent:
    ParentImage|endswith:
      - '/ruby'
      - '/ruby3.0'
      - '/ruby3.1'
      - '/ruby3.2'
      - '/ruby3.3'
      - '/puma'
      - '/sidekiq'
      - '/unicorn'
      - '/passenger'
  selection_child:
    Image|endswith:
      - '/sh'
      - '/bash'
      - '/dash'
      - '/zsh'
      - '/curl'
      - '/wget'
      - '/nc'
      - '/ncat'
      - '/netcat'
      - '/python'
      - '/python3'
      - '/perl'
      - '/base64'
  condition: selection_parent and selection_child
falsepositives:
  - Deployment tooling (Capistrano, Chef, Ansible runs invoking system commands from Ruby)
  - Legitimate rake tasks invoking shell commands
level: high
---
title: Web Worker Crash Loop Indicating Memory Corruption
description: Detects repeated crashes (SIGSEGV/SIGABRT) of Ruby application worker processes, consistent with attempted or successful triggering of a stack buffer overflow in libxml2 DTD validation (CVE-2025-71407).
references:
  - https://nvd.nist.gov/vuln/detail/CVE-2025-71407
author: Security Arsenal
date: 2026/02/14
id: 9d2e5b48-1c63-4f07-b3a1-6e8d4a52c907
status: experimental
tags:
  - attack.initial_access
  - attack.t1190
logsource:
  category: process_creation
  product: linux
detection:
  selection:
    CommandLine|contains:
      - 'kernel: '
      - 'segfault'
      - 'general protection fault'
  selection_target:
    CommandLine|contains:
      - 'ruby'
      - 'puma'
      - 'sidekiq'
      - 'unicorn'
      - 'rails'
      - 'passenger'
  condition: selection and selection_target
falsepositives:
  - Unrelated application instability; correlate with XML ingestion endpoints and patch state
level: medium

A note on fidelity: the first rule is your highest-value detection. A Rails or Sidekiq worker spawning curl or bash is almost never normal in production, and it's exactly what you'd see if a memory-corruption crash became a code-execution foothold. The second rule is noisier — use it as a correlation signal against hosts you know are running unpatched Nokogiri versions, not as a standalone paging alert.

KQL — Microsoft Sentinel / Defender
// Hunt: Ruby application workers spawning shells or download tools
// Relevant to post-exploitation of XML parsing flaws (CVE-2025-71407, libxml2 in Nokogiri)
// Tables cover both MDE-on-Linux (DeviceProcessEvents) and Syslog/CEF ingestion
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName has_any ("ruby", "puma", "sidekiq", "unicorn", "passenger", "rails")
   or InitiatingProcessCommandLine has_any ("puma", "sidekiq", "rails server")
| where FileName has_any ("sh", "bash", "dash", "zsh", "curl", "wget", "nc", "ncat", "python", "python3", "perl", "base64")
| project TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, AccountName
| order by TimeGenerated desc
;
// Companion: Syslog crash telemetry for Ruby workers (segfaults during DTD/XML parsing)
Syslog
| where TimeGenerated > ago(7d)
| where SyslogMessage has_any ("segfault", "general protection fault", "SIGABRT", "stack smashing detected")
| where SyslogMessage has_any ("ruby", "puma", "sidekiq", "rails", "libxml")
| summarize CrashCount = count() by Computer, bin(TimeGenerated, 1h)
| where CrashCount >= 3
| order by CrashCount desc
VQL — Velociraptor
-- Velociraptor artifact: inventory Nokogiri gem versions and flag CVE-2025-71407 exposure
-- Scans installed gem specifications across hosts; flags anything below 1.18.3
SELECT FullPath,
       parse_string_with_regex(string=FullPath,
          regex='nokogiri-(?P<Version>[0-9]+\\.[0-9]+\\.[0-9]+)').Version AS NokogiriVersion,
       read_file(filename=FullPath, length=2000) AS GemspecHeader
FROM glob(globs=['/var/lib/gems/*/specifications/nokogiri-*.gemspec',
                 '/usr/local/lib/ruby/gems/*/specifications/nokogiri-*.gemspec',
                 '/usr/lib/ruby/gems/*/specifications/nokogiri-*.gemspec',
                 '/opt/*/vendor/bundle/ruby/*/specifications/nokogiri-*.gemspec',
                 '/home/*/.rbenv/versions/*/lib/ruby/gems/*/specifications/nokogiri-*.gemspec',
                 '/usr/local/bundle/specifications/nokogiri-*.gemspec'])
WHERE NokogiriVersion < '1.18.3'
Bash / Shell
#!/bin/bash
# CVE-2025-71407 remediation/verification: Nokogiri vendored libxml2 stack buffer overflow
# Run on Ruby application hosts and in container build pipelines.

set -euo pipefail

echo "=== [1] Enumerate installed Nokogiri versions ==="
gem list nokogiri --all 2>/dev/null || true
# Also check bundler-locked versions per application
find / -name 'Gemfile.lock' -not -path '*/proc/*' 2>/dev/null | while read -r lock; do
  ver=$(grep -E '^\s+nokogiri \(' "$lock" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)
  [ -n "$ver" ] && echo "$lock -> nokogiri $ver"
done

echo "=== [2] Check whether Nokogiri uses vendored or system libxml2 ==="
for lock in $(find / -name 'Gemfile.lock' -not -path '*/proc/*' 2>/dev/null); do
  appdir=$(dirname "$lock")
  (cd "$appdir" && ruby -rnokogiri -e \
    'puts "#{File.dirname("$0")}: Nokogiri #{Nokogiri::VERSION} / libxml2 #{Nokogiri::LIBXML2_VERSION} (#{Nokogiri.uses_system_libraries? ? "SYSTEM" : "VENDORED"})"' \
    2>/dev/null || true)
done

echo "=== [3] Remediate: upgrade to Nokogiri >= 1.18.3 ==="
# Per-application (preferred — respects lockfile):
#   bundle update nokogiri --conservative
# System-wide fallback:
#   gem update nokogiri

echo "=== [4] Verify post-patch ==="
ruby -rnokogiri -e \
  'abort("STILL VULNERABLE: #{Nokogiri::VERSION}") if Gem::Version.new(Nokogiri::VERSION) < Gem::Version.new("1.18.3"); puts "OK: Nokogiri #{Nokogiri::VERSION} / libxml2 #{Nokogiri::LIBXML2_VERSION}"'

echo "=== [5] If system libxml2 is used, patch the OS package too ==="
# Debian/Ubuntu:  apt-get update && apt-get install --only-upgrade libxml2
# RHEL/Rocky:     dnf update libxml2
# Alpine:         apk upgrade libxml2

Remediation

  1. Upgrade Nokogiri to 1.18.3 or later. This single action resolves CVE-2025-71407 and, if you're upgrading from very old pins, closes the older CVE-2022-51000 (fixed in 1.13.2) and CVE-2024-58378 (fixed in 1.15.6 / 1.16.2) exposures in the same motion. Run bundle update nokogiri --conservative per application, rebuild containers, and redeploy. Gem updates do not take effect in running processes — you must restart Puma/Sidekiq/Passenger workers.
  2. Determine vendored vs. system libxml2. Use the script above (Nokogiri.uses_system_libraries?). If you compile against system libxml2, patch the OS package (apt/dnf/apk) and restart — the gem version alone won't protect you.
  3. Attack-surface reduction (defense in depth, not a workaround). Where business logic permits, disable DTD processing entirely for untrusted input: parse with Nokogiri::XML::ParseOptions::NONET, do not enable DTD validation on attacker-controlled documents, and reject DOCTYPE declarations at the ingestion layer where the integration allows it. This narrows the exposed code path even after patching.
  4. Prioritize internet-facing XML consumers. SAML Service Providers, SOAP endpoints, file-import features, and webhook handlers that accept XML payloads are your top patch targets. Inventory them via your Gemfile.lock files and SBOM — the VQL artifact above automates this across a fleet.
  5. Don't forget the old CVEs as a hygiene signal. If any host in your fleet still runs Nokogiri < 1.13.2 (CVE-2022-51000 territory) or < 1.15.6 (CVE-2024-58378 territory), escalate it — that indicates a dependency-freeze problem that will burn you again.

References: NVD — CVE-2025-71407, NVD — CVE-2022-51000, NVD — CVE-2024-58378, and the Nokogiri project's security advisories on GitHub.

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.