모의해킹 스터디 복습

모의해킹 스터디 14주차(2): 파일 업로드 - 우회 가능 케이스

whydontyoushovel 2025. 2. 5. 00:21

 

 File Upload 

공격자가 원하는 임의의 파일을 서버에 업로드할 수 있는 공격

 

 

1) 발생 원인

  • 파일 업로드 시 검증을 제대로 거치지 않음
  • 예를 들어 프로필 사진을 업로드하는 곳이라면 이미지 파일만 올릴 수 있게 해야하는데 그런 과정이 없음

2) 발생 위치

  • 파일을 업로드할 수 있는 곳
  • 게시판, 프로필 사진, 서류 제출, 파일 업로드를 통한 신분증 인증 등
  • 파일을 파라미터로 보내고 있는지 버프 스위트로 확인

 

 

목차

  • Content-Type 변조
  • 파일 저장 경로 변조
  • 확장자 업로드 제한 우회
  • 헥사에 실행 코드 삽입하여 우회

 

 

   파일 업로드 진단 시 우회 가능한 상황들       

10% 정도(?) 부족한 파일 검증

 

▷ Content-Type 변조

  • 서버에서 업로드 파일의 Content-Type을 검증할 경우
if ($_FILES['___']['type'] == "image/jpeg") {
    // 서버에 저장
} else {
    // 돌아가
};
  • MIME 타입(image/jpeg 등)을 정해두고 업로드 한 파일의 Conent-Type 헤더의 값과 같은지 확인함

[+] MIME 타입:

웹으로 파일 주고 받을 때 파일을 잘 전달하기 위해 해당 형식에 맞게 인코딩하는데 이 형식을 나타내는 방식인 듯

 

★ 내 서버에서 실습해보기

  • 관련 코드
    if ($_FILES['image']['type'] == "image/jpeg") {
        //파일의 MIME타입이 jpeg 형식이면 

        move_uploaded_file($_FILES['image']['tmp_name'],$targetPath);
        //서버에 파일 저장
        //DB저장, 게시글로 리다이렉션 코드 생략
        
    } else {
        echo "<script>alert('이미지 형식만 지원됩니다.');</script>";
        //파일 형식이 jpeg가 아니면 alert 출력
    }

 

  • 웹쉘 업로드 실습

가입된 계정으로 사이트 접속

 

웹쉘 코드가 작성된 php 파일을 업로드

alert창이 출력되며 업로드 실패함

 

우회를 염두에 두며 다시 게시물 작성

 

요청 데이터를 변조하기 위해 버프 스위트의 intercept 기능을 활성함

 

업로드 버튼 클릭

 

인터셉트한 요청 데이터의 본문 내용 중 파일의 Content-Type을 확인

application/octet-stream이었던 형식을 image/jpeg로 변조
변조한 요청을 forward 버튼을 눌러 전송

정상 업로드를 알리는 alert 확인

업로드된 게시물 읽기 페이지로 리다이렉션 됨

 

서버에 저장된 것도 확인할 수 있음
  • 파일의 MIME 타입을 지정하여 검증할 경우 요청 데이터의 Content-Type 변조를 통해 우회 가능

 

 

▷ 파일 저장 경로 변조

  • 서버에서 파일 저장 경로의 실행 권한을 제한할 경우 업로드 시 저장 경로를 변조함

파일 저장 경로에 .htaccess 파일을 생성한 뒤 위와 같은 설정을 추가
-> 맨 첫 줄의 "\.php$" 설명
** \는 이스케이프
**.php 는 확장자명
**$는 문자열의 끝 => 파일 이름의 끝이 .php인 놈만 해당

 

 

★ 내 서버에서 실습해보기

웹쉘을 포함한 파일을 업로드함

 

웹쉘 파일을 새탭에서 열기

 

서버에서 실행되지 않은 채 단순 문자열로 출력되는 것을 확인

 

/var/www/html/img가 아닌 다른 디렉토리에 파일을 저장할 게시물 작성

 

