레이블이 Python인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Python인 게시물을 표시합니다. 모든 게시물 표시

2010년 11월 15일 월요일

Scapy 로 패킷 핸들링하는 프로그램 만들기 - 세번째

Scapy 의 마지막으로 세번째 이야기를 소개해본다. 이번에는 Scapy 를 이용해 나만의 프로그램을
만드는 방법이다. 앞서 소개한 것과 같이 Scapy 는 파이썬기반이어서, 파이썬에서도 Scapy 모듈을 로드하여
내가 원하는 형태로 패킷을 쉽게 다룰 수가 있다.

모든 패킷 도구들이 내가 원하는 입맛에 맞게 맞춰져 있지는 않다. 이럴때 Scapy 는 간단하게 패킷을 쉽게
다룰 수 있는 방법을 제공해 준다. 이에 몇 가지 사용 예제를 소개해 본다. 예제를 보는 것 만으로도
쉽게 이해가 될 것이며, 이 예제를 기반으로 원하는 프로그램으로 확장하는 것은 그리 어렵지 않을 것이다.
여기 외에, 인터넷을 찾아보면 많은 예제파일들이 있으므로 여기서는 '아 ~ 이현형태로 이용가능하구나' 하는 것만을 인지해 두면 될것 같다.

1. 간단한 ICMP 패킷 보내보기

첫 시작으로 간단하게 ICMP 패킷 보내는 것을 해 보자. 우선, Scapy 모듈을 사용하기 위해서는
모듈을 불러들여야 한다.

from scapy.all import sr1, IP, ICMP

위와 같이 하여 sr1, IP, ICMP 만을 로드하였다. 물론 import * 로 하여 전체 관련된 것을 모두 로드할 수 도 있다. import sys 는 실행할때 인자로 받아들일 값을 사용하기 위해 sys 모듈을 로드하였고,
packet 변수에 IP()/ICMP() 와 같이 하여 IP 와 ICMP 헤더를 넣어주었다. 이때 IP 헤더에는 인자로 받은 값을 넘겨주어 목적지 주소를 지정한 것이다. show() 는 각 구성 형태를 세부적으로 보여주며, sr1 은 패킷 한개를 발송하도록 정의한 것이다.

#!/usr/local/bin/python
# PacketInside.com - Scapy Example
import sys
from scapy.all import sr1,IP,ICMP

packet=IP(dst=sys.argv[1])/ICMP()
print "+-------- Sending Packet INFO"
packet.show()
result = sr1(packet)
if result:
    print "+-------- Receiving Packet INFO"
    result.show()

발송하기 전에 패킷 구성 형태를 packet.show() 를 통해 한번 보여주고, 패킷 발송후에 성공했으면
전달 받은 패킷 데이터를 또 보여주는 간단한 구조다. 다음과 같이 실행해 본다.

# ./icmp.py google.com
WARNING: No route found for IPv6 destination :: (no default route?)
+-------- Sending Packet INFO
###[ IP ]###
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     =
  frag      = 0
  ttl       = 64
  proto     = icmp
  chksum    = None
  src       = 192.168.0.240
  dst       = Net('google.com')
  \options   \
###[ ICMP ]###
     type      = echo-request
     code      = 0
     chksum    = None
     id        = 0x0
     seq       = 0x0
Begin emission:
.Finished to send 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
+-------- Receiving Packet INFO
###[ IP ]###
  version   = 4L
  ihl       = 5L
  tos       = 0x0
  len       = 46
  id        = 56007
  flags     =
  frag      = 0L
  ttl       = 49
  proto     = icmp
  chksum    = 0x6d5f
  src       = 74.125.53.147
  dst       = 192.168.0.240
  \options   \
###[ ICMP ]###
     type      = echo-reply
     code      = 0
     chksum    = 0x0
     id        = 0x0
     seq       = 0x0
###[ Raw ]###
        load      = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
 
google.com 으로 ICMP 패킷을 보내고 전달받은 결과를 보여준다. 와이어샤크로 해당 패킷을 덤프해 보면
아래와 같이 제대로 패킷이 전송된 것을 알 수 있다.


만약 위 packet 변수에 아래와 같은 값을 입력하면 어떨까?

packet=IP(dst=sys.argv[1])/TCP(sport=4321,dport=80)/Raw(load="GET /index.html HTTP/1.0 \n\n")

의미는 간단해 보인다. TCP 출발지 포트를 4321, 목적지 포트를 80 으로 하고 페이로드에는 /index.html 을 요청하는 것을 집어 넣은 것이다. 하지만 이렇게 바꾸고 실행하면 오류가 발생한다. Scapy 모듈을 로드시에 IP,ICMP 만 했기 때문이다. 모듈 로드를 아래와 같이 해 주어야 한다.

from scapy.all import sr1,IP,TCP,Raw

2. ARP 패킷 모니터링

네트워크 인터페이스에서 ARP 트래픽만을 모니터링 하는 코드를 보자. 앞서 소개한 Scapy 에서 패킷 덤프를 할때 sniff 를 사용 한다고 했다. sniff() 를 통해 패킷 덤프를 하되, filter 를 통해 arp 프로토콜로만 한정을 한다.
#!/usr/local/bin/python
from scapy.all import *

def arp_monitor_callback(pkt):
    if ARP in pkt and pkt[ARP].op in (1,2):
        return pkt.sprintf("%ARP.hwsrc% %ARP.psrc%")

sniff(prn=arp_monitor_callback, filter="arp", store=0)

arp_monitor_callback 이라는 함수를 정의하고 넘어온 pkt 에서 ARP 가 있으면 하드웨어 맥 주소와 출발지 주소를 sprintf 로 리턴해 주어 출력하도록 한 것이다. ARP 이외도 ICMP, TCP 등 여러분들이 원하는 형태로 지정만 하면 출력이 가능해 지는 것이다. 위 코드의 실행결과는 아래와 같다:

# ./arp.py
WARNING: No route found for IPv6 destination :: (no default route?)
00:e0:4d:41:fc:XX 192.168.0.221
00:01:36:2e:0b:XX 192.168.0.200
00:01:36:2e:0b:XX 192.168.0.200
00:e0:4d:41:fc:XX 192.168.0.221

3. 패킷 추출정보 파일로 저장하기
 
지정한 파일을 읽어들이고 Payload 부분만 저장해 보자. 이때 출발지 주소가 192.168.0.240 인 IP 로만 한정을 한다. 각 세부 동작내용은 아래 주석을 참고하자

#!/usr/local/bin/python
from scapy.all import *

src="192.168.0.240" # 출발지 IP 정의
pcap_file = "test3.pcap" # 읽어들일 PCAP 파일 정의
pcap = rdpcap(pcap_file) # rdpcap() 을 통해 pcap_file 을 읽어들임

