Scapy を使用したパケット作成

サイバーセキュリティBeginner
オンラインで実践に進む

はじめに

Scapy は、Python で記述された強力な対話型パケット操作プログラムおよびライブラリです。これを使用すると、さまざまなプロトコルのパケットを作成またはデコードし、ネットワーク上に送信してキャプチャし、要求と応答を照合することができます。

Scapy の最大の特徴は、ソケット層やアプリケーション層だけでなく、パケット構築層で直接作業できる点です。これにより、通常のクライアントライブラリでは隠蔽されがちなヘッダー、フラグ、フィールド、プロトコルの組み合わせを明示的に制御できます。

この実験では、Scapy を使用したパケット作成の基礎を学びます。まず必要なモジュールをインポートし、次に ICMP(ping)パケットや TCP パケットをレイヤーごとに作成します。最後に、これらのカスタムパケットを送信してネットワーク応答を解析します。この実践的な経験を通じて、ネットワークプロトコルの内部動作や、セキュリティ専門家がどのようにネットワーク防御をテストしているかについて、より深い理解を得ることができます。

Scapy モジュールのインポート

このステップでは、Python 環境をセットアップし、必要な Scapy モジュールをインポートします。Scapy は対話型コンソールとしても使用できますが、スクリプトを作成することで、パケット作成ロジックを保存、修正、再利用しやすくなります。

これは、既存のプロトコルを使用する段階から、プロトコルを構築する段階への移行です。既存のアプリケーションにトラフィックの送信を依頼するだけでなく、トラフィックがどのようなものであるべきかを自分自身で定義します。

まず、デフォルトの作業ディレクトリにいることを確認してください。pwd コマンドで確認できますが、通常は ~/project にいるはずです。

nano テキストエディタを使用して、packet_crafter.py という名前の新しい Python ファイルを作成します。

nano packet_crafter.py

このファイル内で Scapy ライブラリをインポートする必要があります。スクリプトで Scapy を使用する標準的な方法は、scapy.all モジュールからすべての関数をインポートすることです。以下の行をスクリプトに追加してください。

from scapy.all import *

print("Scapy modules imported successfully.")

Ctrl+O を押してから Enter を押してファイルを保存します。テスト前にスクリプトの構築を続ける場合は nano を開いたままで構いません。ターミナルに戻る準備ができたら Ctrl+X を押してください。

次に、Python を使用してスクリプトを実行し、構文エラーがないことと Scapy ライブラリが正しく読み込まれることを確認します。生のネットワークパケットを作成するには管理者権限が必要なため、sudo を使用してスクリプトを実行する必要があります。

sudo python3 packet_crafter.py

成功すると、以下のような出力が表示されます。

Scapy modules imported successfully.

実験環境によっては、成功メッセージの前に matplotlib などのオプションの Python パッケージから警告が表示される場合があります。Scapy modules imported successfully. と表示されていれば、次のステップに進んで問題ありません。

ICMP エコー要求の作成

このステップでは、一般的に「ping」として知られる ICMP エコー要求パケットを作成します。ネットワークパケットは、OSI 参照モデルに従ってレイヤーごとに構築されます。Scapy では、/ 演算子を使用してこれらのレイヤーを積み重ねることができます。

レイヤー化は、ここで理解すべき最も重要な概念の一つです。各レイヤーは独自のフィールドと役割を追加します。Scapy はそのモデルを直接反映しているため、パケット構造を論理的に理解しやすくなっています。

ICMP ping には、送信元アドレスと宛先アドレスを指定するための IP レイヤー(ネットワーク層)と、メッセージのタイプを定義するための ICMP レイヤーが必要です。

packet_crafter.py が nano で開いたままの場合は、そのまま編集を続けます。閉じてしまった場合は、再度開いてください。

nano packet_crafter.py

以下のコードをスクリプトの末尾に追加します。このコードは、Google のパブリック DNS サーバー(8.8.8.8)を宛先とする IP オブジェクトを作成し、ICMP オブジェクトを作成して、それらを積み重ねます。show() メソッドは、パケット構造の詳細な内訳を表示します。

## Google DNS を宛先とする IP レイヤーを作成
ip_layer = IP(dst="8.8.8.8")

## ICMP レイヤー(エコー要求)を作成
icmp_layer = ICMP()

## レイヤーを積み重ねて最終的なパケットを形成
icmp_packet = ip_layer / icmp_layer

print("\n--- ICMP Packet Structure ---")
icmp_packet.show()

Ctrl+O を押してから Enter を押してファイルを保存します。実行前にスクリプトをさらに完成させたい場合は、nano を開いたまま次のステップに進んでください。

更新したスクリプトを実行して、作成したパケットの構造を確認します。

sudo python3 packet_crafter.py

Scapy が自動的に補完したデフォルト値(プロトコルバージョン、TTL(Time To Live)、ICMP タイプなど)を含む詳細な出力が表示されます。

--- ICMP Packet Structure ---
###[ IP ]###
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     =
  frag      = 0
  ttl       = 64
  proto     = icmp
  chksum    = None
  src       = 10.0.2.15
  dst       = 8.8.8.8
  \options   \
###[ ICMP ]###
     type      = echo-request
     code      = 0
     chksum    = None
     id        = 0x0
     seq       = 0x0
     unused    = ''

