2025 WEB 정규팀 3주차

유원상·5일 전
0

SF Web Regular

목록 보기
3/3

Blind Command Injection CTF Challenge

블라인드 커맨드 인젝션 취약점을 활용한 CTF 문제입니다.

문제 설명

명령어 출력을 바로 반환하지 말고 파일로 저장하게 해서 blind command injection 하게 합니다.
flag는 flag.py 에 있습니다.

index.html

메인 페이지(/)에 명령어 입력 칸이 있습니다.
여기가 커멘드 인젝션 공격 칸입니다. uploads 페이지로 이동할 수 있습니다.

uploads.html

upload 엔드포인트에 저장된 파일을 출력해주는 페이지가 존재합니다. 또한 파일 업로드 기능도 지원합니다. 그러나
업로드 기능은 현재 비활성화되었습니다. 라고 출력하여 우리가 의도하지 않은 파일은 올라올 수 없도록 합니다.

app.py

로직 구현

.gitkeep

비어 있는 파일의 안정성 보장

실행 방법

docker compose up

공략 방법

플래그 파일을 읽어서 uploads 디렉토리에 전달해 문제를 해결할 수 있습니다.

cat flag.py > /uploads/output.txt ;

핵심 취약점

명령어 실행 결과가 화면에 표시되지 않지만(Blind), 파일 시스템을 이용해 결과를 확인할 수 있습니다.

app.py

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>
profile
이것저것 적는 블로그입니다.

0개의 댓글