HappyNote3966’s blog

備忘録、作業記録的なことを書きます。低レベル注意。ご指摘等ございましたらやんわりとお願いします(´;ω;`)

ARPのキャプチャをしてみる

前回の記事に引き続いて、今度はARPをキャプチャできるようにプログラムを作成してみた。

作成したプログラムはこちら。

github.com

これまでの知識やWiresharkでキャプチャした結果を頼りに作ってみた。

以下に示すのは重要な部分のコード。

int displayARP(u_char *arp_data){
    nextfunc = NULL;
    printf("|============= ARP DATA =============|\n");
    int hardware_type = data2value16(&arp_data[0]);
    int protocol_type = data2value16(&arp_data[2]);
    int hardware_size = data2value8(&arp_data[4]);
    int protocol_size = data2value8(&arp_data[5]);
    int opcode = data2value16(&arp_data[6]);

    printf("[!!] Hardware type : ");
    switch(hardware_type){
        case 0x0001:
            printf("Ethernet\n");
            break;
        default:
            printf("Unknown...\n");
            break;
    }

    printf("[!!] Protocol type : ");
    switch(protocol_type){
        case 0x0800:
            printf("IPv4\n");
            break;
        default:
            printf("Unknown...\n");
            break;
    }

    printf("[!!] Hardware size : 0x%x\n",hardware_size);
    printf("[!!] Protocol size : 0x%x\n",protocol_size);
    printf("[!!] Opcode : 0x%04x\n",opcode);
    switch(opcode){
        case 0x0001:
            printf("[=>] Request : Who has ");
            displayIP(&arp_data[24]);
            printf("? Tell ");
            displayIP(&arp_data[14]);
            printf("\n");
            break;
        case 0x0002:
            printf("[=>] Response : ");
            displayIP(&arp_data[24]);
            printf(" is at ");
            displayMAC(&arp_data[8]);
            printf("\n");
            break;
        default:
            printf("[=>] Opcode Unknown...\n");
            printf("[!!] Sender MAC Address : ");
            displayMAC(&arp_data[8]);
            printf("\n");
            printf("[!!] Sender IP Address : ");
            displayIP(&arp_data[14]);
            printf("\n");
            printf("[!!] Target MAC Address : ");
            displayMAC(&arp_data[18]);
            printf("\n");
            printf("[!!] Target IP Address : ");
            displayIP(&arp_data[24]);
            printf("\n");
            break;
    }

    printf("|============= END DATA =============|\n");
    return 0;   
}

ARPの中身を見ようとするだけでかなり長いコードになってしまった。

ARPの中身は以下のようになっており、重要なのはOpcodeSender MAC address,Sender IP address,Target MAC address,Target IP addressであると考えられる。

(※横並びに繋がっているものとして見てください)
| Hardware type (2byte) | Protocol type (2byte) |
| Hardware size (1byte) | Protocol size (1byte) |
| Opcode (2byte) |
| Sender MAC address (6byte) | Sender IP address (4byte) |
| Target MAC address (6byte) | Sender IP address(4byte) |

ARPは、IPアドレスに対応するMACアドレスを取得するプロトコルで、 Opcodeの部分でその要求と応答とを区別している。

Address Resolution Protocol - Wikipedia

Opcodeが0x0001のときはRequest(要求)となり、Target MAC addressが全部0埋めでデータが送信される。 Opcodeが0x0002のときはReply(応答)となり、Requestで要求されていたTarget MAC addressに該当するアドレスが`Sender MAC addressの領域に格納されてデータが送信されることになる。

それらをWiresharkの表示のように、Request時にはWho has XX.XX.XX.XX? Tell XX.XX.XX.XXと表示し、Reply時にも同様にXX.XX.XX.XX is at XX:XX:XX:XX:XX:XXと表示するようにしたのが上記コードである。

先人から学ぶ

前回と同じく、tcpdumpリポジトリ内にあるprint-arp.cというファイルから参考にできないか探していたところ、以下のようなコードがあった。

github.com

#define ARPOP_REVREQUEST 3      /* request protocol address given hardware */
#define ARPOP_REVREPLY 4 /* response giving protocol address */

ARPOpcodeに該当する部分が、3,4という数値でもOKらしい。なんとなくRARPっぽい感じがするので念ののために調べてみると…

d.hatena.ne.jp

このサイトで3,4がRARPのRequestとReplyに該当するOpcodeになるということが分かった。

他にも、各領域のサイズをチェックしているような処理が見受けられた。

else if (PROTO_LEN(ap) != 4)
    ND_PRINT("<wrong len>");

tcpdumpでは構造体を使って各データを表現している。しかし、私のプログラムはただ表示するだけで構造体等は意識していない。 効率的にデータを扱うのなら構造体を使うべきなのだと思う。そして、その場合はしっかりとサイズを確認しなければ、オーバーフローなどの脆弱性を生んでしまうのだと感じた。

感想

コードが増えると、その管理も煩雑になってくるので、ソースコードを複数のファイルに分割したり、重複処理を関数化したりなど、きれいにしなければならないとも感じた。 加えて、セキュリティを勉強しているのにもかかわらず、サイズのチェック処理などを施していないのが良くないなと思った。未然に防ぐ立場も簡単ではないと思う。