블라인드 커맨드 인젝션 취약점을 활용한 CTF 문제입니다.
명령어 출력을 바로 반환하지 말고 파일로 저장하게 해서 blind command injection 하게 합니다.
flag는 flag.py 에 있습니다.
메인 페이지(/
)에 명령어 입력 칸이 있습니다.
여기가 커멘드 인젝션 공격 칸입니다. uploads 페이지로 이동할 수 있습니다.
upload 엔드포인트에 저장된 파일을 출력해주는 페이지가 존재합니다. 또한 파일 업로드 기능도 지원합니다. 그러나
업로드 기능은 현재 비활성화되었습니다. 라고 출력하여 우리가 의도하지 않은 파일은 올라올 수 없도록 합니다.
로직 구현
비어 있는 파일의 안정성 보장
docker compose up
플래그 파일을 읽어서 uploads 디렉토리에 전달해 문제를 해결할 수 있습니다.
cat flag.py > /uploads/output.txt ;
명령어 실행 결과가 화면에 표시되지 않지만(Blind), 파일 시스템을 이용해 결과를 확인할 수 있습니다.
from flask import Flask, request, render_template, session
import os
app = Flask(__name__)
app.secret_key = os.urandom(32) # 세션 암호화 키
os.makedirs('uploads', exist_ok=True) # 앱 시작 시 uploads 폴더가 자동으로 만들어짐
@app.route('/', methods=['GET', 'POST']) # 메인 페이지
def index():
if request.method == 'POST': # POST 요청일 때 실행
cmd = request.form.get('cmd', '')
os.system(cmd) # 명령어 실행
return render_template('index.html')
@app.route('/uploads') # 파일 업로드 페이지
def uploads():
files = os.listdir('uploads')
files = [f for f in files if f != '.gitkeep'] # .gitkeep 파일을 제외한 나머지 파일만 남김
return render_template('uploads.html', files=files) # uploads.html을 렌더링하고 files 리스트 전달
@app.route('/uploads/<filename>') # 파일 내용 확인
def view_file(filename): # filename을 인자로 받음
if '..' in filename or '/' in filename or '\\' in filename: # 디렉토리 트래버셜 방지
return "Access denied!", 403
try:
with open(f'uploads/{filename}', 'r', encoding='utf-8', errors='ignore') as f: # 파일 읽기
content = f.read()
return f'<pre>{content}</pre><br><a href="/uploads">Back</a>' # 내용 출력
except FileNotFoundError: # 파일이 없을 때
return "File not found!", 404
except Exception as e: # 그 외 모든 에러
return f"Error: {str(e)}", 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Command Injection</title>
</head>
<body>
<h1>Command Injection</h1>
<form method="POST">
<input type="text" name="cmd" placeholder="Enter command..." required>
<button type="submit">submit</button>
</form>
<hr>
<p><strong>Hint</strong></p>
<ul>
<li>없습니다.</li>
</ul>
<p><a href="/uploads">View Uploads</a></p>
</body>
</html>
uploads.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Uploads</title>
</head>
<body>
<h1>Uploads Directory</h1>
<p><strong>Notice:</strong> 파일 업로드 기능은 현재 사용할 수 없습니다.</p>
<hr>
{% if files %}
<h2>Files:</h2>
<ul>
{% for file in files %}
<li><a href="/uploads/{{ file }}">{{ file }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No files found</p>
<p><em>존재하는 파일은 이곳에 표시됩니다.</em></p>
{% endif %}
<hr>
<p><a href="/">Back to Home</a></p>
</body>
</html>