본문 바로가기
SK Shieldus Rookies 19th/애플리케이션 보안

[SK shieldus Rookies 19기][애플리케이션 보안] - Bee box, Upload

by En_Geon 2024. 3. 31.

1. Upload 취약점

파일 업로드 기능이 제공되는 경우, 파일의 크기와 개수, 종류들 제한하지 않고, 외부에서 접근할 수 있는 경로에 업로드 파일을 저장하는 경우 발생

 

1) 가이드

 

  • 파일 크기와 개수를 제한하지 않는 경우
    • 서버의 연결 및 디스크 자원을 고갈시켜 정상적인 서비스를 방해
  • 종류를 제한하지 않는 경우
    • 바이러스와 같은 파일을 업로드해서 해당 서버를 악성코드 유포지로 활용
    • 서버에서 실행할 수 있는 파일을 업로드해서 실행
    • 웹쉘(WebShell)
  • 외부에서 접근할 수 있는 경로에 업로드 파일을 저장하는 경우
    • URL을 통해서 접근할 수 있는 경로에 위치
    • Web Document Root 아래에 위치

 

2) 파일 업로드 기능 확인 방법

form 태그 설명

 

<form enctype="multipart/form-data"	method="post" ... > 
    <input type="file" ... >
</form>

 

위 코드가 파일 업로드에 사용되는 코드다. 

input type에 파일이 들어가면 enctype에는 multipart/form-data가 들어가게 되어 있다.

 

 

3) 방어 기법

 

  1. 업로드 파일의 크기와 개수를 제한
    • 설계 시 제공하는 서비스에 맞는 적절한 크기와 개수를 정의하는 것이 필요
  2. 파일의 종류를 제한
    • 업로드 가능한 파일의 종류를 미리 정의하고 정의된 범위 내에서만 업로드 허용
    • 화이트 리스트(허용 목록) 방식으로 제한
    • 파일 종류를 비교하는 방법
      • 확장자 - 파일명이 어떻게 끝나는가로 판단
      • Content--Type
      • File Signature - Magic Number
  3. 업로드 파일을 외부에서 접근할 수 없는 경로에 저장
    • Web Root(document) 밖
  4. 업로드 파일의 이름을 외부에서 알 수 없는 형태로 변경해서 알 수 없는 경로에 저장
  5. 업로드 파일의 실행 속성을 제거하고 저장

 

2. 이미지 업로드 실습

 

 

 

1) 이미지 업로드

 

 

 

here에 마우스를 올리면 위와 같은 주소가 나온다. 이 주소는 외부에서 접근할 수 있는 경로에 업로드 파일명과 동일한 이름으로 저장하고 있는 것을 볼 수 있다.

 

 

이 페이지는 php로 만들어진 페이지로 동작하고 있는데 이때 서버에서 실행할 수 있는 파일을 업로드 해본다.

 

 

2) 실행할 수 있는 파일 업로드

PHP로 만들어진 웹쉘 업로드

 

PHP로 만들어진 웹쉘은 kali에서 제공하는데 /usr/share/webshells/php 경로에 simple-backdoor.php 파일이다.

이 파일을 업로드 페이지에 업로드 하게되면 위와 같이 경로 주소가 나오게 된다.

 

 

 위에 나온 주소로 이동하게 되면 주소 뒤에 ?cmd=cat /cat/passwd를 넣으라는 사용법이 나온다.

사용법에 따라 주소를 추가로 넣게 되면 passwd의 내용이 나오는 것을 볼 수 있다.

 

 

 

3) 취약한 소스 코드 확인

 sudo gedit /var/www/bWAPP/unrestricted_file_upload.php

 

코드가 길어 내용이 길어지기 때문에 코드를 간략하게 본다. 위 파일에 들어가면 코드 원본이 있으므로 참고한다.

 

switch($_COOKIE["security_level"])
    {
        case "0" : 					
            move_uploaded_file($_FILES["file"]["tmp_name"], "images/" . $_FILES["file"]["name"]);
            break;
    }

 

이 코드는 업로드 파일을 현재 디렉터리의 images 아래에 업로드 파일명으로 저장하는 코드다. 

그렇기 때문에 위 실습에서 외부에서 접근할 수 있는 주소로 나오게 된 것이다.

 

 

