프로젝트/DVWA 실습

DVWA 실습 #5 - File Inclusion

하루히즘 2021. 1. 12. 04:36

DVWA의 네 번째 실습 대상인 File Inclusion이다. 각 파일을 클릭하면 특정 php 코드가 실행되며 URL에 파일 이름이 파라미터로 전달된다. 이 특성을 활용하여 웹 서버가 다른 파일을 읽도록 하는 것이 이 실습의 목적이다.

File Inclusion?

File Inclusion은 실행 시간에 다른 파일을 읽어 동적으로 웹페이지를 생성하는 애플리케이션에서 자주 볼 수 있는 취약점이다. 주로 PHP의 include() 함수가 그 단적인 예인데 예를 들어 데이터베이스 연결 클래스를 만들어 이를 여러 페이지에서 사용하고자 한다면 include("db.php")처럼 불러와서 사용할 수 있다. 마치 C언어에서 사용자 지정 헤더를 include 하는 것과 비슷한 방법으로 꼭 클래스뿐 아니라 다수의 페이지에서 공통적으로 사용하는 요소(내비게이션 바, 사이드 메뉴 등)를 매 페이지마다 작성하지 않고 미리 작성된 파일을 불러와 사용하는 등 다양한 방면으로 활용할 수 있다.

 

그런데 웹 애플리케이션이 불러올 파일 경로에 사용자 입력을 삽입한다면 공격자는 웹 애플리케이션이 읽을 파일의 경로를 올바르지 않은 곳으로 지정하거나 간섭할 수 있다. 예를 들어 사용자들의 아이디로 각각 블로그 페이지를 생성해서 디렉토리에 저장한 후 요청 시마다 이를 찾아서 출력해주는 서비스가 있다고 하자. 만약 사용자들의 id를 GET이나 POST 파라미터로 받아서 해당 파일명의 블로그 페이지를 include 하는 방식으로 출력한다고 할 때 이 사용자 id를 위조하면 다른 파일(Directory Traversal 등을 활용)의 내용을 출력시키거나 미리 업로드된 웹쉘 파일을 실행하는 등 원래 웹 애플리케이션이 의도한 작업과 다른 작업을 수행할 수 있다.

 

이는 비인가된 파일 시스템에 접근하는 Directory Traversal과 달리 웹 애플리케이션이 프로그램 실행을 위해 다른 파일의 실행 코드를 읽는 기능을 악용한다는 점에서 차이가 있다. 이런 File Inclusion 공격이 성공적으로 수행된다면 RCE(Remote Code Execution) 등을 이용하여 추가적인 공격의 발판으로 삼을 수 있다(위키피디아).

 

File Inclusion 취약점을 방지하려면 사용자 입력을 항상 필터링하고 직접 매개변수로 사용되지 않도록 해야 한다. 이는 꼭 URL 파라미터 뿐 아니라 쿠키, 헤더, POST 데이터 등 모든 입력에 해당된다. 파일의 개수가 한정적이라면 include 할 수 있는 파일 이름들을 미리 화이트리스트로 저장하여 허가된 파일만 include 할 수 있도록 검증하는 것도 좋다. 제일 기본적인 것은 정말 필요한 경우가 아니라면 외부 파일을 include 하는 기능을 비활성화하는 것이다.

LFI, RFI?

File Inclusion 취약점은 크게 두 가지로 나뉜다. 첫 번째는 Local File Inclusion(LFI), 두 번째는 Remote File Inclusion(RFI)인데 전자는 웹 애플리케이션이 구동되는 서버 내의 파일을 읽거나 실행하는 것이고 후자는 서버 외의 파일, 즉 인터넷 상의 다른 곳에 위치한 파일을 읽거나 실행하는 것이다. 후자가 가능하려면 php 설정에서 allow_url_include 옵션이 켜져 있어야 하기 때문에, 그리고 보안상의 이유로 기본적으로 꺼져있기 때문에 처음 포스트에서 다뤘듯이 따로 활성화해야 한다.

LFI의 예시

LFI는 서버 내의 파일을 읽는 취약점으로 웹 애플리케이션을 실행하는 계정의 권한과 Directory Traversal을 통해 여러 중요한 파일을 읽을 수 있다. 리눅스 서버라면 /etc/passwd를, 윈도 서버라면 /window/win.ini 등을 읽을 수 있으며 주로 File Upload 취약점과 같이 사용되는데 업로드된 멀웨어를 LFI로 실행시켜 공격하는 것이다.

