File Upload Vulnerability
: 공격자가 원하는 파일 / 악성 확장자를 갖는 파일을 서버의 파일 시스템 상 임의 경로에 업로드
1. 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()
/fileUpload는 POST 요청을 받으면, 클라이언트가 전송한 파일을 ./uploads에 저장한다.
그런데 이용자가 입력한 파일 이름 f.filename을 그대로 사용하기 때문에 Path Traversal에 취약하다.
▶ 정상적인 요청
파일을 정상적으로 업로드하면 아래와 같은 HTTP 요청이 전송된다.
이 요청의 filename값은 위 취약 코드 내 f.filename가 됨.
그러면 아래와 같이, uploads 디렉토리에 test.txt가 생성됨.
▶ 공격(악의적인 요청)
특정 디렉터리에만 업로드를 허용하는 제한이 없다면,
악의적인 이용자가 Path Traversal을 통해 임의 디렉터리에 파일을 업로드함으로써
웹 서버의 소스 코드나 서버에 있는 중요 시스템 파일을 덮어 쓸 수 있다.
아래도 파일 업로드 HTTP 요청인데, filename 필드가 변조되어 path traversal을 한다.
이 요청을 받으면 app.py 파일 위치와 같은 디렉토리에 hack.py가 생성됨
정말 공격을 당했는지 서버 파일 시스템을 확인해보자.
만약 hack.py가 app.py를 덮어쓴다면,
서버가 재실행될 때 임의의 파이썬 코드를 실행할 수 있다.
2. 악성 파일 업로드
▶ 취약한 코드
>> 악성 파일 업로드 모듈 예시 코드
<?php
if(!empty($_FILES['file'])){
$filename = "user_uploaded_file_".time();
$ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
$path = "./uploads/" . $filename . "." . $ext;
if(move_uploaded_file($_FILES['file']['tmp_name'], $path)) {
return true; // upload success
} else {
return false; // upload fail
}
}
?>
파일 이름을 임의의 파일명+"."+확장자 로 생성하므로 Path Traversal은 불가능하지만,
이용자가 올린 확장자를 별다른 검사없이 사용하므로 php 웹셸을 업로드할 수 있다.
그리고 html파일을 업로드하여 Stored XSS를 수행할 수도 있다.
웹서버는 php를 CGI로 실행하여 결과를 반환
웹서버는 .php, .jsp, .asp와 같은 확장자의 파일 (이것이 즉 웹쉘로서 작용)을 CGI(Common Gateway Interface)(: 동적인 컨텐츠를 처리하기 위해서, 웹 서버 - php 같은 외부의 프로그램 간 인터페이스를 제공하는 프로토콜)로 실행하고,
그 결과를 이용자에게 반환한다.
아래 Apache 설정 파일은
이용자가 요청한 파일의 확장자가 정규표현식 ".+\.ph(p[3457]?|t|tml)$"를 만족하면
(.php, .php3, .phtml이 이를 만족)
x-httpd-php로 핸들링하게 한다.
x-httpd-php는 PHP 엔진이고, 요청한 파일을 실행한 뒤 그 결과를 반환한다.
웹브라우저의 요청 처리 방식
웹 브라우저는 파일의 확장자나 응답의 Content-Type에 따라 요청을 다양하게 처리한다:
요청한 파일의 확장자가 .html 이거나, 반환된 Content-Type 헤더가 text/html일 경우 ㅡ 응답은 HTML 엔진으로 처리,
파일의 확장자가 .png, .jpg 등의 이미지 확장자이거나, Content-Type이 image/png일 경우 ㅡ 이미지로 렌더링
▶ 공격
웹쉘 이용
공격자가 임의의 php 소스 파일 (즉 웹셸)을 .php 확장자로 업로드하고, GET 요청을 보낼 수 있다면
CGI에 의해 해당 코드가 실행되도록 할 수 있다.
아래 php파일을 업로드: 임의 시스템 명령어 실행을 위한 exploit.php
아래는 업로드 결과이다.
html 이용한 XSS 공격
만약 공격자가 서버에 exploit.html을 업로드하고,
이에 접근하는 URL이
https://dreamhack.io/uploads/exploit.html
이라면,
브라우저는 이를 HTML로 해석하는데,
만약 이 exploit.html에 악의적인 스크립트가 있다면,
Cross-Site-Scripting (XSS) 공격으로 이어질 수 있다.
아래 exploit.html을 업로드
아래는 업로드 결과이다.
업로드 취약점을 막으려면
- 개발자는 업로드 디렉터리를 웹 서버에서 직접 접근할 수 없도록 함
- 업로드 디렉터리에서는 CGI가 실행되지 않도록 함
- 업로드된 파일 이름을 그대로 사용하지 않고, basepath와 같은 함수를 통해 파일 이름을 검증한 후 사용
- 허용할 확장자를 명시해 그 외 확장자는 업로드될 수 없도록 함
File Download Vulnerability
공격자가 다운로드할 파일의 이름을 임의로 정할 수 있는 Path Traversal 취약점이 있다면,
공격자는 서버 파일 시스템에 존재하는 임의 파일 (설정 파일, 패스워드 파일, 데이터 베이스 백업본, 외부 API 서버의 비밀키 등)을 다운로드할 수 있다.
▶ 예시 서버 코드
# ...
Secret = os.environ("Secret")
# ...
@app.route("/download")
def download():
filename = request.args.get("filename")
content = open("./uploads/" + filename, "rb").read()
return content
# ...
▶ 공격
목적: Secret 값을 획득하기
프로세스의 환경 변수는 /proc/[pid]/environ, /proc/self/environ에서 확인할 수 있으며,
만약 이용자가 프로세스를 호출하기 전에 환경 변수를 bash의 명령어를 통해 설정했다면, .bash_history에서도 이를 알 수 있다.
아래와 같이, .bash_history를 읽으면 이용자가 export 명령어로 설정한 Secret 값을 알 수 있다.
※ 파일 다운로드 취약점이 자주 발생하는 URL 패턴
다운로드 취약점을 막으려면
- 요청된 파일 이름을 basepath과 같은 함수를 통해 검증
- 파일 이름과 1:1 맵핑되는 키를 만들어 이용자로부터 파일 이름이 아닌 키를 요청하도록 함
- 이용자가 업로드한 파일을 다운로드 받거나 이미지를 불러올 때 특정 디렉터리에 있는 파일만 접근하도록 제한
'IT > 웹 보안' 카테고리의 다른 글
[IDOR] (0) | 2024.08.27 |
---|---|
[Server Side Request Forgery (SSRF)] (0) | 2024.06.08 |
[Command Injection] 발생 원리, exploit 예시, 예방 (0) | 2024.06.08 |
[No SQL Injection] Simple Injection, Blind Injection (0) | 2024.06.08 |
[SQL Injection #1] Simple Injection, Blind Injection (2) | 2024.06.08 |