버프 스위트의 intercept 기능 활성화 후 게시글 업로드

 

webshell.php였던 파일 이름을 ../webshell.php로 변경 후 forward로 요청 전송
원래 var/www/html/img/webshell.php에 저장되었으나
변경 후 var/www/html/webshell.php에 저장하게 됨

 

정상 업로드 됨

업로드 후 리다이렉션 된 페이지에서 웹쉘을 찾아 우클릭
새 탭에서 열기를 클릭

 

하면 여전히 안되는 것을 볼 수 있음

 

  • 이 우회방법의 경우 서버가 파일 이름을 그대로 저장해야 가능한 듯함
  • 내 서버는 사용자가 설정한 파일 이름에서 앞에 /어쩌구/저쩌구 다 떼고 마지막 파일 이름만 남겨놔서 적용 안됨
  • 이 우회 방법은 디렉토리 트래버설(이동)을 이용한 파일 덮어쓰기 공격(?)으로도 이어질 수 있을 것으로 보임
  • 파일 덮어쓰기 공격: /etc/passwd 파일을 /../../etc/passwd라는 제목의 파일을 업로드하여 덮어씌우기 등

 


▷ 확장자 업로드 제한 우회

  • 서버에서 확장자명이 .php인 파일을 실행 (혹은 업로드)하지 못하게 막을 경우 확장자명을 우회함

해당 디렉토리에 저장되는 파일 중
확장자명이 .php 인 파일의 경우 php실행을 막도록 .htaccess로 설정

 

 

★ 내 서버에서 실습해보기

웹쉘 파일의 확장자명을 .php에서 .phtml로 수정한 뒤 업로드

업로드 완료

리다이렉션된 페이지에서 웹쉘 파일을 찾아 우클릭한 후
새 탭에서 열어 웹쉘을 실행함 

cmd 파라미터로 ls 명령어를 수행한 결과
웹쉘 코드가 정상 작동되는 것을 확인함

 

  • 서버에서 파일 확장자명으로 실행 및 업로드 제한할 경우 다른 형태의 확장자명으로 우회 가능함

우회 가능 확장명 ↓

https://vulp3cula.gitbook.io/hackers-grimoire/exploitation/web-application/file-upload-bypass

 

 

 

 

▷ 헥사에 실행 코드 삽입하여 우회

  • 파일 시그니처로 파일 형식을 검증하는 서버의 경우
  • 가장 많이 접할 우회 방법

 파일 시그니처 란?

  • 파일 맨 앞부분에 삽입하는 8바이트 데이터로, 이 데이터를 통해 파일의 형식이 무엇인지 구분할 수 있음.
  • "매직 넘버"라고도 함

PNG 파일의 파일 시그니처 8바이트

 

★ 내 서버에서 실습해보기

  • 관련 코드
<?php
function file_vali_check($tmp_file) {
    $bin_file = fopen($tmp_file,'rb');
    $signature = fread($bin_file,8);
    fclose($bin_file);
    
    $hex_sig = bin2hex($signature);

    if($hex_sig === "89504e470d0a1a0a") {
        return true;
    } else {
        echo "<script>alert('png 형식만 지원됩니다.');
            location.href='upload.php';</script>'";
        exit;
    }
}

if($_SERVER['REQUEST_METHOD'] === 'POST') {

    $png_file = file_vali_check($_FILES['image']['tmp_name']); 

    if (isset($_FILES['image']) && $_FILES['image']['error'] === 0 && $png_file) {
        //서버와 DB에 파일 저장
    } else {
        // 돌아가
    }
}
?>
  • POST 요청이 들어오면 파일 임시 저장 경로를 매개로 파일 시그니처 검증을 시작함
  • 파일을 바이너리 읽기 형식으로 열고 핸들($bin_file)로 파일 맨 앞부분 8바이트를 읽어와 저장
  • 파일 닫음
  • 문자열로 저장된 8바이트 데이터를 HEX 값으로 변경하여 PNG의 파일 시그니처와 비교 (***bin2hex함수는 hex 값을 소문자로 변경)
  • 두 데이터가 일치하면 "참" 전달, 일치하지 않으면 alert출력 후 게시글 작성 페이지로 리다이렉션
  • 업로드한 파일이 PNG 형식 + 업로드한 파일 있음 + 에러 없음 => 데이터 저장
  • 세 조건 중 하나라도 해당하지 않으면 게시글 작성 실패

