2011년 6월 27일 월요일

자동화된 패킷분석의 어려움을 이야기 해보자!

패킷분석을 주 업무로 하는 사람이라면, 그들만의 도구를 가지고 있거나 방법이 있을 것이다. 특히나 분석할 패킷파일 대상이 많다면, 더욱 더 자동화된 기능이 필요하다. 하지만, 자동화된 방법으로 개발하여 사용하다 보면 어려움이 많이 발생할 경우가 많다. 대표적인 것이, 주어진 프로토콜 형식에 맞지 않는 패킷파일이다.

이건 보안적인 관점에서 봐도 발생되는 같은 이치인데, 대표적인 SQL Injection 을 생각해 보자.
웹 개발을 할때, 개발자가 생각한 것 이외의 값이 들어오지 않을 것이라 가정하고, 개발하면서 보안적인 문제점이 생긴다. 프로그램 관점에서 정해진 폼에 따라 사용자가 데이터를 입력하면 아무런 문제가 없을 것이다. 하지만, 사용자가 인자값을 직접 수정할 수도 있다는 가정이 되어야 한다. 인자값을 임의로 수정해 전송해 버리면, 프로그램에서는 그 값에 대한 체크가 없는 이상 사용자가 정상적으로 입력한 값으로 인지해 버려 SQL Injection 과 같은 취약점이 발생되어 버린다.

패킷파일도 마찬가지이다. 나 또한 보안적 관점에서 패킷 파일 분석을 수행하다 보면,이 패킷파일들이 별에별 형태로 다양하다는 것이다. 악성코드가 생성한 패킷파일이면 더욱 그러하다. 예를 들어, DNS 는 UDP 53 번을 기본으로 사용한다. 그리고 Zone Transfer 와 같은 상황등에서는 TCP 53 번을 사용하기도 한다. 흔히, UDP 53 번을 사용하면 DNS 데이터라고 가정한다. 그런데, DNS 가 아닌 어뚱하게도 어떤 통신을 위해 사용되는 것이라면 어떨까? DNS 쿼리 데이터의 일정 값을 사용하는데 이상하게도 알수없는 페이로드로 채워져 있다면 말이다.

패킷분석을 수행하는 도구 입장에서는 DNS 로 판별했는데, 실제 데이터가 정확하지 않아 Exception 과 같은 오류가 발생할 수 있다. 이런류의 패킷파일 데이터 판단은 힘들어지는데, 실제 이 패킷 데이터를 알아내기 위해서는 직접 악성코드를 리버싱을 해야 정확한 데이터 의미를 파악할 수 있게된다.

비정상적인 DNS 데이터를 가지고 있는 패킷파일을 Scapy 를 통해서 확인해 보면 아래와 같다.

>>> a=rdpcap("test_dns.pcap")
WARNING: wrong value: DNS.ancount=38440
WARNING: wrong value: DNS.nscount=27668
WARNING: more wrong value: DNS.arcount=45458
WARNING: DNS RR prematured end (ofs=73, len=44)
WARNING: DNS RR prematured end (ofs=77, len=44)
WARNING: more DNS RR prematured end (ofs=81, len=44)

자 일단 읽어 들이는 과정에서도 경고 메시지가 나타난다. DNS 의 ancount 나 nscount 값이 지나치게 크게 나온다는 것이다.

>>> a
<test_dns.pcap: TCP:6 UDP:1115 ICMP:0 Other:28>

다음은 패킷파일 중 정상적인 DNS 패킷 데이터를 본 것이다.

>>> a[1145]
<Ether  dst=[삭제] src=[삭제] type=0x800 |<IP  version=4L ihl=5L tos=0x0 len=68 id=956 flags= frag=0L ttl=128 proto=udp chksum=0x52c9 src=[삭제] dst=[삭제] options=[] |<UDP  sport=1039 dport=domain len=48 chksum=0xfdec |<DNS  id=52467 qr=0L opcode=QUERY aa=0L tc=0L rd=1L ra=0L z=0L rcode=ok qdcount=1 ancount=0 nscount=0 arcount=0 qd=<DNSQR  qname='1.1.1.1.in-addr.arpa.' qtype=PTR qclass=IN |> an=None ns=None ar=None |>>>>