カスタム TCP SYN パケットの作成

このステップでは、TCP SYN パケットを作成します。このタイプのパケットは、ネットワークスキャン(Nmap のステルススキャンなど)で、接続を完全に確立することなくターゲットマシンの特定のポートが開いているかどうかを確認するためによく使用されます。

これは、ヘッダーのわずかな変更がネットワークの挙動に大きな違いをもたらす好例です。宛先ホストやポートを変更しているわけではなく、フラグとプロトコルフィールドを変更しているだけですが、それらの詳細がパケットの意味とターゲットの解釈方法を決定します。

ICMP パケットと同様に、TCP パケットにも基礎となる IP レイヤーが必要です。その上に、宛先ポートと TCP フラグを指定できる TCP レイヤーを追加します。S フラグは SYN(Synchronize)を表します。

同じ nano セッションにいる場合は、packet_crafter.py の編集を続けます。そうでない場合は、再度開いてください。

nano packet_crafter.py

以下のコードをスクリプトに追加します。前のステップで作成した ip_layer を再利用し、その上にポート 80(HTTP)をターゲットとする新しい TCP レイヤーを積み重ねます。

## ポート 80 をターゲットとし、SYN フラグを設定した TCP レイヤーを作成
tcp_layer = TCP(dport=80, flags="S")

## IP レイヤーと TCP レイヤーを積み重ねる
tcp_packet = ip_layer / tcp_layer

print("\n--- TCP SYN Packet Structure ---")
tcp_packet.show()

Ctrl+O を押してから Enter を押してファイルを保存します。ターミナルからスクリプトを実行したい場合のみ Ctrl+X で終了するか、編集を続けて最後のステップに進んでください。

スクリプトを再度実行して、新しい TCP SYN パケットの構造を検査します。

sudo python3 packet_crafter.py

出力には TCP レイヤーの詳細が含まれ、宛先ポートが http(ポート 80)に設定され、フラグが S に設定されていることが確認できます。

--- TCP SYN Packet Structure ---
###[ IP ]###
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     =
  frag      = 0
  ttl       = 64
  proto     = tcp
  chksum    = None
  src       = 10.0.2.15
  dst       = 8.8.8.8
  \options   \
###[ TCP ]###
     sport     = ftp_data
     dport     = http
     seq       = 0
     ack       = 0
     dataofs   = None
     reserved  = 0
     flags     = S
     window    = 8192
     chksum    = None
     urgptr    = 0
     options   = []

ネットワーク応答の送信と解析

この最後のステップでは、実際に作成した ICMP パケットをネットワーク経由で送信し、応答をキャプチャします。

パケットを送信することで、一連の流れ(レイヤーの定義、パケットの構築、送信、応答の解釈)が完結します。これは、多くのカスタムネットワークテストツールで共通する高レベルなループ処理です。

Scapy にはパケットを送信するための関数がいくつか用意されています。sr1() 関数は、パケットを送信して最初に受信した応答を返すため、特に便利です。「s」は send(送信)、「r」は receive(受信)、「1」は 1 つのパケットを意味します。

ファイルを開いたままにしている場合は編集を続けます。そうでない場合は packet_crafter.py を再度開いてください。

nano packet_crafter.py

以下のコードを追加して icmp_packet を送信し、応答を解析します。パケットがドロップまたはブロックされた場合にスクリプトが永久に停止しないよう、timeout パラメータを含めています。

print("\n--- Sending ICMP Packet ---")
## パケットを送信し、1 つの応答を待機(タイムアウトは 2 秒)
## verbose=0 は、Scapy のデフォルトの送信出力を非表示にして結果を見やすくします
response = sr1(icmp_packet, timeout=2, verbose=0)

if response:
    print(f"Response received from: {response[IP].src}")
    print("\n--- Response Packet Structure ---")
    response.show()
else:
    print("No response received. The packet might have been dropped.")

ファイルを保存してエディタを終了します。

スクリプトを実行してパケットをインターネットに送信し、Google の DNS サーバーからの応答を待ちます。

sudo python3 packet_crafter.py

ネットワークが ICMP 要求を許可している場合、応答パケットの構造が表示されます。応答内の ICMP タイプが echo-reply になっていることに注目してください。

--- Sending ICMP Packet ---
Response received from: 8.8.8.8

--- Response Packet Structure ---
###[ IP ]###
  version   = 4
  ihl       = 5
  tos       = 0x0
  len       = 28
  id        = 0
  flags     =
  frag      = 0
  ttl       = 114
  proto     = icmp
  chksum    = 0x67d8
  src       = 8.8.8.8
  dst       = 10.0.2.15
  \options   \
###[ ICMP ]###
     type      = echo-reply
     code      = 0
     chksum    = 0xffff
     id        = 0x0
     seq       = 0x0
     unused    = ''
###[ Padding ]###
        load      = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

まとめ

Scapy を使用した Python スクリプトのセットアップ方法、/ 演算子を使用して ICMP や TCP パケットをレイヤーごとに手動で作成する方法、そしてそれらのパケットをネットワーク経由で送信して生の応答を解析する方法を学びました。この基礎知識は、ネットワークプロトコルの動作を理解し、セキュリティ評価を実施し、独自のネットワーク解析ツールを開発するために不可欠です。