Dreamhack/Dreamhack Wargame (Challenge)

[154] IT 비전공자 [dreamhack]Badge문제 풀기

imaginefuture-1 2025. 2. 11. 08:57

쌈뽕한데요?
로그인?
guest/guest 국룰 입력할게요
ㅠㅠ 실패
guest/guest 쌈뽕한 계정 하나 만들겠습니다

 

쌈뽕한 뱃지 발급하러가볼까요

 

 

헤엥...일단 작전상 후퇴 소스코드보러가보자

 

 

 

generate_badge.php 소스코드다

<?php
    include './db/lib.php';
    session_start();

    if (!isset($_SESSION['username'])) {
        header("Location: login.php");
        exit();
    }

    function generateRandomKey($length = 32) {
        return bin2hex(random_bytes($length / 2));
    }

    if ($_SERVER["REQUEST_METHOD"] == "POST") {
        if (!isset($_POST['password']) || empty($_POST['password'])) {
            header("Location: generate_badge.php");
            exit();
        }

        $username = $_SESSION['username'];
        $password = $_POST['password'];
        
        $stmt = $pdo->prepare("SELECT password FROM users WHERE username = ?");
        $stmt->execute([$username]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$user || ($password!==$user['password'])) {
            echo "<script>alert('Wrong password.....');location.href='/generate_badge.php';</script>";
            exit();
        }
        
        $stmt = $pdo->prepare("SELECT COUNT(*) FROM dreambadge WHERE username = ?");
        $stmt->execute([$username]);
        $badgeExists = $stmt->fetchColumn();

        if ($badgeExists > 0) {
            $stmt = $pdo->prepare("SELECT badge_key FROM dreambadge WHERE username = ?");
            $stmt->execute([$username]);
            $badgeKey = $stmt->fetchColumn();

            setcookie('key', $badgeKey, time() + 3600, "/");

            echo "<script>alert('your badge already exists!');location.href='/view_badge.php';</script>";
            exit();
        }

        $badgeName = $username . "_badge";
        $accessKey = generateRandomKey();
        $stmt = $pdo->prepare("INSERT INTO dreambadge (username, badge_name, badge_key) VALUES (?, ?, ?)");
        $stmt->execute([$username, $badgeName, $accessKey]);
        setcookie('key', $accessKey, time() + 3600, "/");

        echo "<script>alert('Success!');location.href='/view_badge.php';</script>";
        exit();
    }
?>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Create Dream Badge</title>
    <link rel="stylesheet" href="/static/css/generate_badge.css">
</head>
<body>
    <div class="container">
        <h1>Create Dream Badge</h1>
        <p>Dream Badge를 발급하시려면 비밀번호를 입력하세요.</p>
        
        <form action="" method="POST">
            <input type="password" name="password" placeholder="비밀번호" required>
            <button type="submit">Create</button>
        </form>
    </div>
</body>
</html>

 

 

    function generateRandomKey($length = 32) {
        return bin2hex(random_bytes($length / 2));
    }

 

뱃지 비번은 랜덤한 16진수 키를 생성하는 함수 (이건 못맞출뜻)

        $badgeName = $username . "_badge";
        $accessKey = generateRandomKey();
        $stmt = $pdo->prepare("INSERT INTO dreambadge (username, badge_name, badge_key) VALUES (?, ?, ?)");
        $stmt->execute([$username, $badgeName, $accessKey]);
        setcookie('key', $accessKey, time() + 3600, "/");

        echo "<script>alert('Success!');location.href='/view_badge.php';</script>";
        exit();
    }

 

🔍 코드 설명: 사용자 뱃지 생성 및 쿠키 설정

이 PHP 코드의 목적은 사용자에게 랜덤한 badge_key(뱃지 키)를 생성하여 데이터베이스에 저장하고, 쿠키에 설정하는 것이야.
이를 통해 사용자는 로그인 후 특정 뱃지를 보유할 수 있고, 해당 뱃지의 키를 사용하여 인증할 수 있음.


