パケット遊び ARP編
この記事は僕の趣味である「パケット遊び」をたくさんの人に知ってもらい、ぜひパケット仲間を増やそう!といった内容の記事です。
What is LibPGEN
LibPGEN (りぶ ぴーじぇん) は筆者(@slankdev)がパケット操作を自由に行うために開発した統合パケット操作補助のC++ライブラリです。 簡単なインターフェースで自由にパケットを操作して送受信を可能にしています。 また、既存のパケットを読み込み、ヘッダの値を変えて再度バイナリ生成、などといったことにもできます。
前提知識
今回はARPパケットを使用して遊ぶため、Ethernet, ARPの基礎知識と入門書程度のC++の知識を前提として進めていきます。
一応ARPパケットのおさらい
今回作って遊ぶARPパケットの構造とヘッダについて軽くおさらいします。必要ない人は飛ばしてもらって結構です! ARPはL3のアドレスからL2プロトコルのアドレスを求めるために使用されるプロトコルです。一般的にはIPアドレスからMACアドレスを調べるときに使いますよね。今回はこの一般例について話を進めていきます。以下にARPヘッダを示します。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | HW Type | Protocol | +---------------+---------------+---------------+---------------+ | HW Len | Proto Len | Opcode | +---------------+---------------+---------------+---------------+ | Sender HW Address | +---------------------------------------------------------------+ | Sender HW Address | Sender Protocol Address | +---------------------------------------------------------------+ | Sender Protocol Address | Target HW Address | +-------------------------------+-------------------------------+ | Target HW Address | +---------------------------------------------------------------+ | Target Protocol Address | +---------------------------------------------------------------+
今回重要になる要素についてだけ軽く説明を行います。
| 要素 | 内容 |
|---|---|
| Operation | オペレーションコード. リクエストなら1,リプライなら2が入る |
| HW src | 送信者のMACアドレス |
| Proto src | 送信者のIPアドレス |
| HW dst | 送信先のMACアドレス, ARPリクエストなら全て0x00か0xff |
| Proto dst | 送信先のIPアドレス |
まあこんな感じですね。

ちなみにこれらの通信では以下のようなパケットが送られます。
ARPリクエスト
| 要素 | 内容 |
|---|---|
| Operation | 1 (Request) |
| HW src | AA:AA:AA:AA:AA:AA |
| Proto src | 192.168.0.1 |
| HW dst | ff:ff:ff:ff:ff:ff |
| Proto dst | 192.168.0.2 |
ARPリプライ
| 要素 | 内容 |
|---|---|
| Operation | 2 (Reply) |
| HW src | XX:XX:XX:XX:XX:XX |
| Proto src | 192.168.0.2 |
| HW dst | AA:AA:AA:AA:AA:AA |
| Proto dst | 192.168.0.1 |
これで前提知識はバッチリです!
今回のお題目
今回の目的は次の二つです。
- パケットは簡単に作れて面白い
- Wiresharkはすごい
パケットは簡単につくれて面白い
LibPGENはC++で簡単に様々なパケットを作成することを補助します。ではLibPGENを使ってARPパケットを作って送るプログラムを作って遊んでみましょう。
使用するプログラムを以下に示します。
#include <pgen.h>
const char* dev = "en0";
int main() {
pgen::net_stream handle;
handle.open(dev, pgen::open_mode::netif);
pgen_arp pack;
pack.ARP.operation = pgen::arp_operation::request;
pack.ARP.hwsrc.setmacbydev(dev);
pack.ARP.psrc.setipbydev(dev);
pack.ARP.psrc = "192.168.179.4";
pack.ARP.hwdst = "ff:ff:ff:ff:ff:ff";
pack.ARP.pdst = "192.168.179.1";
pack.ETH.src = pack.ARP.hwsrc;
pack.ETH.dst = pack.ARP.hwdst;
pack.compile();
pack.summary();
pack.hex();
handle.send(pack.raw(), pack.length());
}
コンパイルではlibpgenをリンクしてc++11でコンパイルしてください。
g++ -std=c++11 main.cc -lpgen
これを実行すると以下のようなバイナリが生成されネットワークインターフェースから送信されます。Wiresharkでキャプチャしてみると、ちゃんとしたARPパケットがキャプチャできていることがわかります。

Wiresharkはすごい
簡単にパケットは送信できてはいるのですが、これにもう一工夫すると、さらに面白いことが確認できます。今回はARPキャッシュポイズニングのパケットも作って送ってみましょう。
今作ったパケットは
| 要素 | 内容 |
|---|---|
| Operation | 1 (Request) |
| HW src | 80:e6:50:::** |
| Proto src | 192.168.179.4 |
| HW dst | ff:ff:ff:ff:ff:ff |
| Proto dst | 192.168.179.1 |
返ってきたパケットは
| 要素 | 内容 |
|---|---|
| Operation | 2 (Reply) |
| HW src | 00:00:00:00:00:00 |
| Proto src | 192.168.179.1 |
| HW dst | 80:e6:50:::** |
| Proto dst | 192.168.179.4 |
ですが、この返信パケットを少し偽装したパケットを同時に送信してみます。具体的には以下の図のようなことをしています。

別のホストから以下のようなパケットを作成して送信します。
| 要素 | 内容 |
|---|---|
| Operation | 2 (Reply) |
| HW src | YY:YY:YY:YY:YY:YY |
| Proto src | 192.168.179.1 |
| HW dst | 80:e6:50:::** |
| Proto dst | 192.168.179.4 |
こうなってしまうと最初にARPリクエストを送信したホストは知りたかったIPアドレスに対応付けられているMACアドレスを知ることが出ない可能性があります、これを利用して不正に通信の向きを改ざんしたりして情報を傍受する攻撃(MITM Attack)などもあります。危険です。。。
ちなみにWiresharkでこの通信の一部始終をキャプチャしていると…

すごい!すでに使われているアドレスで、変なパケットだって教えてくれる。。 ただのパケットキャプチャなのに、キャッシングしているパケットと比べて、変なパケットを調べるあたり、本当にすごい。。ってことでWiresharkはすごい! (注意, だからと言ってARPスプーフィングに対して安全という訳ではありません。)
このように簡単にパケットを作成したりすることが可能になります。LibPGENはこれだけでなく、他にも様々な機能があります。詳しくはlibpgen.orgを参照してください。