정상적인 형태는 각 필드정보가 정확히 확인이 된다. 그런데, 아래의 경우를 보면 다르다.
몇몇 필드의 데이터 값이 이상하고, Raw 페이로드 값이 존재한다.

>>> a[176]
<Ether  dst=[삭제] src=[삭제] type=0x800 |<IP  version=4L ihl=5L tos=0x0 len=84 id=16541 flags= frag=0L ttl=24 proto=udp chksum=0xfe73 src=[삭제] dst=[삭제] options=[] |<UDP  sport=domain dport=7783 len=64 chksum=0xf167 |<DNS  id=56 qr=0L opcode=QUERY aa=0L tc=1L rd=1L ra=0L z=1L rcode=server-failure qdcount=0 ancount=38440 nscount=27668 arcount=45458 qd=None an='' ns='' ar='' |<Raw  load='$\xabn\x00z\xb6\x0bl\x98Q\xff\x87\xf9\xe2\xd0\xaeu\xa2[삭제]0305rh\x02\xbe\x18\xc0\x00\x00' |>>>>>
>>>

HEX 값을 확인해 보면 아래와 같다.

>>> hexdump(a[176][Raw])
0000   24 AB 6E 00 7A B6 0B 6C  98 51 FF 87 F9 E2 D0 AE   $.n.z..l.Q......
0010   75 A2 [삭제] 3 37 37 42   u..[삭제]C77B
0020   30 33 30 35 72 68 02 BE  18 C0 00 00               0305rh......

ancount, nscount 가 이렇게 큰 데이터를 가질 수 있는가? 즉, 큰 데이터 값만 보고도 조작된 패킷 가능성이 크다고 볼 수 있다. 그러므로 Scapy 에서도 로드할때 경고 메시지가 나타난다. UDP/53 번에 패킷을 전송하니 이것을 DNS 패킷으로 인식하고 그 값 데이터를 필드 값으로 사용하면서 엉뚱하게 이상한 값으로 설정된 것이다.

이와 같은 경우에는 다양한 Exception 처리를 해 주어야 하는데, 어떤 형태가 들어오기 예측하기 쉽지 않다보니 특수한 환경의 패킷 분석에서는 상당한 어려움이 발생할 수 있다. 이러한 이유로 어떤 자동화된 형태로 패킷을 분석 처리하기에는 많은 경험도 필요하고, 각각의 상황에 따라 처리도 해주어야 하다 보니 안정화 되기까지는 시간도 필요하다. 물론, 안정화 후에도 계속 이런 가능성은 존재하게 된다.  사용하려는 목적은 다르겠지만, 완벽하게 패킷 데이터를 분석하여 사용한다는 관점보다는 좀더 쉽게 패킷 분석가를 도와준다는 관점에서 생각하고 사용하면 이런 작업을 진행하는 분들에게는 좀더 마음이 편해지지 않을까?

/Rigel

댓글 3개:

  1. Packet 분석 Buffer Overflow도 자주 일어 나죠. L7 장비를 만들 때에는 항상 이런 예외 상황에 대비를 해야 한다는... ^^

    답글삭제
  2. 장비단이라면, 더욱 더 고려해야 할 상황들이 많아지겠죠. : )
    이와 관련한 경험있으신 분들의 경험 댓글 공유해요~

    답글삭제
  3. 장비단이라고 해도 결국 SW이어서 그리 다를 바는 없을 거에요. 다만, 장비로 만들면 24시간 이상 작동 없이 돌아 가야 하는 거라서 안정성 및 performance에 많은 염두를 둬야 겠죠.

    좋은 개발 방법론은 아니지만 class의 member variable을 둬서 access를 하게 되면 indirect addressing이 많이 일어 나서 performance에 좋지 않을 수도 있습니다. 따라서 많은 class member variable들을 static으로 선언하거나 global고 선언하기도 합니다. 개인적으로는 비추이지만서도... ^^

    답글삭제