IT/웹 보안

[File Upload/Download Vulnerability]

kykyky 2024. 6. 8. 08:40

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 맵핑되는 키를 만들어 이용자로부터 파일 이름이 아닌 키를 요청하도록 함
- 이용자가 업로드한 파일을 다운로드 받거나 이미지를 불러올 때 특정 디렉터리에 있는 파일만 접근하도록 제한