▼ 2009/04/16(木) Scapyでネットワーク遊び
Pythonで作られたScapyってやつが恐ろしいほど便利で楽しいのでメモ書きをしておきますよ。導入方法の例は、Ubuntu 8.10。ちなみにaptitudeでScapyのインストールできるけど、v1.2だった気がする。v2.xを使いたいのでaptitudeでScapyのインストールはしないことにする。v2.xを使いたい場合はPythonのバージョンが2.5以上が必要なので先ずシステムに入ってるPythonのバージョンを確認しておく。
$ python -V Python 2.5.2
続いて、libdnetが必要なのでインストール。
$ sudo aptitude install libdnet
あとは以下のものもインストールしておくと便利なので、aptitudeでインストールしておいたほうがいい。
- gnuplot
- tcpdump
- graphviz
- imagemagick
- nmap
- python-gnuplot
- python-pyx
- python-crypto
- python-visual
Scapyからlatestのzipをダウンロードして以下のようにインストールする。
$ unzip scapy-latest.zip $ cd scapy-2.0.1 $ sudo python setup.py install
Scapyがあるかどうか確認。
$ which scapy /usr/bin/scapy
Scapyの実行にはroot権限が必要。試しに実行してみる。
$ sudo scapy INFO: No IPv6 support in kernel WARNING: No route found for IPv6 destination :: (no default route?) Welcome to Scapy (2.0.1) >>>
これで使える状態になっているので、試しにls()なんて打ってみるとぞろぞろと出てくる。
>>> ls() ARP : ARP ASN1_Packet : None BOOTP : BOOTP ...
■ Scapyの使い方
パケットキャプチャ
Scapyで色々と遊んでみる。先ずはパケットキャプチャ。
>>> sniff(iface="eth0", prn=lambda x: x.show())
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x10 len= 1500
id= 616
flags= DF
frag= 0L
ttl= 64
proto= tcp
chksum= 0xab7d
src= 192.168.0.250 dst= 192.168.0.220
options= ''
...
sniff()を使えば上記のような出力が得られる。もっと簡単な表示でいい場合は、show()ではなくて、summary()を使う。
>>> sniff(iface="eth0", prn=lambda x: x.summary()) Ether / IP / TCP 192.168.0.250:ssh > 192.168.0.220:61264 PA / Raw Ether / IP / TCP 192.168.0.250:ssh > 192.168.0.220:61264 PA / Raw
hpingみたいなポートへのping
>>> ans,unans = sr(IP(dst="192.168.0.200", ttl=7)/TCP(dport=[21,22,25,53,80,110,143,443,445]), retry=-2)
Begin emission:......*.......*.......*......*......*........*.........*.......Finished to send 9 packets.
.*.........*
Received 75 packets, got 9 answers, remaining 0 packets
>>> ans.make_lined_table(lambda(s,r): (s.dport, s.dst, r.sprintf("%IP.id% {TCP:%TCP.flags%}{ICMP:%IP.src% %ir.ICMP.type%}")))
--------------+------+------+------+------+------+------+------+------+------+
| 21 | 22 | 25 | 53 | 80 | 110 | 143 | 443 | 445 |
--------------+------+------+------+------+------+------+------+------+------+
192.168.0.200 | 0 SA | 0 SA | 0 SA | 0 RA | 0 SA | 0 RA | 0 RA | 0 SA | 0 RA |
--------------+------+------+------+------+------+------+------+------+------+
もっと簡単な出力でいいなら、以下のようにやっちゃう。
>>> sr(IP(dst="192.168.0.200")/TCP(dport=[22,23,80])) Begin emission: .........*.....*....Finished to send 3 packets. .....* Received 26 packets, got 3 answers, remaining 0 packets(<Results: TCP:3 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>) >>> ans,unans=_ >>> ans.summary() IP / TCP 192.168.0.250:ftp_data > 192.168.0.200:ssh S ==> IP / TCP 192.168.0.200:ssh > 192.168.0.250:ftp_data SA / Padding IP / TCP 192.168.0.250:ftp_data > 192.168.0.200:telnet S ==> IP / TCP 192.168.0.200:telnet > 192.168.0.250:f tp_data RA / Padding IP / TCP 192.168.0.250:ftp_data > 192.168.0.200:www S ==> IP / TCP 192.168.0.200:www > 192.168.0.250:ftp_dat a SA / Padding
flagsがSAならポートが開いていて、RAであれば閉じている。
PDFも作れる
>>> p=IP()/ICMP()
>>> p.pdfdump("test.pdf")
たったこれだけなのに、何だかそれっぽいPDFが出来上がってくれるw
traceroute
>>> ans,unans = traceroute("www.yahoo.co.jp", dport=80)
Begin emission:
**************************Finished to send 30 packets.
****
Received 30 packets, got 30 answers, remaining 0 packets
203.216.235.201:tcp80
1 192.168.0.1 11
2 122.1.164.132 11
3 122.1.164.129 11
4 221.184.12.189 11
5 60.37.11.141 11
6 60.37.54.161 11
7 60.37.54.206 11
8 210.254.187.38 11
9 210.163.230.86 11
10 118.155.197.141 11
11 124.211.14.22 11
12 202.93.74.223 11
13 203.216.238.194 11
14 203.216.235.201 SA
15 203.216.235.201 SA
16 203.216.235.201 SA
17 203.216.235.201 SA
18 203.216.235.201 SA
19 203.216.235.201 SA
20 203.216.235.201 SA
21 203.216.235.201 SA
22 203.216.235.201 SA
23 203.216.235.201 SA
24 203.216.235.201 SA
25 203.216.235.201 SA
26 203.216.235.201 SA
27 203.216.235.201 SA
28 203.216.235.201 SA
29 203.216.235.201 SA
30 203.216.235.201 SA
ルーティング関連の操作
現在のルーティング情報の確認。netstat -rn, route -n の出力と同じようなものが出てくる。
>>> conf.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 eth0 192.168.0.250 169.254.0.0 255.255.0.0 0.0.0.0 eth0 192.168.0.250 0.0.0.0 0.0.0.0 192.168.0.1 eth0 192.168.0.250
指定したルーティング情報を削除してみる。その後に確認。
>>> conf.route.delt(net="0.0.0.0/0", gw="192.168.0.1") >>> conf.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 eth0 192.168.0.250 169.254.0.0 255.255.0.0 0.0.0.0 eth0 192.168.0.250
ルーティング情報を追加してみる。その後に確認。
>>> conf.route.add(host="192.168.1.100", gw="192.168.0.220") >>> conf.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 eth0 192.168.0.250 169.254.0.0 255.255.0.0 0.0.0.0 eth0 192.168.0.250 192.168.1.100 255.255.255.255 192.168.0.220 eth0 192.168.0.250
最初の状態に戻す。
>>> conf.route.resync() >>> conf.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 eth0 192.168.0.250 169.254.0.0 255.255.0.0 0.0.0.0 eth0 192.168.0.250 0.0.0.0 0.0.0.0 192.168.0.1 eth0 192.168.0.250
DHCPサーバを見つける
DHCP discover requestを送ることができる。
>>> conf.checkIPaddr = False
>>> fam,hw = get_if_raw_hwaddr(conf.iface)
>>> dhcp_discover = Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)/BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"), "end"])
以下を実行後は、数秒後にCTRL-C して止める
>>> ans,unans = srp(dhcp_discover, multi=True)
結果の出力
>>> ans.summary()
Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.0.1:bootps > 255.255.255.255:bootpc / BOOTP / DHCP
>>> for p in ans: print p[1][Ether].src, p[1][IP].src
...
00:0a:79:aa:aa:aa 192.168.0.1
こんな感じで色々と遊べます。ARP Cache Poisoningなんかもできたり。便利だし遊べるのでインストールしておいて損は無いですよ。
【参考サイト】
- TB-URL http://chibilog.name/0365/tb/

