문제 정보
File Download 취약점이 존재하는 웹 서비스입니다.
flag.py를 다운로드 받으면 플래그를 획득할 수 있습니다.
풀이 힌트
1. 공격 백터 파악
2. Path Traversal Attack
문제 풀이
들어가보니, 기능이 Upload My Memo라는 Upload 기능일 거 같은 메뉴가 하나있었다.
해당 페이지의 공격 백터는 filename과 content이다.
일단 업로드를 하면 어떻게 작동하는지 확인을 해보도록 하겠다.
업로드를 한 결과 Home 페이지에 내가 filename에 넣은 값이 리스트로 들어가 있었다.
들어가보니, Get 메소드로 name을 넘겨주는 것을 확인하였다.
이 또한 사용자가 값을 입력할 수 있으니, 공격 백터에 추가하겠다.
내가 테스트로 넣은 값들은 잘 나오고 있었다.
내가 현재 찾은 공격 백터는 아래와 같다.
- upload 페이지 : filename, content → Post
- read 페이지 : name → Get
일단 나는 Path Traversal 취약점이 생각이 났고, read 페이지에 넣어봤다.
그 결과 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)
해당 코드를 보니, 아주 간단하게 구성이 되어있었다.
@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')
위 코드는 Upload 페이지의 코드이다.
클라이언트에서 서버로 filename과 content를 Post로 보내면, uploads 폴더 안에 filename으로 만들어진 파일이 생성된다.
해당 파일의 내용은 content가 채우게 된다. 그 후 Home 페이지로 리다이렉트가 된다.
업로드 같은 기능은 공격자에게 업로드되는 경로, 업로드되는 파일 이름이 노출이 되면, 업로드 공격으로 이어질 수 있다.
또한 해당 문제 풀이와 같이 의도치 않는 파일을 다운로드하거나 읽을 수 있기 때문에 업로드되는 경로와 업로드되는 파일 이름은 공격자가 알지 못하도록 해야한다.
위 코드에서 재미난 부분이 있는데, 파일 이름에 ..이 포함되어 있다면 bad characters를 출력하도록 한 것이다.
아마도 문제가 Download 공격이니, 읽어오는 기능에서 공격하도록 하고 싶은게 아니였을까....?
@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)
위 코드는 read 페이지의 코드이다.
Get 메소드로 name에 파일 이름을 넣으면 uploads 폴더에 있는 해당 파일을 읽어서 출력하는 기능이다.
하지만, 이 기능에선 업로드에선 검증하였던 ..을 하지 않아서 Path Traversal 취약점이 발생하였다.
'Wargame > Dreamhack' 카테고리의 다른 글
[Dreamhack Web - Lv 1] csrf-1 (0) | 2021.10.25 |
---|---|
[Dreamhack Web - Lv 1] proxy-1 (0) | 2021.10.25 |
[Dreamhack Web - Lv 1] xss-1 (0) | 2021.10.23 |
[Dreamhack Web - Lv 1] welcome (0) | 2021.10.15 |
[Dreamhack Web - Lv 1] simple_sqli (0) | 2021.10.15 |
Comment