📌 코드 분석

$badgeName = $username . "_badge";  // 사용자 뱃지 이름 생성
$accessKey = generateRandomKey();   // 랜덤한 뱃지 키 생성
  • $badgeName → 사용자($username)에 "_badge"를 붙여 뱃지 이름을 생성 (admin_badge 같은 형식).
  • $accessKey → generateRandomKey() 함수를 호출하여 랜덤한 32자리 키를 생성.

🔹 1️⃣ 데이터베이스(dreambadge)에 뱃지 정보 저장

$stmt = $pdo->prepare("INSERT INTO dreambadge (username, badge_name, badge_key) VALUES (?, ?, ?)");
$stmt->execute([$username, $badgeName, $accessKey]);
  • SQL 실행 방식 (prepare & execute)
    • prepare() → SQL 쿼리를 미리 준비하고 보안적으로 안전하게 바인딩하는 방식 (SQL 인젝션 방어 가능).
    • execute([$username, $badgeName, $accessKey]) → ?에 들어갈 값을 $username, $badgeName, $accessKey로 채움.
  • dreambadge 테이블에 데이터 저장
    • username → 사용자 이름
    • badge_name → 생성된 뱃지 이름 (admin_badge 같은 형식)
    • badge_key → 랜덤한 32자리 키 (보안 용도로 사용됨)

🔹 2️⃣ 쿠키(key) 설정

setcookie('key', $accessKey, time() + 3600, "/");
  • 쿠키 이름: 'key'
  • 쿠키 값: $accessKey (랜덤하게 생성된 badge_key)
  • 쿠키 유효 시간: time() + 3600 → 1시간(3600초) 동안 유지
  • 쿠키 경로: "/" → 웹사이트 전체에서 사용 가능

✅ 이렇게 하면 사용자가 브라우저에서 자동으로 해당 쿠키를 보유하게 되므로, 이후 페이지에서 인증할 때 이 쿠키를 확인할 수 있음.


🔹 3️⃣ 사용자에게 성공 메시지 출력 및 페이지 이동

echo "<script>alert('Success!');location.href='/view_badge.php';</script>";
exit();
  • <script>alert('Success!'); → "Success!" 알림 메시지를 브라우저에서 띄움.
  • location.href='/view_badge.php'; → 사용자를 /view_badge.php 페이지로 자동 리디렉션.
  • exit(); → PHP 실행을 즉시 종료.

🚀 최종 코드 동작 과정

1️⃣ 사용자가 특정 동작(예: 버튼 클릭, 로그인 등)을 수행하면 이 코드가 실행됨.
2️⃣ 랜덤한 badge_key를 생성 (generateRandomKey()).
3️⃣ 사용자의 뱃지 정보를 dreambadge 테이블에 저장.
4️⃣ 사용자의 브라우저에 key라는 이름의 쿠키를 설정 (1시간 유지).
5️⃣ 성공 메시지를 띄우고 /view_badge.php로 이동.


🔥 결론

사용자에게 랜덤한 뱃지 키(badge_key)를 생성하여 데이터베이스에 저장
해당 키를 브라우저 쿠키(key)에 저장하여 이후 인증에 활용 가능
성공 메시지를 출력하고 /view_badge.php로 자동 이동

🚀 이제 해당 뱃지 정보를 /view_badge.php에서 확인할 수 있을 거야! 🎯

 

 

db 폴더의 lib.php 파일

 

<?php
    $dbname = '/db/dreamdatabase.sqlite';

    try {
        $pdo = new PDO("sqlite:$dbname");
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        $pdo->exec("
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                username TEXT UNIQUE NOT NULL,
                password TEXT NOT NULL
            );

            CREATE TABLE IF NOT EXISTS dreambadge (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                username TEXT UNIQUE NOT NULL,
                badge_name TEXT UNIQUE NOT NULL,
                badge_key TEXT NOT NULL
            );

            INSERT OR IGNORE INTO users (username, password)
            VALUES ('admin', '{{**REDACTED**}}');

            INSERT OR IGNORE INTO dreambadge (username, badge_name, badge_key)
            VALUES ('admin', 'admin_badge', '{{**REDACTED**}}');
        ");
    } catch (PDOException $e) {
        die("Database Failed: " . $e->getMessage());
    }
