모의해킹/웹해킹

파일 업로드 취약점

여긴어디나는누구 2016. 9. 2. 09:06

1.      개요

 

1.1     파일 업로드 취약점

파일 업로드 취약점은 파일 첨부를 할 수 있는 게시판에 일반적으로 허용된 파일 (이미지 파일, 문서파일) 이외의 악의적인 스크립트가 포함된 소스파일 (.jsp , .php, .asp .. ) 을 첨부 할 수 있게 되면 공격자는 악성 스크립트 업로드 후 서버상에서 스크립트를 실행시켜 쉘 획득, 서버변조 등 웹서버를 장악할 수 있다.

  

 

2.   점검 방법

2.1     STEP 1

파일 첨부 기능이 있는 페이지를 찾는다.

ex ) 게시판, 자료실 등

 

 

2.2     STEP 2

첨부기능이 존재하는 경우 지정된 확장자 이외의 파일을 업로드 할 수 있는지 확인한다.

ASP , ASPX , JSP , PHP , CER , CDX , ASA , PHP3 , WAR

클라이언트 소스에서 방어코드가 있을 경우, 프록시 등을 이용해 우회해서 확인

 


 

2.3     STEP 3

파일 업로드가 가능할 경우 업로드 된 파일에 접근이 가능한지, 실행이 가능한 지 확인

접근이 가능한 경우 피싱페이지로 활용될 수 있고, 실행까지 가능한 경우 시스템의 제어권한을 획득 가능하다.

3.   조치 권고안

3.1     일반 권고안

[1] 서비스를 위해 허용하고 있는 파일의 확장자 외에는 업로드가 불가능 하도록 해야 하며 스크립트 파일의 업로드 및 실행을 금지 시킨다. ( 파일 업로드 기능이 있는 모든 곳에 적용 )

 

[2] 사용자가 파일을 업로드 할 때 확장자의 적합성 여부를 검사하는 루틴은 서버측에서 구현

 

[3] 파일명 및 확장자 검증을 우회하는 것을 방지하기 위하여 대소문자를 구분하지 않고 문자열을 비교해야 하며 특수문자가 포함되어 있을 경우 업로드를 금지시킨다.

※ 우회기법의 예 : “shell.Asp”, “shell. aSp”, “shell.asP”, “shell.asp%00.jpg”, “shell.asp\00.jpg”

 

[4] 다운로드 스크립트를 구성할 때, 파일명을 Parameter 로 받는 것 보다 파일이름은 데이터베이스에 저장하고 해당 index만을 Parameter로 받아서 사용해야 한다.

 

[5] 아주 작거나 큰 파일을 처리하는 로직을 포함해야 하고, 임시 디렉토리에서 업로드 된 파일을 지우거나 다른 곳으로 이동시켜야 한다.

 

[6] 웹 서버 엔진 설정 시 업로드 된 디렉토리의 Server Side Script 언어의 실행 권한을 제거하고 업로드 된 파일명, 확장자 난수화 하여 변경

 

