상세 컨텐츠

본문 제목

DVWA : File Upload - Impossible level

Vulnerability Assessment/Web Application

by DarkSoul.Story 2025. 1. 5. 19:26

본문

반응형

[ 환경 ]

DVWA v1.9
Burp Suite Community Edition v2024.11.2

1. Source code analysis

[그림 1] Impossible level Source code

 

CSRF 토큰 검증

checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
  • 제출된 user_token과 세션에 저장된 session_token을 비교
  • 토큰이 일치하지 않으면 index.php로 리디렉션하거나 요청 차단

파일 정보 추출

$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_tmp  = $_FILES[ 'uploaded' ][ 'tmp_name' ];
  • $uploaded_name : 사용자가 업로드한 파일 이름
  • $uploaded_ext : 파일 이름에서 마지막 점(.) 이후의 확장자
  • $uploaded_size : 파일 크기(바이트 단위)
  • $uploaded_type : MIME 타입(예: image/jpeg)
  • $uploaded_tmp : PHP가 임시로 저장한 파일 경로

저장 경로 및 파일명 설정

$target_path   = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';
$target_file   =  md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
$temp_file     = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
$temp_file    .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
  • $target_path : 업로드된 파일을 저장할 디렉토리 경로
  • $target_file 업로드된 파일의 최종 이름, MD5 해시와 고유 ID(uniqid())를 결합해 무작위화하여 이름 충돌 방지
  • $temp_file 임시 파일 경로, PHP 설정(upload_tmp_dir) 또는 시스템 기본 임시 디렉토리를 사용

이미지 검증

if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&
  ( $uploaded_size < 100000 ) &&
  ( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&
  getimagesize( $uploaded_tmp ) ) {
  • 확장자가 jpg, jpeg, png인지 확인
  • 크기가 100KB(100,000 바이트) 미만인지 확인
  • MIME 타입이 image/jpeg 또는 image/png인지 확인
  • getimagesize( )를 사용해 파일이 유효한 이미지인지 확인

이미지 재인코딩

if( $uploaded_type == 'image/jpeg' ) {
    $img = imagecreatefromjpeg( $uploaded_tmp );
    imagejpeg( $img, $temp_file, 100);
}
    else {
      $img = imagecreatefrompng( $uploaded_tmp );
      imagepng( $img, $temp_file, 9);
}
imagedestroy( $img );
  • 이미지 불러오기
    • JPEG 파일: imagecreatefromjpeg( ), PNG 파일: imagecreatefrompng(  )
  • 이미지 재인코딩
    • JPEG 파일은 imagejpeg( ), PNG 파일은 imagepng( )로 재인코딩
    • 메타데이터 제거를 통해 악의적인 정보 포함 방지
  • 임시 파일에 저장
    • 재인코딩된 이미지를 임시 파일에 저장
  • 이미지 리소스 해제
    • imagedestroy( ) 로 이미지 리소스를 해제하여 메모리 누수 방지

최종 파일 이동

if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
   echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";
}
    else {
    echo '<pre>Your image was not uploaded.</pre>';
}
  • 임시 파일을 최종 저장 경로로 이동하며, rename( )를 사용하여 저장
  • 성공: 업로드된 파일의 링크와 성공 메시지를 출력, 실패: 오류 메시지를 출력

임시 파일 삭제

if( file_exists( $temp_file ) )
            unlink( $temp_file );
    }
  • 이동 후 임시 파일이 남아 있으면 삭제

이 코드는 여러 보안 기능을 통해 안전한 파일 업로드를 보장한다. 먼저, Anti-CSRF 토큰 검증을 통해 요청의 신뢰성을 확보하며, MD5 해시와 고유 ID를 사용하여 파일명을 무작위화해 이름 충돌과 파일 예측을 방지한다. 또한, 확장자, MIME 타입, 파일 크기, 그리고 getimagesize( )를 활용하여 업로드된 이미지의 유효성을 철저히 검증한다. 더불어, 이미지 재인코딩을 통해 악의적인 메타데이터가 포함되는 것을 방지하며, 임시 디렉토리를 활용하고 사용 후 이를 삭제하여 임시 파일을 효과적으로 관리한다.

 

2. Source code improvements

MIME 타입 확인 강화

finfo_file()로 실제 MIME 타입을 확인

$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $uploaded_tmp);
finfo_close($finfo);

$allowed_mime_types = ['image/jpeg', 'image/png'];

if (!in_array($mime_type, $allowed_mime_types)) {
    echo '<pre>Invalid file type!</pre>';
    exit;
}

업로드 디렉토리 보호

디렉토리 내 실행 파일 방지를 위해 .htaccess 추가 (Apache 기준)

<FilesMatch "\.php$">
    Deny from all
</FilesMatch>

업로드 크기 및 설정 제한

PHP 설정으로 업로드 크기 제한

upload_max_filesize = 2M
post_max_size = 8M

개선된 코드의 장점

  • 보안이 강화된 이미지 처리
  • 업로드 파일의 실행 위험 방지
  • 고유한 파일명을 생성하여 충돌 방지
  • 클라이언트와 서버 모두에서 파일 검증
반응형

관련글 더보기