?>

 

 

admin에 비번은 redacted라고 되어있음  redact - 기밀 혹은 민감한 내용을 지우다라고 사전 검색하니 나온다

알려줘요 비번...

 

register.php 파일을 살펴보자

회원가입할때 뭔가 꼼수가 있지않을까?

<?php
    include './db/lib.php';

    if ($_SERVER["REQUEST_METHOD"] == "POST") {
        $username = $_POST['username'];
        $password = $_POST['password'];

        $stmt = $pdo->prepare("SELECT COUNT(*) FROM users WHERE username = ?");
        $stmt->execute([$username]);
        $userExists = $stmt->fetchColumn();

        if ($userExists > 0) {
            echo "<script>alert('username already exists...'); location.href='register.php';</script>";
            exit();
        }

        $stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (?, ?)");

        if ($stmt->execute([$username, $password])) {
            echo "<script>alert('Register Success!');location.href='login.php';</script>";
        } else {
            echo "<script>alert('Something Wrong....')</script>";
        }
    }
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Register</title>
    <link rel="stylesheet" href="/static/css/register.css">
</head>
<body>
    <div class="register-container">
        <h2>회원가입</h2>
        <form action="register.php" method="POST">
            <input type="text" name="username" placeholder="아이디" required>
            <input type="password" name="password" placeholder="비밀번호" required>
            <button type="submit">회원가입</button>
        </form>
    </div>
</body>
</html>

 

그냥 평범한 회원가입 코드 같은데...

 

 

login.php 파일도 보자

로그인 할때 취약점이 있을 수 있으니..!

<?php
    include './db/lib.php';
    session_start();

    $errorMessage = "";

    if ($_SERVER["REQUEST_METHOD"] == "POST") {
        $username = $_POST['username'];
        $password = $_POST['password'];

        $stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
        $stmt->execute([$username]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);

        if ($user && ($password === $user['password'])) {
            if (isset($_COOKIE['key'])) {
                setcookie('key', '', time() - 3600, '/');
            }

            $_SESSION['username'] = $username;

            $stmt = $pdo->prepare("SELECT badge_key FROM dreambadge WHERE username = ?");
            $stmt->execute([$username]);
            $dreamBadge = $stmt->fetch(PDO::FETCH_ASSOC);

            if ($dreamBadge) {
                setcookie('key', $dreamBadge['badge_key'], time() + 3600, '/');
            }

            echo "<script>alert('Login Success!');location.href='/';</script>";
        } else {
            $errorMessage = "Wrong ID or Password.....";
        }
    }
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <link rel="stylesheet" href="/static/css/login.css">
</head>
<body>
    <div class="login-container">
        <h2>로그인</h2>
        <?php if ($errorMessage): ?>
            <div class="error-message"><?php echo $errorMessage; ?></div>
        <?php endif; ?>
        <form action="login.php" method="POST">
            <input type="text" name="username" placeholder="아이디" required>
            <input type="password" name="password" placeholder="비밀번호" required>
            <button type="submit">로그인</button>
        </form>
    </div>
</body>
</html>

 

 

....그냥 평범해보이는데...?

 

큐앤에이에도 다들 web 해킹 이것저것 시도하고 뭔지 질문하시는듯

 

에...뭘까?

curl -b "PHPSESSID= efcb2c0d7f4c693fe0c659137202c5a7 " http://host1.dreamhack.games:18772/view_badge.php

 

Login

 

host1.dreamhack.games:18772

세션은 아니고...

 

C:\WINDOWS\system32>curl -s http://host1.dreamhack.games:18772/static/badge.png
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.62 (Debian) Server at server Port 80</address>
</body></html>

 

캐싱도 아니야...

 

 

안돼...