2013年01月10日

Python(Scapy)を使ってGateway Finderを作ってみる

ネットワークへ接続する為にIPアドレスとネットマスクは知っているけれど、ゲートウェイのアドレスは分からない、隠れているゲートウェイを見つけたい。 たまーにですけど、こんな状況ありますよね。

gateway-finder2

いざという時に手作業で調べていては大変。

今日はPython + ScapyでGateway Finderを作ってみます。

Scapyのインストール


何時もの通りapt-getでScapyネットワーク・ライブラリを用意します。

$ sudo apt-get install python-scapy

apt-getは使わずにsetup.pyでインストールするならこちら。

$ wget http://www.secdev.org/projects/scapy/files/scapy-latest.tar.gz
$ tar xzf scapy-latest.tar.gz 
$ cd scapy-.x.x/
$ sudo python setup.py install


Gateway Finderの考え方


Gatewayを見つけるロジックは次の通り。

  • ARPリクエストを送信して同一ネットワーク上のノード(IP,MACアドレス)情報をスキャンする
  • 存在するノードに対してTTLを1にしたICMPパケットを送信する
  • TTL値の減算が行われ時間超過(TTL=0,Time Exceeded )メッセージ(11)を返信したノードをルーターと考える
  • ルーターに対し、外部アドレス宛のICMPパケットを送信し、応答があればGatewayと判断する

気の利く人なら直ぐに思いつくかもしれませんね。

時間超過メッセージを返信するノードなんてそう簡単にはいないから3番目までで判断しても良いんじゃ無い?、と思うかもしれませんが最近はVMwareのような仮想化製品(その性質上、ルーティングやDHCPサーバー機能を持っている)を使っているなんてこともありますから、怪しい候補としてはリストしつつ、外部サーバーからICMP Replyを受け取るまでを組込み・判断します。

gateway-finder4


Code of Gateway Finder


コードはこちら。

import sys
import threading
import time
from scapy.all import *
import socket
import fcntl
import struct

ICMP_TYPE_ECHO_REPLY = 0
ICMP_TYPE_TTL_EXCEEDED = 11
END_POINT_ADDR = "8.8.8.8"
conf.iface = "eth0"
conf.verb = 0

if len(sys.argv) != 2:
    print "Usage: gateway.py   eg: sudo python gateway-finder.py 192.168.1.0/24"
    sys.exit(1)

def get_ip_addr(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    return socket.inet_ntoa(fcntl.ioctl(s.fileno(),
      0x8915,struct.pack('256s', ifname[:15]))[20:24])

iface_ip = get_ip_addr(conf.iface)
iface_hw = get_if_hwaddr(conf.iface)

# arp
ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=sys.argv[1]),timeout=3)
mac2ip = {}
for snd,rcv in ans:
  mac2ip[rcv.src] = rcv.psrc

# sniff & handle icmp packet
class ICMPSniffer(threading.Thread):

  def __init__(self, mac2ip):
    threading.Thread.__init__(self) 
    self.mac2ip = mac2ip

  def callbak(self, pkt):
    global iface_hw
    global iface_ip
    if ICMP in pkt:
      if pkt[ICMP].type == ICMP_TYPE_TTL_EXCEEDED:
        print "ROUTER?: %s\t%s" % (pkt[IP].src, pkt.src)
        icmp = Ether(src=iface_hw,dst=pkt.src)/IP(src=iface_ip,dst=END_POINT_ADDR)/ICMP()
        sendp(icmp)
      elif pkt[ICMP].type == ICMP_TYPE_ECHO_REPLY:
        print "GATEWAY: %s\t%s" % (self.mac2ip[pkt.src], pkt.src)

  def run(self):
    sniff(prn=self.callbak, filter="icmp", store=0)

icmpsniff = ICMPSniffer(mac2ip)
icmpsniff.daemon = True
icmpsniff.start()
time.sleep(1)

for mac,ip in mac2ip.iteritems():
  #print "IP NODE: %s\t%s" % (ip, mac)
  icmp = Ether(src=iface_hw,dst=mac)/IP(src=iface_ip,dst=END_POINT_ADDR,ttl=1)/ICMP()
  sendp(icmp)



このコードではインタフェースはeth0、外部への疎通確認用IPアドレス(END_POINT_ADDR)には8.8.8.8 (Google DNS)を指定しています。

最初に自身のIPアドレス(iface_ip)、MACアドレス(iface_hw)を取得しておき、ローカル・ネットワーク上にarpリクエストを送信、MACアドレスとIPアドレスとのマップを作成しておきます。
(インタフェース名からそのIPアドレスを取得する簡単な方法って無いのかしら・・・)

続いてインタフェースをsniffし、ICMPパケットのハンドリングを行うスレッドを用意・開始しておきます。

このスレッドは受信したICMPパケットのメッセージタイプを見てTime Exceeded (11)であれば、ルーティング機能を持つノードだと判断して宛先アドレスをEND_POINT_ADDR (8.8.8.8)としたICMP Echo Request (8)を送信、ICMP Echo Reply (0)ならばGatewayだと判断します。
(書いていて思ったんですが、Echo Replyの場合に送信元IPアドレスがEND_POINT_ADDRか否かもチェックした方が確実ですね!)

最後にARPスキャンして集めた同一ネットワーク上にあるノードのMACアドレスを宛先にしたEtherオブジェクトに宛先IPアドレスをEND_POINT_ADDR (8.8.8.8)としたICMPパケット、つまりルーティング・リクエストを一斉に送信しています。

このパケットはTTLの値を1としているのでルーターであれば、値を1減算して0になり、パケット破棄及び送信元へのTime Exceeded (11)メッセージ送信が行われるので、先程用意しておいたスレッドで検知されるという訳なんです。

このコードをgateway-finder.pyとして保存、引数にローカルのネットワーク・アドレスを指定して起動してみます。

$ sudo python gateway-finder.py 192.168.1.0/24 
ROUTER?: 192.168.1.120  00:90:c1:1a:a6:f9
ROUTER?: 192.168.1.213  00:de:0b:a2:33:1e
ROUTER?: 192.168.1.254  00:a0:de:16:ff:30
GATEWAY: 192.168.1.254  00:a0:de:16:ff:30

よーし、ルーター候補を3ノード、実際に外部に接続できるゲートウェイを1ノード無事に見つけてくれました。


それでは、また今度!



Scapy
Scapy
posted with amazlet at 13.01.10

Equ Press

ハッカーと画家 コンピュータ時代の創造者たち
ポール グレアム
オーム社
売り上げランキング: 21,582

Posted by netbuffalo at 22:00│Comments(0)TrackBack(0)プログラミング | ネットワーク


この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/netbuffalo/4335463

コメントする

名前
URL
 
  絵文字