(1) 보안 등급이 중간인 경우

file_upload_check_1 함수

 

function file_upload_check_1($file, $file_extensions = array("asp", "aspx", "dll", "exe", "jsp", "php"), $directory = "images")                                       
{
    $file_array = explode(".", $file["name"]);				

    // Puts the last part of the array (= the file extension) in a new variabele
    // Converts the characters to lower case
    $file_extension = strtolower($file_array[count($file_array) - 1]);	

    // Searches if the file extension exists in the 'allowed' file extensions array   
    if(in_array($file_extension, $file_extensions))			
    {
       $file_error = "Sorry, the file extension is not allowed. The following extensions are blocked: <b>" . join(", ", $file_extensions) . "</b>";

       return $file_error;
    }

    // Checks if the file already exists in the directory
    if(is_file("$directory/" . $file["name"]))				
    {
        $file_error = "Sorry, the file already exists. Please rename the file...";      
    }

    return $file_error;
}

 

 

file_upload_check_1 함수 코드인데, 중간에 많은 내용이 생략되었고 필요한 부분만 잘라서 가져온 것이기 때문에, 파일에 있는 함수를 참고하기를 바란다.

 

file_extensions로 허용되지 않는(블랙 리스트) 파일 확장자를 정의하고 있다. 

 

  • file_array = explode
    • 확장자 검증을 위해서 .을 기준으로 분리
  • file_extension = ...
    • 확장자를 소문자로 변환
  • if(in_array(...))
    • 제한 목록에 포함된 경우 오류 처리
  • if(is_file(...))
    • 동일한 파일이 존재하는지 검증

 

(2) 확장자 변경

 

위 코드를 확인했을 때 블랙 리스트로 정의가 되어 있었다. 블랙 리스트에는 PHP3가 정의되어 있지 않기 때문에 PHP3로 확장자를 변경해서 업로드 해볼 수 있다.

 

보안 등급을 중간으로 올린 후 업로드 페이지로 들어간다.

 

 

 

 

보안 등급이 중간단계인 경우 php 파일을 업로드 하려고 하면 블랙 리스트에 정의 되어 있어 오류가 발생한다.

 

 

 

 

그러나 위에서 말했듯이 확장자를 php3으로 바꿔 업로드 하면 업로드가 되는 것을 볼 수 있다.

 

 

 

 

 

블랙 리스트로 정의했을 때 발생할 수 있는 보안 사고를 보여주는 예제이다.

 

 

(3) 보안 등급이 가장 높은 경우

 

function file_upload_check_2($file, $file_extensions = array("jpeg", "jpg", "png", "gif"), $directory = "images")
{		

    // Searches if the file extension exists in the 'allowed' file extensions array   
    if(!in_array($file_extension, $file_extensions))	
    {
       $file_error = "Sorry, the file extension is not allowed. Only the following extensions are allowed: <b>" . join(", ", $file_extensions) . "</b>";

       return $file_error;
    }

 

이 코드도 많은 생략이 되었으므로 파일에서 코드를 찾아보기 바란다.

 

  • file_extensions
    • 화이트 리스트 방식으로 확장자 체한
  • if(!in_array(...))
    • 화이트 리스트에 포함되지 않은 확장자인 경우 오류 처리

 

 

 

 

 

 

 

4) 웹 루트 밖 저장

 

업로드 파일이 /data/images/myimage.gif 형태로 저장되어 있다고 가정한다.

이때 data 앞에 있는 / 이 디렉터리는 해당 서버의 루트 디렉터리를 의미한다.

그렇다면 아래와 같이 접근이 불가다.

 

<img src="/data/images/myimage.gif">

<a href="/data/images/myimage.gif" >

 

왜냐하면 위 코드의 data 앞에 있는 / 이 디렉터리는 웹 루트 디렉터리를 의미하기 때문이다. 

업로드 파일에 접근하려면 다운로드 기능을 추가해야 한다.

 

<img src="download?file=/data/images/myimage.gif">

<a href="download?file=/data/images/myimage.gif">

 

위 코드처럼 두 가지의 방법을 사용할 수 있다. download는 요청 파라미터로 전달된 경로의 파일을 읽어서 응답으로 반환한다. 이때 되도록 업로드 파일이 저장된 경로는 외부에 노출되지 않는 것이 안전하다.

 

