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 연관글]

댓글 없음:

댓글 쓰기