[7] 업로드 된 파일을 루트 디렉토리와 동일한 수준에 존재하는 다른 디렉토리에 저장하거나, URL 요청에 의해서 직접 접근 (http://[hostname]/[upload_dir]/[uploadfile name]) 이 불가능한 곳에 저장하며, 상위 폴더 경로 이동을 제한한다.

 

[8] 업로드 파일의 컨텐츠는 문자 검색을 하여 저장을 제한한다. (request , eval , exec , cmd)

※ 웹쉘에 자주 이용되는 문구는 문서 아래 별첨 1 로 첨부

[9] 상용 , 오픈소스 에디터의 경우 정기적으로 업데이트 하여 사용한다.

※ 에디터 관련 문서는 문서 아래 별첨2 로 첨부

 

3.2     어플리케이션 별 권고안

※ 아래 코드는 예로서 제시하는 것으로 구현시 다를 수 있음. 충분히 테스트 한 후 적용할 것을 권고함

 

ASP

<%

Set Up = Server.CreateObject("SiteGalaxyUpload.Form")

Path1 = server.mappath(".") & "\upload\"

 

Fname = Up("file1")

 

if Fname <> "" then' 파일 첨부가 되었으면

 

if Up("file1").Size > 10240 then' 용량 제한

Response.Write "용량 초과"

Response.End

end if

 

if Up("file1").MimeType <> "image" then' 이미지만 업로드 허용

Response.Write "이미지 파일이 아닙니다."

Response.End

end if

 

Filename=Mid(Fname,InstrRev(Fname,"\")+1)'파일이름부분 추출

 

' 중복시에 파일이름부분을 변경하기 위해 분리를 한다

Farry=split(Filename,".")'.을 기준으로 분리

preFname=Farry(0)'파일이름 앞부분

extFname=Farry(1)'파일의 확장자

 

' 저장할 전체 path를 만든다, 파일이름을 구한다

Path2 = Path1 & Filename

saveFname=preFname & "." & extFname

 

Set fso = CreateObject("Scripting.FileSystemObject")

countNo = 0' 파일 중복될경우 셋팅 값

fExist=0' 같은 이름의 파일 존재 체크

 

Do until fExist = 1

If(fso.FileExists(Path2)) Then

countNo = countNo + 1

Path2 = Path1 & preFname & countNo & "." & extFname

saveFname=preFname & countNo & "." & extFname

else

fExist=1

End If

Loop

Up("file1").SaveAs(Path2)

response.write(saveFname & " 저장완료")

else

response.write("Error")

end if

Set Up = nothing

%>

 

PHP

<?php

$uploaddir = '/var/www/uploads/';

 

//파일 사이즈가 0byte 보다 작거나 최대 업로드 사이즈보다 크면 업로드를 금지 시킨다.

if($_FILES['userfile']['name'])

if($_FILES['userfile']['size'] <= 0)  // 최대 업로드 사이즈 체크 삽입

print "파일 업로드 에러";

exit;

 

//파일 이름의 특수문자가 있을 경우 업로드를 금지 시킨다.

if (eregi("[^a-z0-9\._\-]",$_FILES['userfile']['name']))

print "파일 이름의 특수문자 체크";

exit;

 

//파일 확장자중 업로드를 허용할 확장자를 정의한다.

$full_filename = explode(".", $_FILES['userfile']['name']);

$extension = $full_filename[sizeof($full_filename)-1];

/* PHP의 경우 확장자 체크를 할 때 strcmp(확장자,"php3"); 로 체크를 하게 되면

pHp3 이나 phP3는 구별을 하지 못하게 되므로 strcasecmp처럼 대소문자 구별을 하지

않고 비교하는 함수를 사용한다. 또한 .을 기준으로 하여 확장자가 하나로 간주하고

프로그램을 할 경우 file.zip.php3 이라고 올린다면 zip파일로 인식하고 그냥 첨부가

되므로 아래와 같이 제일 끝에 존재하는 확장자를 기준으로 점검하도록 한다. */

 

$extension= strtolower($extension);

if (!( ereg($extension","hwp") || ereg($extension","pdf") || ereg($extension","jpg")) )

print "업로드 금지 파일 입니다";

exit;

 

$uploadfile = $uploaddir. $_FILES['userfile']['name'];

if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile))

print "파일이 존재하고, 성공적으로 업로드 되었습니다.";

print_r($_FILES);

else

print "파일 업로드 공격의 가능성이 있습니다! 디버깅 정보입니다:\n";

print_r($_FILES);

?>

 

JSP

<%@ page contentType="text/html;charset=euc-kr" %>

<%@ page import="com.oreilly.servlet.MultipartRequest,com.oreilly.servlet.multipart. DefaultFileRenamePolicy, java.util.*"%>

<%

String savePath="/var/www/uploads"; // 업로드 디렉토리

int sizeLimit = 5 * 1024 * 1024 ; // 업로드 파일 사이즈 제한

 

try

MultipartRequest multi=new MultipartRequest(request, savePath, sizeLimit, "euc-kr", new DefaultFileRenamePolicy());

Enumeration formNames=multi.getFileNames();  // 폼의 이름 반환

String formName=(String)formNames.nextElement();

String fileName=multi.getFilesystemName(formName); // 파일의 이름 얻기

 

String file_ext = fileName.substring(fileName.lastIndexOf('.') + 1);

if(!( file_ext.equalsIgnoreCase("hwp") || file_ext.equalsIgnoreCase("pdf") || file_ext.equalsIgnoreCase("jpg")) )

out.print("업로드 금지 파일");

 

if(fileName == null)

out.print("파일 업로드 실패");

 else

fileName=new String(fileName.getBytes("8859_1"),"euc-kr"); // 한글인코딩

out.print("File Name  : " + fileName);

 

 catch(Exception e)

 

3.3     시스템 설정 권고안 ( IIS )

업로드 된 파일이 존재하는 디렉토리에서는 에플리케이션이 실행될 수 없도록 실행권하능 제거 한다.

 

[1] 인터넷정보 서비스 à 업로드 폴더 à 속성 à 등록정보 / 디렉토리 à 실행권한 없음 ( 2003 경우 )

 

 

[2] 인터넷 정보 서비스 à 업로드 폴더 선택 à 처리기 매핑 à 사용권한 편집 à 스크립트 해제 ( 2003 이상 )


 

 

 

3.4     시스템 설정 권고안 ( Apache )

[1] Apache 설정 파일인 httpd.conf 에 해당 디렉토리에 대한 문서 타입을 컨트롤

 

<Directory "업로드 디렉토리 경로 ">

           AllowOverride FileInfo (또는 All)….

           …

<Directory>

 

[2] 파일 업로드 디렉토리에 .htaccess 파일을 만들고 다음과 같이 AddType 지시자를 이용, 현재 서버에서 운영되는 Server Side Script 확장자를 text/html MIME Type을 재조정 하여 업로드 된 Server Side Script가 실행 되지 않도록 설정 또는 FileMatch 지시자를 이용하여, *.ph, *.inc, *lib 등의 Server Side Script 파일에 대해서 직접 URL 호출을 금지

<.htaccess>

<FilesMatch "\.(ph|inc|lib)">

 Order allow, deny

 Deny from all

</FilesMatch>

AddType text/html .html .htm .php .php3 .php4 .phtml .phps .in .cgi .pl .shtml .jsp

 

주의 사항

[1] 파일이 업로드 되는 디텍토리에 운영상 필요한 Server Side Script가 존재하는지 확인한다.

 

[2] 파일 다운로드 프로그램을 사용하지 않고 직접 URL 호출을 통해 파일을 다운받는 경우 FileMatch 지시자를 사용하면 차단 설정한 확장자의 파일 다운로드는 거부된다.

 

 

 


 

 

4.   별첨 1 ( 웹쉘 키워드 정리 )

4.1     자주 사용되는 웹쉘 문구의 정규식 표현의 예

 

1. [^a-zA-Z_]eval *\(.*_POST *\[.*\]

2. ` *(\$cmd|\$pwd|\$command|\$exec|ps -aux|\$cmdline|\$Mcmd) *`

3. CreateObject\("w.*?"(?:&|\+)"[script"&+]+\.("(?:&|\+)")?s.*?"(?:&|\+)"[hel"&+]+l"\)

4. Response\.write\("<option>(oracle\sodbc|microsoft\saccess|mysql

\sodbc|sql\sserver).*?</option>"\)

5. exec\s+(?:master)?(?:\.)?(?:dbo)?(?:\.)?xp_cmdshell\s+

6. Set\s[\w=\s]+New\s\w+:\w+\.(DelFile|EditFile|CopyFile|

MoveFile|DelFolder|CopyFolder|MoveFolder

|NewFolder)\(\w+\)

7. javascript:FullForm\("[\w&\s.,"()\\]+(DownFile|EditFile|DelFile|CopyFile|MoveFile)""\)

8. CreateObject\("s.*?"(?:&|\+)"[ripting"&+]+\.("(?:&|\+)")?F.*?

("(?:&|\+)")[ilesystemobjec"&+]+t"\)

9. ACTION=["']{1}\?[\w\d=&]+<%String\s[\w\d]+=config

\.getServletContext\(\)\.getRealPath\("/"\)

10. <%=\s*"\\\\\"[\s&\w]+\.ComputerName

[&\s]+"\\"[&\s\w]+\.UserName\s+%>

11. SELECT\sCount\(\*\)\sFROM\sMASTER\.DBO\.SYSOBJECTS

\sWHERE\sXTYPE='X'\sAND\sNAME='XP_(CMDSHELL|SERVICECONTROL|REGWRITE)

12. (?<=m[y|s]sql_query\()["']+(SHOW)?\s?CREATE TABLE [`'". ]*(tmp_file|temp_r57_table|r57_temp_table|xploit|\$_POST|\$tab)

13. value=<\?[\w\s]*?base64_decode\(\$\w+\)\s\?>

14. exec\(.*?basename\(\$\w+\).*?/bin/sh',\s\$\w+,\s\$\w+

15. find[\s/]+(?:-user|-type)[-\sa-z]*(-perm|-type)\s(?:777|d)