<img src="download?file=myimage.gif">

<a href="download?file=myimage.gif">

 

여기서 download는 요청 파라미터로 전달된 파일을 해당 서버의 /data/images 디렉터리에서 읽어서 응답으로 반환한다.

경로는 서버 내부에 숨겨 놓고 파일명만 받는 것이 안전하다.

 

 

(1) 다운로드 기능 유의 사항

 

  • 지정된 경로를 벗어나서 파일을 읽지 못하도록 제한
    • 경로 조작 취약점(path traversal)에 노출

 

download?file=myimage.gif 로 만들면 좋다고 했다. 하지만 서버 내부에서 file의 경로를 검증하지 않고 그냥 사용하면 지정된 경로를 조작할 수 있는 문자열을 넣을 수 있게 된다.

 

download?file=../../../../etc/passwd 로 상위 디렉터리로 이동할 수 있는 명령어를 사용해서 루트 디렉터리로 이동해 시스템 파일에 접근할 수 있게 된다. 

 

 

(2) 취약한 코드 수정

 

        case "2" :            
                       
            $file_error = file_upload_check_2($_FILES["file"], array("jpg","png"));
            
            if(!$file_error)
            {
                
                move_uploaded_file($_FILES["file"]["tmp_name"], "/data/images/" . $_FILES["file"]["name"]);
				// 외부에서 접근할 수 없는 경로에 파일을 저장하도록 수정
            }            
            
            break;

 

 

 

if(isset($_POST["form"]))
    {

        if(!$file_error)
        {
            echo "The image has been uploaded <a href=\"/data/images/" . $_FILES["file"]["name"] . "\" target=\"_blank\">here</a>.";

	    echo "<img src=\"/data/images/" . $_FILES["file"]["name"] . "\">";
        }	//  업로드된 파일에 대한 링크(<a>)와 출력(<img>)을 저장된 경로로 수정 
		//  주소를 통해서 접근할 수 없으므로 링크도 출력도 되지 않음

 

 

(3) Bee box 가상머신에 파일 저장을 위한 디렉터리 생성

 

sudo mkdir -p /data/images
ls /data/images
sudo chown root:www-data /data/images
sudo chmod 777 /data/images

 

 

(4) Kali에서 이미지 업로드

 

 

 

웹 루트 디렉터리 아래에 존재하지 않는 디렉터리와 파일을 참조하므로 링크도 동작하지 않고 이미지도 나타나지 않는다.

 

 

(5) 다운로드 기능 추가

sudo gedit /var/www/bWAPP/download.php

 

<?php
  $target_Dir = "/data/images/";
  $file = $_GET['file'];
  $down = $target_Dir.$file;
  $filesize = filesize($down);
  
  if(file_exists($down)){
    header("Content-Type:application/octet-stream");
    header("Content-Disposition:attachment;filename=$file");
    header("Content-Transfer-Encoding:binary");
    header("Content-Length:".filesize($target_Dir.$file));
    header("Cache-Control:cache,must-revalidate");
    header("Pragma:no-cache");
    header("Expires:0");
    if(is_file($down)){
        $fp = fopen($down,"r");
        while(!feof($fp)){
          $buf = fread($fp,8096);
          $read = strlen($buf);
          print($buf);
          flush();
        }
        fclose($fp);
    }
  } else{
    ?><script>alert("존재하지 않는 파일입니다.")</script><?
  }
?>

 

 

(6) 링크와 이미지 출력 반영

 

    if(isset($_POST["form"]))
    {

        if(!$file_error)
        {

/*
            echo "The image has been uploaded <a href=\"/data/images/" . $_FILES["file"]["name"] . "\" target=\"_blank\">here</a>.";

	    echo "<img src=\"/data/images/" . $_FILES["file"]["name"] . "\">";
*/
            echo "The image has been uploaded <a href=\"download.php?file=" . $_FILES["file"]["name"] . "\" target=\"_blank\">here</a>.";

	    echo "<img src=\"download.php?file=" . $_FILES["file"]["name"] . "\">";


        }

        else
        {

            echo "<font color=\"red\">" . $file_error . "</font>";        

        }

    }

 

 

 

이미지와 링크가 출력되는 것을 볼 수 있다.

댓글