RFI는 서버 외의 파일을 읽는 취약점으로 FTP, HTTP 프로토콜 등으로 얻을 수 있는 인터넷 상의 파일을 서버가 읽거나 실행하기 때문에 치명적인 결과를 불러올 수 있다. 특히 자바스크립트(node.js)나 PHP 등으로 작성된 각종 멀웨어가 RCE를 발생, 서버가 장악될 수 있다. PHP에서는 file_get_contents() 함수를 사용할 경우 allow_url_include 옵션이 꺼져있어도 RFI가 가능하다.

 

특히 PHP에서는 include, require 함수 등을 이용하여 File Inclusion을 수행할 수 있는 자체 함수들을 지원하고 있기 때문에 이런점이 더욱 부각된다.

What is the Local File Inclusion Vulnerability? | Netsparker

Remote File Inclusion Vulnerability | Netsparker

What is Remote File Inclusion (RFI)? | Acunetix

Security Level: low

이 단계에서는 다음과 같은 요청이 전송된다.

GET /dvwa/vulnerabilities/fi/?page=file1.php HTTP/1.1
Host: 192.168.56.103
Upgrade-Insecure-Requests: 1
DNT: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.56.103/dvwa/vulnerabilities/fi/?page=include.php
Accept-Encoding: gzip, deflate
Accept-Language: ko,en;q=0.9,en-US;q=0.8
Cookie: security=low; PHPSESSID=10617456c566367335033ea47d9b7c3d
Connection: close

GET 메서드로 전송되는 요청에 page 파라미터로 파일 이름이 들어간다는 것을 제외하고는 특별한 것은 없다. 이 단계에서는 다음과 같은 소스 코드가 적용된다.

<?php

// The page we wish to display
$file = $_GET[ 'page' ];

?>

단순히 page 파라미터로 얻은 파일 이름을 $file 변수로 지정하고 있다. 이는 실습 페이지(index.php)에서 다음처럼 include 된다. 만약 file 변수가 존재하지 않는다면 다시 파일 목록(include.php)으로 돌려보낸다. 

Security Level: medium

이 단계에서는 이전 단계와 동일한 요청이 전송된다.

이 단계에서는 다음과 같은 소스 코드가 적용된다.

<?php

// The page we wish to display
$file = $_GET[ 'page' ];

// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );

?>

이전 단계와 달리 RFI, LFI를 필터링하기 위해 "http://", "https://", "../", "..\" 문자열을 str_replace() 함수를 이용해 제거하고 있다. 몇 번이고 다뤘지만 필터링 대상 문자열을 한 번만 훑고 마는 str_replace() 함수의 특징 때문에 필터링을 쉽게 우회할 수 있다.

Security Level: high

이 단계에서는 이전 단계와 동일한 요청이 전송된다.

이 단계에서는 다음과 같은 소스 코드가 적용된다.

<?php

// The page we wish to display
$file = $_GET[ 'page' ];

// Input validation
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
    // This isn't the page we want!
    echo "ERROR: File not found!";
    exit;
}

?>

이전 단계와 달리 프로토콜이나 Directory Traversal을 탐지하지 않고 fnmatch() 함수를 이용해 입력값을 검증하고 있다. fnmatch() 함수는 패턴을 기반으로 문자열을 탐색하며 해당 패턴이 발견될 경우 true를 반환한다. 여기서는 page 파라미터 값이 file 문자열로 시작하는지 확인하고 있는데 그 뒤에 무슨 문자열이 오는지 검사하지 않기 때문에 이를 우회하여 다른 파일을 읽을 수 있다.

Security Level: impossible

이 단계에서는 이전 단계와 동일한 요청이 전송된다.

이 단계에서는 다음과 같은 소스 코드가 적용된다.

<?php

// The page we wish to display
$file = $_GET[ 'page' ];

// Only allow include.php or file{1..3}.php
if( $file != "include.php" && $file != "file1.php" && $file != "file2.php" && $file != "file3.php" ) {
    // This isn't the page we want!
    echo "ERROR: File not found!";
    exit;
}

?>

이제는 조건문을 통해 지정된 파일명만 읽을 수 있도록 제한하고 있다. 이는 위에서 언급한 화이트리스트 방식으로 제일 바람직한 방법이지만 파일이 많아지거나 파일명이 복잡하다면 적용하기 어려운 방법이다.

'프로젝트 > DVWA 실습' 카테고리의 다른 글

DVWA 실습 #5-2 - File Inclusion(medium)  (0) 2021.01.12
DVWA 실습 #5-1 - File Inclusion(low)  (0) 2021.01.12
DVWA 실습 #4-3 - CSRF(high)  (0) 2021.01.08
DVWA 실습 #4-2 - CSRF(medium)  (0) 2021.01.07
DVWA 실습 #4-1 - CSRF(low)  (0) 2021.01.06