파일이 업로드되는지 확인하기 위해 일반 PNG 형식 이미지를 업로드

 

정상적으로 업로드 됐다는 alert창 확인

업로드한 게시물 읽기 페이지로 리다이렉션 됨
PNG 파일은 서버에서 정상적으로 처리하고 있다는 것을 확인

 

헥스 에디터로 이미지 파일을 열고 웹쉘 스크립트 삽입 후 php로 저장

이미지 파일이 php 파일로 저장됨

 

웹쉘 스크립트가 포함된 파일을 업로드함

정상적으로 업로드 됨

깨진 이미지 파일을 우클릭하여 새 탭에서 이미지를 열어봄

 

새 탭으로 연 파일에 cmd 명령어를 파라미터로 요청했으나 아무런 변화가 없음
당연함 php 확장자는 실행 제한했었음

 

php였던 파일 확장자를 pHp로 변경한 뒤 저장

 

확장자명을 pHp로 수정한 파일 업로드

 

게시글 업로드됨

하지만 웹쉘은 작동하지 않음
다른 확장자명으로 바꿔서 시도함

 

pHp였던 확장자명을 phtml로 수정

 

확장자명을 수정한 파일과 함께 게시글을 업로드

 

게시글 업로드됨

 

업로드한 파일을 우클릭하여 새 탭에서 엶

 

맨 처음 파일 시그니처와 함께
이미지가 완전히 깨진 형태로 출력되는 것을 확인

 

ls 명령어를 전달한 결과 웹쉘 코드를 삽입했던 위치에서
img 디렉토리의 파일 목록을 확인할 수 있었음

 

pwd 명령어 입력 결과
왠지 두번 출력됨
아 이제보니 ls 명령어도 마지막에 있는 뭘봐라이언 파일명을 두 번 출력함
system함수가 마지막 줄을 한 번 출력하고 echo가 파일 목록을 한 번 출력해서 중복된다는 듯
(system 함수 결과를 변수에 저장해서 출력하는 웹쉘은 한번만 출력됨)

 

  • 파일 시그니처를 통해 파일 형식을 검증하는 방법은 꽤 안전한 검사 방법일 수는 있지만 완전한 방법은 아님
  • 파일을 헥스 에디터로 열어 웹쉘 스크립트를 삽입하고 확장자명을 서버측 실행 스크립트로 수정하면 우회가 가능할 수 있음
  • 그리고 아예 스크립트 파일을 헥스 에디터로 열고 이미지 파일 시그니처를 앞에 넣는 방식으로도 우회 가능한 듯함

[+] .htaccess로 확장자를 제한하면 pHp로 업로드해도 실행이 안됨

 

 

★ 이 방법으로 우회할 때 유의할 점

  • 파일 확장자를 png가 아닌 php로 해야 서버가 이 파일을 실행시킴!
  • .php.png 식의 이중확장자도 안됨

 

 

 

 

파일 업로드 우회 가능 케이스 정리     

1) MIME 타입 검증 시

  • 프록시 툴에서 Content-Type 수정하여 우회

2) 파일 저장 경로에 php 스크립트 실행 막을 때

  • 서버에서 사용자가 설정한 파일명을 그대로 저장할 경우 상위 디렉토리에 저장하여 우회

3) 파일 확장자명 차단할 때

  • 실행 가능한 다른 확장자명으로 우회

4) 파일 시그니처로 파일 형식 검증할 때

  • php 파일에 이미지 파일 시그니처를 삽입하거나 이미지 파일에 웹쉘 코드 삽입하여 우회

 

 

 

 

 

그림 파일 웹쉘이 뭘까