서버를 생성하고 문제 파일 다운로드
페이지를 살펴보면 메모를 업로드하라는 index.html 페이지와 Upload My memo 페이지를 확인할 수 있습니다
메모를 업로드하라고 하니 업로드해줍시다
메모를 작성하고 메모지 페이지로 접속해 보니 파라미터 값에 우리가 앞서 작성한 '무사' 이름이 그대로 표시되어 있습니다
즉 URL 파라미터 값의 디렉터리 접근 공격을 이용하여 파일을 다운로드할 수 있습니다 ../ (상위 경로) 하나 입력했을 때는 존재하지 않는 경로라고 표시되지만 flag.py 파일을 찾아야 하므로 ../flag.py를 입력해 봅시다
그러면 이렇게 flag 값을 반환하는 걸 알 수 있습니다
해당 문제는 디렉터리 접근 공격으로 파일을 다운로드할 수 있다는 걸 알려주기 위해 쉽게 제출한 문제인 거 같습니다
#!/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 값은 쉽게 알아냈지만 왜 flag 값이 반환되는지 알아보기 위해 소스 코드를 해석해 봅시다
'/' 경로는 os 모듈에서 listdir 함수를 사용하는데 이 함수는 UPLOAD_DIR = 'uploads' 안에 있는 파일과 디렉터리 목록을 가져오는 역할을 합니다
'/upload' 경로는 우리가 Upload My memo 항목에서 메모를 작성할 때 POST 방식으로 메모를 작성합니다 하지만 파일명에 .. 문자열이 포함되어 있으면 메모 작성을 중지하고 'bad characters,,' 문구를 반환합니다 작성된 메모를 클릭하면 GET 방식으로 upload.html 템플릿을 렌더링 하여 클릭한 파일에 내용을 보여줍니다
'/read' 경로는 우리가 작성한 메모 페이지에서 name 파라미터 값을 전달받아 UPLOAD_DIR의 지정된 업로드 디렉터리에서 파일 내용을 읽어 반환합니다 앞서 '무사'라는 파일명은 /upload 경로에서 작성하여 현재 경로에서 '무사'라는 파일명 내용을 확인할 수 있습니다 결국 지정된 업로드 디렉터리 내부에 flag.py 파일이 존재했기 때문에 name 파라미터 값을 ../flag.py로 입력하면 flag 값을 확인할 수 있었습니다
[ 선수 학습 내용 ]