파일 공유 서비스를 개발할 때, 이용자가 업로드한 파일을 데이터베이스에 저장하는 것보다는 서버의 파일 시스템에 저장하는 것이 개발하기 쉽고, 관리 효율도 높다 그러나 반대로, 임의 파일이 다운로드 되는 취약점이나, 악성 웹셸 파일을 업로드하여 임의 코드를 실행할 수 있는 취약점을 발생시키기도 한다
파일 업로드 취약점(File Upload Vulnerability)은 웹 서비스를 통해 이용자의 파일을 서버의 파일 시스템에 업로드하는 과정에서 발생하는 보안 취약점이며, 이용자가 업로드될 파일의 이름을 임의로 정할 수 있을 때 발생한다
파일 이름에 이용자가 입력한 문자열을 그대로 사용하거나, 이용자의 이메일, 닉네임 등을 포함시키는 등의 소스 코드 패턴이 이러한 취약점을 발생시킬 수 있다
파일 업로드 취약점은 크게 Path Traversal과 악성 파일 업로드로 분류됨 파일 업로드를 허용하는 대개의 서비스는 보안을 위해 특정 디렉터리에만 업로드를 허용
만약 이러한 제한이 없다면, 악의적인 이용자가 웹 서버의 소스 코드나 서버에 있는 중요 시스템 파일을 덮어 쓸 위험이 있음 Path Traversal 취약점은 업로드에 존재하는 이러한 제약을 우회하여, 임의 디렉터리에 파일을 업로드할 수 있는 취약점을 말함
from flask import Flask, request
app = Flask(__name__)
@app.route('/fileUpload', methods = ['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
f.save("./uploads/" + f.filename)
return 'Upload Success'
else:
return """
<form action="/fileUpload" method="POST" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit"/>
</form>
"""
if __name__ == '__main__':
app.run()
위는 파일 업로드 기능이 취약한 웹 서비스의 코드
(이용자가 입력한 파일 이름 f.filename을 그대로 사용하기 때문에 Path Traversal에 취약)
예를 들어 공격자가 ../와 같은 메타문자를 입력하면 uplads를 벗어나 상위 디렉터리에도 파일을 업로드할 수 있음
이용자가 파일을 업로드할 때, 이를 제대로 검사하지 않아서 발생하는 취약점을 말함
웹 서버는 .php, .jsp, .asp와 같은 확장자의 파일을 Common Gateway Interface(CGI)로 실행하고, 그 결과를 이용자에게 반환
많은 웹 서버들이 php파일에 대해 위와 같은 핸들링을 지원함
따라서 공격자가 임의의 php 소스 파일을 .php 확장자로 업로드하고, GET 요청을 보낼 수 있다면 CGI에 의해 해당 코드가 실행되도록 할 수 있음
웹 브라우저는 파일의 확장자나 응답의 Content-Type에 따라 요청을 다양하게 처리한다
만약 요청한 파일의 확장자가 .html 이거나, 반환된 Content-Type 헤더가 text/html일 경우 응답은 HTML 엔진으로 처리됨
또, 파일의 확장자가 .png, .jpg등의 이미지 확장자이거나, Content-Type이 image/png일 경우에는 이미지로 렌더링됨
만약 공격자가 서버에 exploit.html을 업로드하고, 이에 접근하는 URL이 https://dreamhack.io/uploads/exploit.html이라면, 브라우저는 이를 HTML로 해석됩니다. exploit.html에 악의적인 스크립트를 삽입하면, Cross-Site-Scripting (XSS) 공격으로 이어질 수 있다
Common Gateway Interface(CGI)란?
동적 웹페이지의 동적인 컨텐츠를 처리하기 위해서 웹 서버와 php 같은 외부의 프로그램에 사이에서 인터페이스를 제공하는 프로토콜
파일 다운로드 취약점(File Download Vulnerability)은 웹 서비스를 통해 서버의 파일 시스템에 존재하는 파일을 내려 받는 과정에서 발생하는 보안 취약점이며, 이용자가 다운로드할 파일의 이름을 임의로 정할 수 있을 때 발생
웹 서비스는 이용자가 업로드한 파일을 다운로드 받거나 이미지를 불러올 때 특정 디렉터리에 있는 파일만 접근하도록 해야 한다
Path Traversal을 이용한 파일 다운로드 취약점은 파일 이름을 직접 입력 받아 임의 디렉터리에 있는 파일을 다운로드 받을 수 있는 취약점을 말함
아래는 파일 다운로드 취약점이 자주 발생하는 URL 패턴
파일 다운로드 취약점이 자주 발생하는 URL 패턴
https://vulnerable-web.dreamhack.io/download/?filename=notes.txt
https://vulnerable-web.dreamhack.io/download/?filename=../../../../../../etc/passwd
https://vulnerable-web.dreamhack.io/images.php?fn=6ed0dd02806fa89e233b84f4.png
#!/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)
Flask 애플리케이션에서 발생할 수 있는 경로 탐색 공격(path traversal attack) 취약점을 해결하기 위해 몇 가지 보완 작업을 적용해야 합니다. 경로 탐색 공격은 공격자가 파일 이름에 ../와 같은 문자를 포함시켜 애플리케이션이 의도하지 않은 디렉토리에 접근하게 만듭니다. 이를 통해 민감한 정보에 접근하거나 파일을 수정할 수 있습니다.
파이썬의 os.listdir()
지정한 디렉터리 혹은 현재 작업 디렉터리의 모든 파일과 디렉터리 명을 반환

왜 ../flag.py는 저장이 안 되는데 url에 ../flag.py라고 직접 입력하면 되는 거지?
ㄴ 이미 불러온 디렉터리 내에 flag.py가 존재하기 때문에
만약 gaza.py처럼 내가 등록한 것도 아니고 기존 디렉터리에 있는 파일이 아닌 이름을 입력하면

이렇게 된다
문제에서 flag.py를 다운받으라고 했으니 해당 파일은 이미 존재하는 것