data = "" # data 변수 초기화
for packet in pcap: # 읽어들인 pcap 파일을 루프돌면서 처리
  ilayer = packet.getlayer("IP") # getlayer 를 통해 IP 레이어를 ilayer 에 저장
  if ilayer.src != src: # 출발지가 192.168.0.240 이 아닌 경우 Skip
    continue
  tlayer = packet.getlayer("TCP") # TCP 레이어를 tlayer 에 저장
  if packet.getlayer("Raw"): # Raw 영역이 존재하면, payload 를 data 에 추가
  data += str(tlayer.payload)
f = open("raw_data.dat", 'w') # raw_data.dat 를 오픈하고 기록한다.
f.write(data)
f.close()

실행을 해 보면 raw_data.dat 가 만들어지고, 이 파일에는 Payload 만 저장된 것을 확인할 수 있다.

# hexdump -C raw_data.dat | more
00000000  47 45 54 20 2f 20 48 54  54 50 2f 31 2e 31 0d 0a  |GET / HTTP/1.1..|
00000010  48 6f 73 74 3a 20 67 6f  6f 67 6c 65 2e 63 6f 6d  |Host: google.com|
00000020  0d 0a 55 73 65 72 2d 41  67 65 6e 74 3a 20 4d 6f  |..User-Agent: Mo|
00000030  7a 69 6c 6c 61 2f 35 2e  30 20 28 58 31 31 3b 20  |zilla/5.0 (X11; |
00000040  55 3b 20 4c 69 6e 75 78  20 69 36 38 36 3b 20 65  |U; Linux i686; e|
00000050  6e 3b 20 72 76 3a 31 2e  39 2e 30 2e 31 39 29 20  |n; rv:1.9.0.19) |
00000060  47 65 63 6b 6f 2f 32 30  30 38 30 35 32 38 20 45  |Gecko/20080528 E|
00000070  70 69 70 68 61 6e 79 2f  32 2e 32 32 0d 0a 41 63  |piphany/2.22..Ac|
00000080  63 65 70 74 3a 20 74 65  78 74 2f 68 74 6d 6c 2c  |cept: text/html,|
00000090  61 70 70 6c 69 63 61 74  69 6f 6e 2f 78 68 74 6d  |application/xhtm|
000000a0  6c 2b 78 6d 6c 2c 61 70  70 6c 69 63 61 74 69 6f  |l+xml,applicatio|
000000b0  6e 2f 78 6d 6c 3b 71 3d  30 2e 39 2c 2a 2f 2a 3b  |n/xml;q=0.9,*/*;|
000000c0  71 3d 30 2e 38 0d 0a 41  63 63 65 70 74 2d 4c 61  |q=0.8..Accept-La|
...(생략)

즉, 필요한 부분만 얻어내서 파일로 만들 수도 있다. 패킷파일의 모든 부분을 출력시키거나 쉽게 저장할 수도 있으며, getlayer() 를 통해 원하는 레이어 영역으로 쉽게 접근할 수 있다.

4. HTTP 패킷을 처리하는 프로그램 형태를 만들어 보자.

조금 더 프로그램 형태를 갖춰서 HTTP 정보를 얻어내는 코드를 살펴보도록 하겠다. 여기 예에서는 기본적인 부분에 대해서 설명하고 있는 것이므로, 응용만 하면 원하는 형태로 쉽게 핸들링이 가능해진다. 이런식으로 사용할 수 있다는 것을 보여주기 위해 작성된 것이므로, 여러분들이 다양하게 응용해 보기 바란다.

#!/usr/local/bin/python
# PacketInside.com - Examples
import sys
from scapy.all import *

CNT = 10 # CNT 에 10 을 지정

def run(target):

    try:
        pkt = rdpcap(target, count=CNT) # 패킷파일을 오픈할때 10 개 까지만 읽어들인다.
    except MemoryError: # 패킷파일을 오픈하다 메모리 에러가 발생하는 경우 Exception 처리
        print "Sorry - Memory Error"
        sys.exit() # 메모리 에러를 출력하고 프로그램 종료
    numPkt = len(pkt) # pkt 의 길이를 numPkt 에 저장

    print "Analyzing : " + target
    print "Total Packets: %d\n" % numPkt # 전체 패킷의 Count 를 출력, rdpcap() 에서 count 를 사용하지 않을시는 전체 패킷 건수를 표시하나 카운트를 10으로 제한했으므로 항상 10이 됨

    for packet in pkt:
        layer = packet.payload
        while layer: # 각 레이어 별로 루프 돌면서 처리
            layerName = layer.name # 레이어 이름을 layerName 에 저장
            if layerName == "IP": # 만약 레이어 이름이 IP 인 경우 다음 내용 수행
                print "[IP Layer]\nSRC: %s, DST: %s" % (layer.src, layer.dst)
# 출발지 와 목적지 주소를 출력 함

            if layerName == "TCP": # 레이어가 TCP 인 경우
                print "[TCP Layer]\nSPORT: %s, DPORT: %s" % (layer.sport, layer.dport)
            if layerName == "Raw": # 레이어가 Raw 인 경우 ( Payload 가 됨)
                result = processHTTP(layer.load) # Raw 가 있는 경우 processHTTP 를 호출함
                if result != "Error": # 넘어온 결과가 Error 인 경우 에러 결과를 출력
                    print result[0] + " " + result[1]

            layer = layer.payload

def processHTTP(data):

    str_method = ""
    str_uri = ""

    # 정규표현식을 통해 넘어온 데이터에서 METHOD, URI, HTTP 버전 정보등으로 구분함
    h = re.search("(?P<method>(^GET|^POST|^PUT|^DELETE)) (?P<uri>.+) (?P<version>.+)", data)
    if not h: return "Error" # 정규표현식에 해당하는 데이터가 없는 경우 Error 를 리턴해줌

    # method 로 정의된 부준은 str_method 에 저장
    if h.group("method"): str_method = h.group("method")
    # URI 데이터는 str_uri 에 저장
    if h.group("uri"): str_uri = h.group("uri")

    return str_method,str_uri # method 와 uri 를 리턴해 줌

# call run
run("tt.pcap") # run 함수를 호출, 이때 읽어들일 pcap 파일을 같이 전달


실행된 결과는 아래 화면과 같다 :

패킷을 분석할 데이터가 많은 경우에는 클래스와 함수를 적절히 사용하여 프로그램을 작성하면, 더욱 효과적으로 사용할 수 있을 것이다.

여기서는 소개하는데 한계가 있기 때문에, 이 정도 선에서 마무리 하고자 한다. 기본적인 것들은 다 언급하였기 때문에 파이썬에 익숙한 분들이라면 어렵지 않게 패킷분석 프로그램을 만들 수가 있다. 물론, 분석 대상의 범위에 따라서 달라지겠지만 Scapy 모듈만을 이용해서 뚝딱 원하는 형태로 패킷을 다룰 수 있으므로, 유용한 도구임에는 틀림없다.  

세번째 소개 글은 프로그램 예제를 만들고 검증하다 보니, 여러분들한테 소개하기까지 늦어졌다. 요 근래, 필자를 괴롭히는 귀차니즘이 한 몫하기도 했다. :-)

Scapy 를 이용하려는 분들에게 조금이라도 도움이 되길 바라며 Scapy 소개를 끝마친다.

From Rigel.

2010년 10월 25일 월요일

Scapy 의 다양한 기능을 익혀보자 - 두번째

Scapy 는 앞서 언급했듯이 파이썬(Python) 기반의 프로그램이다. 그러므로 파이썬에 익숙치 않은 사용자라면
이게 무엇인가 하고 혼란스럽기도 하다. 일반적인 컴퓨터 언어의 기본지식이 있는 사용자라면,
어렵지 않게 이해할 수 있는 부분이니 예제를 몇 개 보는 것만으로도 충분히 이해가 될 것이다. 첫번째 이야기에서 언급한 대로 간단히 설치를 하고 아래 예제대로 몇 가지를 직접 실행해 보자. 그러면 금방 이해가 되고, 머리속에 기억이 오래 남을 것이다.

1. Scapy 의 설정 정보 살펴보기

Welcome to Scapy (v1.1.1 / -)
>>> conf
Version    = v1.1.1 / -
ASN1_default_codec = <ASN1Codec BER[1]>
AS_resolver = <__main__.AS_resolver_multi instance at 0xaa69a4c>
BTsocket   = <class __main__.BluetoothL2CAPSocket at 0xaa0f89c>
IPCountry_base = 'GeoIPCountry4Scapy.gz'
L2listen   = <class __main__.L2ListenSocket at 0xaa0f74c>
L2socket   = <class __main__.L2Socket at 0xaa0f6ec>
L3socket   = <class __main__.L3PacketSocket at 0xaa0f6bc>
auto_fragment = 1
checkIPID  = 0
checkIPaddr = 1
checkIPsrc = 1
check_TCPerror_seqack = 0
color_theme = <DefaultTheme>
countryLoc_base = 'countryLoc.csv'
debug_dissector = 0
debug_match = 0
ethertypes = </etc/ethertypes/ >
except_filter = ''
gnuplot_world = 'world.dat'
histfile   = '/root/.scapy_history'
iface      = 'eth1'
manufdb    = </usr/share/wireshark/wireshark/manuf/ >
mib        = <MIB/ >
nmap_base  = '/usr/share/nmap/nmap-os-fingerprints'
noenum     = <Resolve []>
p0f_base   = '/etc/p0f/p0f.fp'
padding    = 1
prog       = Version    = v1.1.1 / -
display    = 'display'
dot        = 'dot'
hexedit    = 'hexer'
pdfreader  = 'xpdf'
psreader   = 'gv'
tcpdump    = '/usr/sbin/tcpdump'
tcpreplay  = 'tcpreplay'
wireshark  = 'wireshark'
promisc    = 1
prompt     = '>>> '
protocols  = </etc/protocols/ pim ip ax_25 esp tcp ah mpls_in_ip ipv6_opts xtp ipv6_route igmp igp ddp etherip xns_idp ipv6_frag vrrp gre ipcomp encap ipv6 iso_tp4 sctp ipencap rsvp hip udp ggp hmp idpr_cmtp fc skip st icmp pup manet isis rdp l2tp ipv6_icmp udplite egp ipip ipv6_nonxt eigrp idrp rspf ospf vmtp>
queso_base = '/etc/queso.conf'
resolve    = <Resolve []>
route      = Network         Netmask         Gateway         Iface           Output IP
127.0.0.0       255.0.0.0       0.0.0.0         lo              127.0.0.1      
192.168.0.0     255.255.255.0   0.0.0.0         eth1            192.168.0.240  
0.0.0.0         0.0.0.0         192.168.0.200   eth1            192.168.0.240  

conf 를 해 보면 현 세션의 설정 정보를 볼 수 있다. 설정정보의 변경은 간단하게 할 수 있는데, 만약 iface 의 네트워크 인터페이스를 바꾸고자 할 경우에는 conf.변수 = '값'  과 같이 사용하면 된다. 아래는 eth1 을 eth0 으로 변경해 본 것이다.

>>> conf.iface = 'eth0'


2. Scapy 를 이용해서 패킷 덤프도 해 보자.

sniff() 를 이용하면 패킷 덤프를 할 수 있다. 아래 화면은 sniff 를 한 후 Ctrl+C 를 눌러 중지하였더니 TCP 274 건 UDP 16 건이 탐지 되었다. 또 sniff 에 필터를 걸거나, count 옵션을 통해 몇 개까지 덤프를 한 후 중지할 것인지 지정할 수 있다. 기록된 데이터는 따로 특정 이름 변수에 담지 않아, '_' 에 기록되어 있다. _ 를 프린트 해 보면 패킷 정보가 출력이 된다.

>>> sniff()

^C<Sniffed: UDP:16 TCP:274 ICMP:0 Other:10>
>>> sniff(filter="tcp and port 80", count=15)
<Sniffed: UDP:0 TCP:15 ICMP:0 Other:0>
>>> print _
[<Ether  dst=00:01:36:2e:0b:a7 src=08:00:27:69:4f:90 type=0x800 |<IP  version=4L ihl=5L tos=0x0 len=610 id=7385 flags=DF frag=0L ttl=64 proto=tcp chksum=0x464d src=192.168.0.240 dst=74.53.201.162 options='' |<TCP  sport=45659 dport=www seq=772295791 ack=1844792927 dataofs=8L reserved=0L flags=PA window=4006 chksum=0x3356 urgptr=0 options=[('NOP', None), ('NOP', None), ('Timestamp', (296792, 1784140432))] |< ....(생략)


3. 복잡한 건 싫어, 간단한 요약 정보 살펴보기

위에 기록된 정보를 a 라는 변수에 할당하였고, nsummary() 를 이용해 출력해 보았더니 보기 쉽게 한 라인 단위로 요약된 정보를 나타내준다. 각 라인 정보는 배열로 들어가 있어, 원하는 배열 정보를 출력해 볼수도 있다.
5번째 있는 정보를 출력한다면 a[5] 와 같이 입력하면 된다.

>>> a = _
>>> a.nsummary()
0000 Ether / IP / TCP 192.168.0.240:45659 > 74.53.201.162:www PA / Raw
0001 Ether / IP / TCP 74.53.201.162:www > 192.168.0.240:45659 A
0002 Ether / IP / TCP 74.53.201.162:www > 192.168.0.240:45659 PA / Raw
0003 Ether / IP / TCP 192.168.0.240:45659 > 74.53.201.162:www A
0004 Ether / IP / TCP 74.53.201.162:www > 192.168.0.240:45659 A / Raw
0005 Ether / IP / TCP 192.168.0.240:45659 > 74.53.201.162:www A
0006 Ether / IP / TCP 74.53.201.162:www > 192.168.0.240:45659 A / Raw
0007 Ether / IP / TCP 192.168.0.240:45659 > 74.53.201.162:www A
0008 Ether / IP / TCP 192.168.0.240:37088 > 72.14.213.95:www PA / Raw
0009 Ether / IP / TCP 192.168.0.240:47910 > 69.174.57.101:www S
0010 Ether / IP / TCP 192.168.0.240:47911 > 69.174.57.101:www S
0011 Ether / IP / TCP 72.14.213.95:www > 192.168.0.240:37088 PA / Raw
0012 Ether / IP / TCP 192.168.0.240:37088 > 72.14.213.95:www A
0013 Ether / IP / TCP 69.174.57.101:www > 192.168.0.240:47910 SA
0014 Ether / IP / TCP 192.168.0.240:47910 > 69.174.57.101:www A
>>> a[5]
<Ether  dst=00:01:36:2e:0b:a7 src=08:00:27:69:4f:90 type=0x800 |<IP  version=4L ihl=5L tos=0x0 len=52 id=7387 flags=DF frag=0L ttl=64 proto=tcp chksum=0x4879 src=192.168.0.240 dst=74.53.201.162 options='' |<TCP  sport=45659 dport=www seq=772296349 ack=1844794757 dataofs=8L reserved=0L flags=A window=4006 chksum=0x518b urgptr=0 options=[('NOP', None), ('NOP', None), ('Timestamp', (296905, 1784175402))] |>>>
>>>

4.  패킷 구성 세부정보를 살펴본다. 어떻게, show() 명령어로..

a 에 기록되어 있는 5번째 내용을 트리구조와 비슷하게 살펴볼 수 있다. show() 를 이용해 언제든지 필요할때 마다 패킷의 구성정보를 살펴보자.

>>> a[5].show()
###[ Ethernet ]###
  dst= 00:01:36:2e:0b:a7
  src= 08:00:27:69:4f:90
  type= 0x800
###[ IP ]###
     version= 4L
     ihl= 5L
     tos= 0x0
     len= 52
     id= 7387
     flags= DF
     frag= 0L
     ttl= 64
     proto= tcp
     chksum= 0x4879
     src= 192.168.0.240
     dst= 74.53.201.162
     options= ''
###[ TCP ]###
        sport= 45659
        dport= www
        seq= 772296349
        ack= 1844794757
        dataofs= 8L
        reserved= 0L
        flags= A
        window= 4006
        chksum= 0x518b
        urgptr= 0
        options= [('NOP', None), ('NOP', None), ('Timestamp', (296905, 1784175402))]

5. str() 로 문자열 정보 보기

>>> str(a[13])
"\x00\x016.\x0b\xa7\x08\x00'iO\x90\x08\x00E\x00\x01\xf8\xadL@\x00@\x06\xe4:\xc0\xa8\x00\xf0J}\x9bc\xd1x\x00PP&7\xdcj!.F\x80\x18\x01m\xa0=\x00\x00\x01\x01\x08\n\x00\x06\x9a\xb1\x9c5<\xd0GET / HTTP/1.1\r\nHost: google.com\r\nUser-Agent: Mozilla/5.0 (X11; U; Linux i686; en; rv:1.9.0.9) Gecko/20080528 Epiphany/2.22\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nKeep-Alive: 300\r\nConnection: keep-alive\r\nCookie: rememberme=false; PREF=ID=ca4bab229fe11b17:TM=1264000421:LM=1264000421:S=ydhaQuYz7bGA7OEL\r\n\r\n"


6. hexdump() 로 16진수 HEX 값 보기

>>> hexdump(a[13])
0000   00 01 36 2E 0B A7 08 00  27 69 4F 90 08 00 45 00   ..6.....'iO...E.
0010   01 F8 AD 4C 40 00 40 06  E4 3A C0 A8 00 F0 4A 7D   ...L@.@..:....J}
0020   9B 63 D1 78 00 50 50 26  37 DC 6A 21 2E 46 80 18   .c.x.PP&7.j!.F..
0030   01 6D A0 3D 00 00 01 01  08 0A 00 06 9A B1 9C 35   .m.=...........5
0040   3C D0 47 45 54 20 2F 20  48 54 54 50 2F 31 2E 31   <.GET / HTTP/1.1
0050   0D 0A 48 6F 73 74 3A 20  67 6F 6F 67 6C 65 2E 63   ..Host: google.c
0060   6F 6D 0D 0A 55 73 65 72  2D 41 67 65 6E 74 3A 20   om..User-Agent:
0070   4D 6F 7A 69 6C 6C 61 2F  35 2E 30 20 28 58 31 31   Mozilla/5.0 (X11
0080   3B 20 55 3B 20 4C 69 6E  75 78 20 69 36 38 36 3B   ; U; Linux i686;
....(생략)

7. 사용가능한 명령어는 무엇이 있을까?

Scapy 에서 사용가능한 명령어가 무엇이 있는지 궁금할때는 lsc() 를 쳐 보자. 그리고 추가 도움말이 필요할때는 help() 가 있다.

>>> lsc()
sr               : Send and receive packets at layer 3
sr1              : Send packets at layer 3 and return only the first answer
srp              : Send and receive packets at layer 2
srp1             : Send and receive packets at layer 2 and return only the first answer
srloop           : Send a packet at layer 3 in loop and print the answer each time
srploop          : Send a packet at layer 2 in loop and print the answer each time
sniff            : Sniff packets
p0f              : Passive OS fingerprinting: which OS emitted this TCP SYN ?
arpcachepoison   : Poison target's cache with (your MAC,victim's IP) couple
send             : Send packets at layer 3
sendp            : Send packets at layer 2
traceroute       : Instant TCP traceroute
arping           : Send ARP who-has requests to determine which hosts are up
ls               : List  available layers, or infos on a given layer
lsc              : List user commands
queso            : Queso OS fingerprinting
nmap_fp          : nmap fingerprinting
report_ports     : portscan a target and output a LaTeX table
dyndns_add       : Send a DNS add message to a nameserver for "name" to have a new "rdata"
dyndns_del       : Send a DNS delete message to a nameserver for "name"
is_promisc       : Try to guess if target is in Promisc mode. The target is provided by its ip.
promiscping      : Send ARP who-has requests to determine which hosts are in promiscuous mode

8. Traceroute 로  IP 경로 추적하기

흔히 경로 추적에 사용하는 traceroute 를 scapy 에서도 그대로 사용할 수 있다.

>>> traceroute("packetinside.com")
Begin emission:
************************Finished to send 30 packets.
******
Received 30 packets, got 30 answers, remaining 0 packets
   211.245.21.34:tcp80
1  192.168.0.200   11  
2  218.146.42.254  11  
3  121.140.24.161  11  
4  218.146.42.253  11  
5  112.190.34.93   11  
6  218.145.33.197  11  
7  112.174.48.158  11  
8  211.44.125.129  11  
9  118.221.4.17    11  
10 211.108.63.138  11  
11 58.229.17.78    11  
12 114.202.0.198   11  
13 211.245.21.34   SA  
14 211.245.21.34   SA  
15 211.245.21.34   SA  
16 211.245.21.34   SA    
...(생략)


9. 프로토콜의 각 레이어 구성하기

위에서는 몇 가지 유용한 명령어들을 알아 보았는데, 실질적으로 내가 패킷파일을 구성하기 위해서는 어떻게 해야 하는지 소개해 보고자 한다. 패킷파일을 들여다 보면 형태에 따라 다르지만 크게,
이더넷 헤더 , IP 헤더, TCP/UDP 헤더, 애플리케이션 헤더 등이 있다.
그럼 이 각 헤더를 구성하는 아래의 예제를 들여다 보자.

>>> IP()
<IP  |>
>>> Ether()/IP()/TCP()
<Ether  type=0x800 |<IP  frag=0 proto=tcp |<TCP  |>>>
>>> Ether()/IP()/TCP()/"GET /HTTP/1.1\r\nHost: packetinside.com\r\n\r\n"
<Ether  type=0x800 |<IP  frag=0 proto=tcp |<TCP  |<Raw  load='GET /HTTP/1.1\r\nHost: packetinside.com\r\n\r\n' |>>>>
>>> hexdump(_)
0000   FF FF FF FF FF FF 00 00  00 00 00 00 08 00 45 00   ..............E.
0010   00 51 00 01 00 00 40 06  7C A4 7F 00 00 01 7F 00   .Q....@.|.......
0020   00 01 00 14 00 50 00 00  00 00 00 00 00 00 50 02   .....P........P.
0030   20 00 4E BE 00 00 47 45  54 20 2F 48 54 54 50 2F    .N...GET /HTTP/
0040   31 2E 31 0D 0A 48 6F 73  74 3A 20 70 61 63 6B 65   1.1..Host: packe
0050   74 69 6E 73 69 64 65 2E  63 6F 6D 0D 0A 0D 0A      tinside.com....
>>>
>>> str(IP())
'E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01'
>>> str(TCP())
WARNING: No IP underlayer to compute checksum. Leaving null.
'\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x00\x00\x00\x00'
>>> a=Ether()/IP(dst="www.google.com")/TCP()/"GET /index.html HTTP/1.0 \n\n"
>>> a
<Ether  type=0x800 |<IP  frag=0 proto=tcp dst=Net('www.google.com') |<TCP  |<Raw  load='GET /index.html HTTP/1.0 \n\n' |>>>>

Ether() 은 이더넷 헤더를 IP() 는 IP 헤더를 의미한다. Ethere()/IP() 와 같이 사용하면 이 두 헤더를 만들어 주는 것이다. 구성된 헤더는 hexdump 로도 확인해 볼 수 있고, str 로도 각 헤더별 구성을 볼 수 있다. 예제를 보는 것 만으로도 대충 동작 형태의 감이 잡힐 거이다. 여러분들이 필요한 헤더를 원하는 대로 가져다 붙이면 되는 것이다.

10. 패킷 구성 조금 더 깊게 들여다 보기

조금더 패킷구성에 대해서 살펴보자. 헤더 마다 세부적으로 값들을 다 조정할 수 가 있는데, 아래는 b 에 IP 헤더를 집어 넣는데 목적지 주소는 192.168.100.100 으로 하고 있다. 목적지 주소만을 보기 위해 b.dst 를 사용했고, b.ttl 을 통해 ttl 을 볼 수도 있다. ttl 값을 128 로 변경하였다가 그 값을 다시 삭제한 것도 보인다.

TCP 도 마찬가지로 c 에 할당하고 flags 값을 설정해 보기도 하고 , 목적지와 출발지 포트도 설정했다. 그리고
z 라는 변수에 IP()/TCP() 로 만들어 넣었더니.

어라~ 이상하다. 내가 설정한 정보하고는 다르다. 여기서 주의할 것이. 이것은 단지 IP/TCP 기본 헤더정보를 넣은 거이다. 우리가 만든 정보를 넣은것이 아니므로 , x 변수에 넣은 것과 같이
x=b/c 와 같이 넣어주고 x.show() 로 확인해 보면 정상적으로 다 들어가 있는 것을 볼 수 있다.

>>> b=IP(dst="192.168.100.100")
>>> b
<IP  dst=192.168.100.100 |>
>>> b.dst
'192.168.100.100'
>>> b.ttl
64
>>> b.ttl="128"
>>> b
<IP  ttl='128' dst=192.168.100.100 |>
>>> del(b.ttl)
>>> b
<IP  dst=192.168.100.100 |>
>>>

>>> c=TCP()
>>> c
<TCP  |>
>>> c.flags="PA"
>>> c.flags
24
>>> c
<TCP  flags=PA |>
>>> c.dport=80
>>> c.sport=2080
>>> c
<TCP  sport=2080 dport=www flags=PA |>
>>> z=IP()/TCP()
>>> z
<IP  frag=0 proto=tcp |<TCP  |>>
>>> z.show()
###[ IP ]###
  version= 4
  ihl= 0
  tos= 0x0
  len= 0
  id= 1
  flags=
  frag= 0
  ttl= 64
  proto= tcp
  chksum= 0x0
  src= 127.0.0.1
  dst= 127.0.0.1
  options= ''
###[ TCP ]###
     sport= ftp_data
     dport= www
     seq= 0
     ack= 0
     dataofs= 0
     reserved= 0
     flags= S
     window= 8192
     chksum= 0x0
     urgptr= 0
     options= {}

>>> x=b/c
>>> x
<IP  frag=0 proto=tcp dst=192.168.100.100 |<TCP  sport=2080 dport=www flags=PA |>>
>>> x.show()
###[ IP ]###
  version= 4
  ihl= 0
  tos= 0x0
  len= 0
  id= 1
  flags=
  frag= 0
  ttl= 64
  proto= tcp
  chksum= 0x0
  src= 192.168.0.240
  dst= 192.168.100.100
  options= ''
###[ TCP ]###
     sport= 2080
     dport= www
     seq= 0
     ack= 0
     dataofs= 0
     reserved= 0
     flags= PA
     window= 8192
     chksum= 0x0
     urgptr= 0
     options= {}
>>>

11. 패킷 전송하기

첫번째 이야기에서도 간단하게 패킷 전송을 다뤄보았다. 앞에서 만든 x 변수를 send 를 통해 쉽게 전송했다. 이 뜻은, 여러분이 필요한 형태로 패킷을 아주 쉽게 만들 수 있다는 것이다. 그리고 send 를 통해 전송을 하고.
이외 파일로 저장된 패킷파일을 바로 불러들여 전송을 시킬 수도 있다.

>>> send(x)
.
Sent 1 packets.
>>> sendp(rdpcap("/home/debian/test.pcap"))
WARNING: DNS RR prematured end (ofs=45, len=42)
WARNING: DNS RR prematured end (ofs=50, len=42)
WARNING: more DNS RR prematured end (ofs=45, len=42)
..........................................................................................................................................
Sent 138 packets.
>>>

Scapy 의 주요한 몇 가지 기능과 사용방법에 대해서 알아보았는데, 굳이 설명이 없더라도
소개한 예제만 보고도 기능이 충분히 이해가 될 것이다. 파이썬 기반이라도 설명을 했기 때문에
다양한 프로그램적인 요소를 접목하기도 아주 쉽다. 일단은 이 정도만 알아두어도
Scapy 를 이용해 활용하기에는 큰 무리가 없을 것이며, 더 많은 기능은 Scapy 사이트에서 문서를 참고해 보기 바란다. 더욱 많은 기능에 놀랄지도 모르겠다. :-)

마지막으로 세번째 이야기에서는 Scapy 를 프로그램 관점에서 소개해 보도록 하겠다.

From Rigel.

[Scapy 연관글]

2010년 10월 20일 수요일

강력한 패킷 조작 프로그램 Scapy 를 소개한다 - 첫번째

Scapy

지금까지 패킷분석과 관련한 많은 도구들을 언급하였는데, 이 도구를 미처 소개하지 못했다.
강력한 기능과 프로그램적인 면에서도 뛰어난 도구인데말이다. 필자가 오늘 소개하고자 하는 도구는
Scapy 라는 것이다. Scapy 는 인터렉티브하게 패킷을 조작할 수 있는 강력한 기능을 제공하며,
수 많은 프로토콜의 디코딩 기능과 수정된 패킷을 전송할 수 있는 기능등을 포함하고 있다.

이 도구의 큰 장점은 다양한 기능을 수행할 수 있는 것인데, 우리가 흔히 스캐닝을 하거나, 패킷덤프를
하거나, 원격 시스템의 Alive 유무를 점검 또는 공격 패킷등을 만들고자 할때 사용하는 도구들이
다 달라진다. 예를 들면, hping, nmap, arpsoof, tcpdump, tshark, p0f 등의 여러 도구의 기능들 말이다
하지만 Scapy 는 이 도구 하나로 가능하다. 너무 강력한 도구인양 소개한 것일까?

사용용도에 따라 달라지겠지만 무척이나 편리한 기능을 갖고 있는 것은 사실이다. 다만 사용하는데 있어서는
일반적인 도구에 비해서는 사용방법이 처음에는 힘들 수도 있다. Scapy 는 파이썬을 기반으로
작성되어 있는데, 사용형태가 파이썬에서 코드를 사용하는 것과 같이 사용할 수 있으므로
파이썬에 익숙해져 있는 사용자라면 더 없이 편할 것이다. 또한, Scapy 모듈을 이용하여,
파이썬에서 패킷을 핸들링 하는 프로그램을 작성한다면 간단하게 그 기능을 이용할 수 있어
개발자 관점에서도 유용하다.

뛰어나다는 설명보다도 일단 직접 경험해 보는 것이 빠르다.  바로 설치해 보고 각 기능을
사용해 보면 앞서 얘기한 것들이 이해가 될 것이다.

1. 설치

Scapy 2.x 이상의 버전에서는 파이썬 2.5 이상이 필요하다. 그리고 libpcap, libdnet 이 필요하다.
보통 많은 경우에는 이런것들이 이미 설치되어 있으므로 사용하는데 큰 문제는 없을 것이다.

다음의 경로에서 파일을 다운로드 받을 수 있다:


$ unzip scapy-latest.zip
$ cd scapy-2.*
$ sudo python setup.py install

하는 것으로 쉽게 설치가 된다. 파이썬에 익숙한 사용자라면 어렵지 않을 것이다. 또는,
각 시스템에서 제공하는 패키지가 있다면 그것을 이용해도 좋다.

# apt-get install scapy

* 참고로 윈도우 기반에서 사용하고자 한다면, 다음의 문서를 참고하여 실행해 보길 바란다.
http://www.secdev.org/projects/scapy/doc/installation.html#platform-specific-instructions

2. 간단한 사용 예

실행하게 되면 다음과 같은 화면을 볼 수 있다.

# scapy
WARNING: No route found for IPv6 destination :: (no default route?)
Welcome to Scapy (2.1.0)
>>>

(설치되어 있는 모듈 및 환경에 따라, 경고 메시지가 뜰 수 있는데 무시하고 사용 가능하다.단, 해당 모듈 기능을 사용하지 않는 한)

몇 가지 간단한 기능을 살펴보자!

>>> ls()
ARP        : ARP
BOOTP      : BOOTP
CookedLinux : cooked linux
DHCP       : DHCP options
DNS        : DNS
DNSQR      : DNS Question Record
DNSRR      : DNS Resource Record
....
현재 지원하는 레이어 형태를 볼 수 있다. 쉽게 보면 프로토콜 이다.

다음은 존재하는 PCAP 파일을 오픈하는 것이다. 오픈할 파일을 " 없이 사용하는 경우는 다른 형태로 이해한다.
오픈 후에는 파싱되어 메모리 상에 다 들어가 있으므로 바로 바로 필요한 내용을 꺼내 사용할 수 있다.

>>> a=rdpcap(test.pcap)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'test' is not defined
>>> a=rdpcap("test.pcap")
>>> a
<test.pcap: TCP:2040 UDP:0 ICMP:0 Other:0>
>>>

파일 전송도 할 수 있는데, 원하는 형태로 마음대로 수정하여 전송이 가능하다. 각 프로토콜 레이어 별로 정보를 기록하고 헤더를 붙일 수 있다. 아래의 경우는 IP 헤더에서 목적지를 1.2.3.4 로 설정하고, ICMP 헤더를 붙인 것이다.

>>> send(IP(dst="1.2.3.4")/ICMP())
.
Sent 1 packets.

전송하기 전 tcpdump 로 확인한 내용을 보면, 목적지 1.2.3.4 로 ICMP 패킷이 전송된걸 확인할 수 있다.

# tcpdump -v icmp
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
09:07:36.479199 IP (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto ICMP (1), length 28) packet > 1.2.3.4: ICMP echo request, id 0, seq 0, length 8

이 정도에서 간단한 맛보기를 언급하고 다음 이어지는 두번째 편에서 좀더 세부적으로 Scapy 기능을 알아보도록 하겠다. 계속 이것저것 소개할 것이 많은데, 정리하고 쓰자니 시간이 꽤 걸리는 거 같다. 이래서 정리는 인내가 필요해지는 것 같다 :-)

2010년 8월 27일 금요일

VIM 의 플러그인을 활용해 기능을 확장해 보자.

VI 를 사용하다 보면 뛰어난 기능에도 놀라지만, 플러그인을 활용하면
활용범위를 더욱 넓힐 수가 있다. 그 중에 두 가지를 한번 소개해 보고자 한다.

1) 트리형태 구조로 편집할 파일을 빨리 찾아보자.

NERDtree 플러그인으로 아래 그림과 같이 왼쪽 편은 디렉토리 목록이
트리형태로 나오고 오른쪽 화면은 편집화면이다. 때로는 많은 파일을 처리해야
하는 경우가 있다. 이럴때 탐색기 같이 좌측 화면에서는 편집할 파일을 찾아
선택하면 우측 화면에 표시가 된다.

다음의 경로에서 다운받고, 압축파일을 사용자 디렉토리의 .vim 아래에 풀면 된다.
그러면 plugin, doc 폴더에 각각 들어간다.

실행은 아래와 같이 하면 되고, 각 창간의 이동은 Ctrl-w(w 두번 반복) 로 할 수 있다.

:NERDtree [경로]
세부적인 사용방법은 doc 파일안에 있는 설명서를 참고하면 된다.
파일편집을 많이 하는 사용자에게는 유용한 플러그인이 될 것이다.

2) 파이썬 코드를 폴더 형태로 접어보자.

파이썬 코드를 작성하다 보면, 특히 함수가 많은 경우에는 라인은 점점 길어지게 된다.
이럴때 유용하게 사용할 수 있는 것이, 함수를 폴더 형태같이 접었다, 필수 있는 형태의
기능이다. 아래 예제는 213라인 인데 한눈에 딱 들어오게 보인다. 각 함수를 정의한 부분이
폴더같이 접혀 있기 때문이다.

사용하기 위해서는 다음 파일을 다운로드 받아 plugin 에 넣어 놓고 사용하면 된다.

전체 폴더를 한번에 펴려면 Shift-f 를 누르면 되고, 특정한 함수 부분만 펼치려면
해당 라인이 표시된 곳으로 가서 f 를 누르면 된다. 파이썬으로 작업을 많이 하는
사용자에게는 나름 편리할 것이다.

2010년 8월 18일 수요일

2년여만에 새롭게 나온 Vim 7.3 버전

Kyle Usbeck

이미지출처 : www.cs.drexel.edu

유닉스 환경에서 많이 사용되는 에디터 프로그램인 VI. 윈도우용으로도 나와 있어 많이 사용되는 에디터이다. 유닉스 시스템을 사용한다면 VI 는 기본적으로 사용할 것이다.
이중에서도 Vim (Vi IMproved) 이 많이 사용되는데 7.2 버전이 나온 후,
2년 여 만에 7.3 버전이 나왔다. 많은 버그들이 픽스 되었고, 주요한 변화는 아래와 같다:
- Persistent undo and undo for reload
- Blowfish encryption, encryption of the swap file
- Conceal text
- Lua interface
- Python 3 interface

스왑파일도 암호화가 되고, Lua 와 파이썬 3 인터페이스가 지원된다. VI 는 아주 많이 사용하고
있는데, 사실 파이썬, 펄, 루비와 같은 인터페이스가 지원되는 줄은 몰랐다. 이번 업데이트
사항을 보고 이래저래 다시 살펴보니 내가 알고 있는 것 이상으로 더욱 많은 기능이 있었다.
그래도 남들 이상으로 다양한 기능을 많이 사용한다고도 생각했지만, 역시나 전체의 일부분만
사용하고 있던 셈이었다. 일반적으로 사용되는 프로그램의 기능중 주요 기능만 일부 사용되는 경우가 많은데
그 프로그램을 제대로 알고 있다면 사용할 수 있는 기능의 범위는 아주 넓어질 것이다.

일단 최신버전의 다운로드는 다음의 경로에서 할 수 있다.
http://www.vim.org/

그리고 오른쪽 메뉴에 *NiX Geek 라는 새로운 메뉴를 만들었다. 이곳에는 리눅스/유닉스 시스템과 관련한
팁 및 유용한 도구 등 다양한 것을 소개해 보고자 한다.
앞으로 이것저것 할것이 많으니 큰일이긴 하다 ^^

2010년 8월 6일 금요일

HTML 형태의 패킷 분석 보고서는 어떤가요?

지금까지 소개한 패킷분석 프로그램은 대부분 텍스트 형태였다. 만약 HTML 형태로 나오면 어떨까?
분석대상에 따라서 달라지겠지만, 때로는 간단하게 텍스트 형태가 편할 수도 있고, 데이터 내용이 많으면
좀더 쉽게 정리되어 볼 수 있는 HTML 형태가 편할수도 있을 것이다. 이것은 분석가의 판단에 따라서
상황에 맞는 것을 선택하면 된다.

일단, 지금 소개하고자 하는 것은 'pcapline' 이라는 것으로 패킷을 분석해 HTML 형태로 만들어주며
세션별로도 구분해준다. pcapline 은 파이썬으로 작성되어 있으며, 다음 경로에서 받을 수 있다.


실행은 분석할 PCAP 파일을 지정해 주는 것으로 끝난다.

# ./pcapline.py test.pcap
[*] Pcapline v0.9
[*] Processing pcap
[*] Generating report

분석이 완료되고 리포트가 생성되면 해당 폴더(실행한 파일이름의 output )에 아래와 같이 서브디렉토리가 존재하고 index.html 이 있다.

# ls -l
total 120
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0001
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0002
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0003
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0004
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0005
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0006
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0007
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0008
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0009
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0010
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0011
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0012
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0013
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0014
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0015
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0016
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0017
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0018
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0019
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0020
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0021
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0022
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0023
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0024
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0025
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0026
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0027
drwxr-xr-x 2 rigel rigel 4096 2010-07-28 23:37 0028
-rw-r--r-- 1 rigel rigel 8010 2010-07-28 23:37 index.html

index.html 을 브라우저에서 읽어들이면 아래와 같은 분석 보고서를 볼 수 있다. 호스트별로 구분되어 있고, 전송된 데이타량 그리고 해당 패킷이 어떤것인지 나타난다. 여기서 이제 각 Flow 를 선택하게 되면 세부적인 정보를 더 볼 수 있게 된다. (참고로, 여기서 사용된 것은 랜덤하게 IP 를 변경한 것이다)


세부 정보를 선택했더니, 더 자세한 내용들이 나타난다. 스트링 데이터도 볼 수 있고, HEX 값도 볼 수 있다.
각 요약 정보는 굳이 설명하지 않더라도 어떤 내용인지 쉽게 이해가 될 것이다.
HTML 형태의 보고서로 만들어 주므로 웹 브라우저 등이 있어야 하는 불편함은 있지만, 어떤 면에서는 더욱 쉽게 내용을 확인할 수 있는 방법을 제공해 준다. 파이썬이 설치되어 있다면 한번 사용해 보는 것도 괜챦다.

혹시 프로그램이 제대로 실행되지 않는다면, 설치된 파이썬 버전은 어떻게 되는지 확인해 보자. 파이썬 2.5 버전을 사용하는 경우에는 실행시 오류가 발생된다. 최신 버전인 2.7 을 사용하면 문제 없을 것이다. (2.6 도 OK)

2010년 7월 11일 일요일

와이어샤크에서 깨져보이는 '한글', 이거 무슨 뜻이지?

와이어샤크를 통해 패킷 분석을 하는 경우, 아래와 같이 문자열이 <xx> 로 보이는 경우가 있다. 문자열로 추측이 되는데 무엇일까? 한글이 이렇게 표현되는 경우가 있다.  왜 와이어샤크에서 한글이 깨져보일까?
와이어샤크에서는 한글 컨버전이 제대로 안돼 한글을 windows-1252 코드로 표현한 것이다. 참고로 영어와 서방 언어 윈도우에서 기본으로 사용되는 코드페이지이다.


가끔 이렇게 표현되는 한글이 무엇일까? 필요한 경우가 있다. 간단한 것은 우리가 알아볼 수 있도록 컨버전을 하는 것이다. 다양한 방법을 통해서 할 수 있겠는데, 일단 필자는 다음과 같이 파이썬을 통해 해 보았다.

x 변수에 각 값을 넣어주고, 디코드를 EUC_KR 로 해 보았다.

>>> x = "\xb0\xe6\xbf\xb5\xc1\xf6\xbf\xf8\xc6\xc0"
>>> print x.decode("euc_kr")
경영지원팀
>>>

그러면 짠 하고 의미가 나타난다. 넷바이오스 관련한 것을 보다보면 이러한 이름뒤에 <00>, <1b>, <20>, <1c> 를 볼 수 있다.  예를 들면 ,  TEST<00>, 홍길동<1C> 와 같은 것들이다. 위에서도 마찬가지로 이름에서 젤 마지막에 붙은 <00> 은 실제 이름이 아니고 다른 의미이므로 그것은 떼어내고 변환해야 한다.

이렇게 변환외에도 알수 있는 방법은 출발지 IP 를 보고 nbtstat 명령어를 통해 보는 것이다.

C:\>nbtstat -A 192.168.x.x
Local Area Connection:
Node IpAddress: [192.168.x.x] Scope Id: []

           NetBIOS Remote Machine Name Table

       Name               Type         Status
    ---------------------------------------------
    TEST12          <00>  UNIQUE      Registered
    경영지원팀     <00>  GROUP       Registered

    MAC Address = 00-19-B9-XX-XX-XX

단, 같은 네트워크 라면 모르지만 원격의 경우는 여러 제약이 있으므로 패킷만 전달받은 상태에서는 이렇게 알기 힘드므로 직접 변환 작업등이 필요하다.

아래 도표는 NetBios 의 Number 를 정리한 도표이다.

NameNumberTypeUsage
<computername>00UWorkstation Service
<computername>01UMessenger Service
<''_MSBROWSE_>01GMaster Browser
<computername>03UMessenger Service
<computername>06URAS Server Service
<computername>1FUNetDDE Service
<computername>20UFile Server Service
<computername>21URAS Client Service
<computername>22UExchange Interchange
<computername>23UExchange Store
<computername>24UExchange Directory
<computername>30UModem Sharing Server Service
<computername>31UModem Sharing Client Service
<computername>43USMS Client Remote Control
<computername>44USMS Admin Remote Control Tool
<computername>45USMS Client Remote Chat
<computername>46USMS Client Remote Transfer
<computername>4CUDEC Pathworks TCPIP Service
<computername>52UDEC Pathworks TCPIP Service
<computername>87UExchange MTA
<computername>6AUExchange IMC
<computername>BEUNetwork Monitor Agent
<computername>BFUNetwork Monitor Apps
<username>03UMessenger Service
<domain>00GDomain Name
<domain>1BUDomain Master Browser
<domain>1CGDomain Controllers
<domain>1DUMaster Browser
<domain>1EGBrowser Service Elections
<INet~Services>1CGInternet Information Server
<IS~Computer_name>00UInternet Information Server
<computername>[2B]ULotus Notes Server
IRISMULTICAST[2F]GLotus Notes
IRISNAMESERVER[33]GLotus Notes
Forte_$ND800ZA[20]UDCA Irmalan Gateway Service

또 다른 예제를 보자. 아래는 LLMNR 프로토콜에서 이름 쿼리를 한 것인데. 위에서 본것과 같이 <xx> 로 표현된 것이 아니라 \xxx\xxx 와 같이 표현되었다. 이건 또 무엇일까? 이 값은 OCT 로 표현된 것이다. 즉, 네트워크 패킷을 덤프할때 시스템 언어의 영향을 받아 덤프할때 언어 표현이 달라진 것이다.


자, 이것은 아래와 같이 변환을 해보자. (참고로 파이썬은 윈도우에서도 사용이 가능하다)
Query 값을 복사할때는 Detail 창에서 해당 값 위에서 오른쪽 클릭을 하면 Copy->Value 를 통해 쉽게
복사가 가능하다는 것을 잊지말자!

# python
Python 2.5.2 (r252:60911, Jan 24 2010, 14:53:14)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = "\355\231\215\352\270\270\353\217\231"
>>> type(x)
<type 'str'>
>>> print x.decode('utf-8')
홍길동
>>>

내용을 utf-8 로 디코드 해 보았더니 홍길동 이라는 이름이 나온다.  자, 필요한 경우 한글로 변환해서
패킷 분석에 도움이 될만한 정보를 얻어보도록 하자!


[참고]
1. Windows-1252