VNX-CS-003 – C# XXE: XML Parser Resolves External Entities
Overview
This rule reports the XML parser configurations in C# code that permit XML External Entity (XXE) injection. An XmlResolver set to an XmlUrlResolver (inline or through a variable) is always reported on XmlDocument and XmlTextReader, and on XmlReaderSettings when the same settings also enable DTDs. DtdProcessing = DtdProcessing.Parse and ProhibitDtd = false are reported when no XmlResolver = null sits beside them, unless every project file in the scan targets .NET Framework 4.5.2+ or .NET (Core), where the default resolver is null. When a project targets .NET Framework 4.5.1 or older (TargetFrameworkVersion v2.0 to v4.5.1, or net20 to net451), XmlDocument.Load/LoadXml and new XmlTextReader(...) on non-constant input are also reported, because those parsers resolve external entities by default there. Documents loaded from string constants, DtdProcessing.Prohibit/Ignore and ProhibitDtd = true are not reported.
XXE vulnerabilities arise because the XML standard includes a mechanism — external entities — that instructs the parser to fetch a URI and substitute its contents into the document before processing. When that URI points to a local file path like file:///etc/passwd, the contents of that file appear in the parsed document and can be read by the application, then returned to the attacker in an error message or response body. When the URI points to an internal HTTP endpoint, the same mechanism enables Server-Side Request Forgery (SSRF).
Modern .NET XML APIs (XDocument, XmlReader with default settings) disable external entity resolution by default, but older APIs (XmlDocument, XmlTextReader) and explicit configuration changes can re-enable it.
Severity: High | CWE: CWE-611 – Improper Restriction of XML External Entity Reference
Why This Matters
XXE attacks have been in the OWASP Top 10 for over a decade and are responsible for significant data breaches. By crafting a malicious XML document that includes an external entity declaration pointing to a sensitive file, an attacker can exfiltrate /etc/passwd, /etc/shadow, application configuration files, source code, or cloud instance metadata endpoints (http://169.254.169.254/). Depending on the parser’s network access, out-of-band XXE can exfiltrate data even when the response is not reflected back.
In .NET applications, XXE is particularly common in SOAP-based web service handlers, file upload processors, and report generation pipelines that parse user-submitted XML. The vulnerability frequently goes undetected because the parser configuration is set once (perhaps buried in a base class or helper method) and then used throughout the codebase. A single misconfigured XmlDocument instance used in a deserialization path can expose the entire server’s filesystem.
Blind XXE — where the exfiltrated data is sent via a DNS lookup or HTTP request to an attacker-controlled server rather than included in the response — is harder to detect with manual testing but can be found reliably with static analysis rules like this one.
What Gets Flagged
// FLAGGED: XmlUrlResolver explicitly enables external entity fetching
var doc = new XmlDocument();
doc.XmlResolver = new XmlUrlResolver(); // allows file:// and http:// fetches
doc.Load(userInputStream);
// FLAGGED (unless every project targets .NET Framework 4.5.2+ or .NET): DTD parsing with the default resolver
var settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Parse;
var reader = XmlReader.Create(userInputStream, settings);
// FLAGGED (same condition): ProhibitDtd = false on XmlTextReader (legacy API)
var reader = new XmlTextReader(userInputStream);
reader.ProhibitDtd = false;
// FLAGGED when a project targets .NET Framework 4.5.1 or older: unsafe default resolver
XmlDocument doc = new XmlDocument();
doc.Load(context.Request.InputStream);
// NOT FLAGGED: resolver disabled before loading
var safe = new XmlDocument { XmlResolver = null };
safe.Load(input);
Remediation
- Set
XmlResolver = nullon anyXmlDocumentinstance that parses external input — this is the single most important change. - Set
DtdProcessing = DtdProcessing.ProhibitinXmlReaderSettingsfor all reader instances that process untrusted XML. - Migrate away from
XmlTextReaderwithProhibitDtd = false; useXmlReader.Create()with explicit settings instead. - Prefer
XDocument.Load()orXDocument.Parse()fromSystem.Xml.Linq, which usesXmlReaderunder the hood with safe defaults, for new code.
// SAFE: XmlDocument with resolver disabled
var doc = new XmlDocument();
doc.XmlResolver = null; // disables external entity resolution
doc.Load(userInputStream);
// SAFE: XmlReaderSettings with DTD processing prohibited
var settings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null,
};
using var reader = XmlReader.Create(userInputStream, settings);
// SAFE: XDocument with safe defaults
var xdoc = XDocument.Load(userInputStream);