無線LAN(IEEE802.11フレーム)を見てみよう

(2022.11.23修正) 問題３での、Apple製デバイスに匿名IDを設定する方法に関する記述を修正しました。

<img src="https://3.bp.blogspot.com/-M1fnP-Dt1O8/UnXnVo1oQ6I/AAAAAAAAaNg/B2j3SeMv08s/s400/kaden_wifi_router.png" width="15%" alt="Wi-Fi illust">

<img src="https://www.wireshark.org/assets/theme-2015/images/wireshark_logo@2x.png" width="30%" alt="wireshark">

定番のオープンソースパケットキャプチャソフト「Wireshark」で無線LANの通信(802.11フレーム)を見てみました。
イーサネットフレームのパケットキャプチャとは異なり、同じチャンネルの全ての通信を受信するためキャプチャ量が膨大になりますが、深いレイヤーの暗号化方式を見ることができて面白いです。

# 準備

以下を前提として進めます。

- macOSもしくはLinuxOS（Windowsは訳あり:[後述](#windows)）
- モニタモードに対応した無線LANカード（大抵は内蔵無線LANでOK）
- Wiresharkインストール済み
- 対象となる無線LANのチャンネル、帯域幅がわかっている。


## モニタモードの設定方法


802.11フレームを受信して表示するには、モニタ（スニファ）モードにする必要があります。
OSごとに設定方法が違います。以下をクリックすると展開します。

<details>
  <summary><font color="#59bb0c">Linux (Debian系)</font></summary>

### Linux (Debian系)

内蔵無線LAN (Intel Dual Band Wireless-AC 7260) にて Ubuntu20.04 で動作確認しました。

必須ではないですが `ip a` コマンド無線LANのデバイス名の確認と、 `iw list` でmonitorモードが利用可能か、利用可能なチャンネル、帯域幅などを確認するとトラブルになった時に解決が楽です。
特にチャンネル144に対応しているか、80MHzに対応しているか(802.11ac以降か)を確認すると良いと思います。
　
無線LANのデバイス名は`wlp2s0`として、以下のコマンドでモニターモードに変更します。

```bash
sudo systemctl NetworkManager stop
sudo ip link set wlp2s0 down
sudo iw wlp2s0 set monitor control
sudo ip link set wlp2s0 up
sudo iw wlp2s0 set channel 144 HT20
iw wlp2s0 info
```
- 1行目でネットワークマネージャーを停止し、システムによる意図しないモードの書き換えを防止しています。
- 2行目から4行目で動作モードを`monitor`に設定します。
- 5行目で受信するチャンネルと帯域幅を設定します。帯域幅が40MHzの場合、サブチャンネルがメインチャンネルより上にある場合`HT40+`、下の場合`HT40-`と入力する必要があります。その他、80MHzや160MHzなどは`iw`コマンドのヘルプを参照してください。
- 6行目で動作モードを確認し、`type monitor`の表記を確認し、`channel`に正しいチャンネルと帯域幅が表示されていればOKです。

この間、無線LANの通信および有線LANでの通信はできません。

Wiresharkによるパケットキャプチャが終了したら、以下のコマンドで元に戻します。

```bash
sudo ip link set wlp2s0 down
sudo iw wlp2s0 set type managed
sudo ip link set wlp2s0 up
sudo systemctl NetworkManager start
```

</details>

<details>
  <summary><font color="#59bb0c">macOS</font></summary>

### mcaOS

MacbookAir(M1)内蔵無線LANにて macOS 13 で動作確認しました。
オプション（⌥)キーを押しながらメニューバーのWi-Fiアイコンをクリックし、「ワイヤレス診断を開く」をクリックします。

<!--![mac-1-out.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/fca899db-fd32-360f-cf54-42bcaf7cea57.png)-->

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/fca899db-fd32-360f-cf54-42bcaf7cea57.png" width="30%">

メニューバーの「ウインドウ」-「スニファ」をクリックします。
表示されたウインドウでチャンネルと帯域幅を指定し、「開始」をクリックします。

<!--![mac-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/be9e2218-0954-de54-a530-347f4c7f89a2.png)-->

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/be9e2218-0954-de54-a530-347f4c7f89a2.png" width="70%">


Wiresharkによるパケットキャプチャが終了したら、「中止」ボタンを押し、元に戻します。

</details>


<details>
  <summary><font color="#59bb0c">Windows（できませんでした）</font></summary>

### Windows（できませんでした）

Windows版のWiresharkとともにインストールされるNpcapで "Support raw 802.11 traffic" にチェックをつけてインストールすると802.11フレームが見えるそうですが、残念ながら私の環境ではWindowsで802.11フレームを見ることはできませんでした。

内蔵無線LAN (Intel Advanced-N 6205) 、Windows10 22H2、Wireshark 4.0.1、Npcap 1.60の組み合わせで動作させましたが、Wireshark上にモニターモードのチェックボックスは表示され、チェックをつけた状態でパケットキャプチャを開始できますが、何も表示されませんでした。

なお、Npcap 1.71 では`WlanHelper.exe Wi-Fi mode`で動作モードを確認するも「to use this function, you need to check the "Support raw 802.11 traffic" option when installing Npcap」と表示され、門前払いされてしまいました。（もちろん"Support raw 802.11 traffic"のチェックをつけてインストール済み）
Npcap 1.60をインストールし直すと該当コマンドを実行すると"Success"と表示されますが、何もキャプチャできないのに変わりはありませんでした。

どうしてもという場合、VirtualBox上でLinuxを動作させ、monitorモードで動作するUSB Wi-Fi アダプタをマウントすれば出来るそうです。インターネット上に記事がいくつかあるので、参考にすると良いです。

</details>

## パケットキャプチャ開始

Wiresharkを起動します。
Linuxの場合、管理者権限でWiresharkを起動します。（ターミナルで`sudo wireshark &`を実行）

上部の「キャプチャ」タブ-「オプション」をクリックし、Wi-Fiのインターフェースを見つけます。
「モニターモード」のチャックボックスを付けます。見切れている場合、右にするロールしましょう。

<!--![wireshark.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/a426b371-ea57-6f28-ab12-11111ac983f0.png)-->

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/a426b371-ea57-6f28-ab12-11111ac983f0.png" width="70%">


「開始」をクリックするとパケットキャプチャが始まります。

個人的に、開始前もしくは開始後設定で「オプション」タブ-「MACアドレス解決」はオフにすることをおすすめします。
理由としては単に表示画面でアドレス幅が一定にならず見づらいのと、送信端末によってはMACアドレスのランダム化などが行われていて、アドレス解決が無意味になっている場合が多いことなどがあります。

## 色分けルールの追加

WiresharkにはデフォルトでTCP/IPモデルで言う所のL2以降は色分けされて表示されますが、今回キャプチャしたのはL1に当たり、色分けされずに表示されます。
そこで、色分けルールを追加して見やすくしましょう。

Wireshrak公式の色分けルールは見当たらなかったので、[Cisco communityで提供されている色分けルールファイル](https://community.cisco.com/t5/wireless-mobility-knowledge-base/download-wireshark-coloring-rules-file/ta-p/3108000)をインポートします。

該当スレットのうち、最初のメッセージ下部にあるzipファイルをダウンロードし、解凍します。
Wiresharkメニューバーの「表示」-「色付けルール」で表示されたウインドウで、下部の「インポート」をクリックして解凍されたファイルを選択すると、色付けルールが追加されます。

<!--![スクリーンショット 2022-11-22 13.17.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/a3afbecc-14e3-9059-809a-2fe7f7b78b72.png)-->
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/a3afbecc-14e3-9059-809a-2fe7f7b78b72.png" width="90%">

## フィルタを適用する

本題の前に、大量のフレームの中から目的のフレームを探し出すのは疲れるので、フィルタを適用して目的のフレームのみを表示します。

例えば目的の端末のMACアドレスが`00:00:aa:00:00:bb`の場合、以下のフィルタ文をWireshark上部のフィルタツールバーに入力すると、該当端末発および該当端末向けのフレームのみ表示することができます。

```
wlan.sa == 00:00:aa:00:00:bb or wlan.da == 00:00:aa:00:00:bb
```

# 実際に見てみよう

<font color='red'>以下ポエムです。</font>

せっかくなので、~~母校の校内無線LAN~~ をキャプチャしてみました。

いくつか気になるところがあったので、5つ記したいと思います。

...と思いましたが、電波法第５９条に抵触してしまう可能性があるので、以降は私の空想という建前でお願いします。

>何人も法律に別段の定めがある場合を除くほか、特定の相手方に対して行われる無線通信を傍受してその存在若しくは内容を漏らし、又はこれを窃用してはならない。


こちらがプローブ要求から接続完了までのフレーム一覧です。WPA2エンタープライズ/EAP-PEAPで接続しています。
白枠は私が付け足したコメントです。送信元と送信先のMACアドレスは非表示にしています。
<font color=red>（追記）コメント１行目のID送信方向は間違いで、(ST→AP)が正しいです。</font>

![fig2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/81bc9662-6f46-8e68-0175-f8a87629a111.png)



## 問題点１：SSIDがステルス

通常であればアクセスポイントからSSIDを含んだビーコンを送信し、端末にSSIDが表示されます。
SSIDがステルスの場合、ビーコンは送信されず、端末から接続したいSSIDを含んだプローブ要求を送信し、そのSSIDに該当するアクセスポイントはプローブ要求を返します。

ユーザはSSIDを知らなければアクセスポイントに接続できないため、一見セキュリティ上利点があるように見えますが、実際はその逆で、端末は該当するアクセスポイントが周囲にない場合でもSSIDを含んだプローブ要求を送信し続けます。
先の画像の先頭フレームがSSIDが記載されたプローブ要求ですが、これを全チャンネル(1~13ch,36~144ch)で定期的にばら撒いています。
これにより、アクセスポイントのなりすましに遭う可能性がゼロとは言えなくなります。
また、プローブ要求のSSIDに学校名などが含まれていると個人が特定できる情報を常に発信し続けていることになるので、あまりいい気分ではありません。

以上の理由から、SSIDステルスは非推奨とする場合が多いです。
例えばAndroid 9以降ではステルスSSIDへの接続はデフォルトで無効になっていて、Wi-Fi設定時に「非公開ネットワーク」をONに設定することで接続できるようになります。
また、iOSでは以下の注意文が表示されます。

>非公開ネットワークを使用すると、個人を特定できる情報が外部にもれる可能性があります。
これが自分のWi-Fiネットワークの場合は、このネットワークをブロードキャストするように設定してください。

## 問題点２：使用できない認証方法での接続を求めてくる

先の画像で日本語コメントをつけたフレームを見てみましょう。
STA(端末)がAP(アクセスポイント)にIDをレスポンスした後、STAはAPにTLS EAP認証を要求しています。しかし、STAにはPEAP EAPを設定しているのでNegativeACKを送信しています。試しにEAP-TLSを設定して接続しようとしましたが、認証に失敗して接続できませんでした。
どの認証方式でも最初はEAP-TLSを要求するようにIEEE802.1X/EAPプロトコルで規定されているのか、それとも単なるアクセスポイントの設定ミスか。調査不足で申し訳ないですが、いずれにせよ使用できない認証方法での接続を求めているのは、すっきりしませんね。

## 問題点３：Identity(ユーザ名)を平文で応答している

<!--厳密には校内向けの設定マニュアルに平文で応答するように記載されていることが問題なのですが、-->

以下の画像は EAP-PEAP 認証ワークフローです。
ワークフローは大きく２つに分けることができます。右側の縦矢印で示す“Phase1”と“Phase2”の２つです。

![PEAP-sequence-flow](https://www.cisco.com/c/dam/en/us/td/i/400001-500000/420001-430000/425001-426000/425937.tif/_jcr_content/renditions/425937.jpg "PEAP-sequence-flow")
画像:[Ciscoテクニカルリファレンス](https://www.cisco.com/c/ja_jp/td/docs/wireless/controller/technotes/8-7/b_802_1x_eap_supplicant_on_cos_ap.html)

“Phase1”が終了するまで平文で送信されることに注意して見ると、上から３つ目のシーケンスでIDを平文で送信していることがわかります。以下はその部分のキャプチャフレームです。

![fig1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/dfde82d1-4429-5762-0a28-48c318c1d511.png)


ここで「hogehoge」が平文で送信されたIDですが、実はここのIDが何であれ、認証には影響がありませんでした。
というのも、TLSトンネル確立後のPhase2でもう一度IDを送信していて(下から７つ目のシーケンス)、そこで認証を行っているようです。
なぜ２回送信が必要なのか私は調査できませんでしたが、１回目に送信されたIDによって暗号化/認証方式を変える方式がオプションとしてあり、そのためにやむを得ず平文でIDを送信しているのかもしれません。

いずれにせよ平文でIDを送信するのはセキュリティ上好ましくありません。
WindowsやAndroid、Linuxでは認証に使うIDの他に「匿名ID」という設定項目があり、平文で送信するIDとTLS内で送信するIDを分けることができます。上の例はAndroidで行いました。
iOS, iPadOS, macOSではGUIで設定する方法が見つけることができませんでしたが、[設定プロファイル](https://docs.blackberry.com/ja/endpoint-management/blackberry-uem/12_11/administration/network-connections/wifi-profile/ake1373051306495/awi1405517180540)に「TTLS、PEAP、およびEAP-FASTの外部ID」を設定するすることで実現できるそうです。

<!--![Screenshot_20221122-152703_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/ea8b2533-c566-9785-c022-8a377c3ba2cb.png)-->

Andoridの例
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/ea8b2533-c566-9785-c022-8a377c3ba2cb.png" width="40%">

WindowsではWi-Fi設定時に自動的に「anonymous」に設定し、変更しないことを推奨しています。

<!--![スクリーンショット 2022-11-23 094730.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/429ee768-cefd-2a16-0457-a5fc3c2ac467.jpeg)-->
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/429ee768-cefd-2a16-0457-a5fc3c2ac467.jpeg" width="40%">

コントロールパネルで後で変更することもできます。

![スクリーンショット 2022-11-23 084856.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/efd9e081-9d47-bd0e-3416-f9844a2c1229.png)




## 問題点4：最新のLinuxOSで接続できない

厳密にはOpenSSL 3.0以降が標準インストールされているLinuxOS(Ubuntuは22.04以降)では[MD4アルゴリズムを有効にしないと](https://sehermitage.web.fc2.com/devel/openssl_v30.html)接続できないです。
OpenSSL 3.0以降ではMD4アルゴリズムはデフォルトで無効にされています。
macOSはLibreSSLを利用していて、LibreSSLはMD4はデフォルトで有効です。

Phase2認証でMSCHAPv2プロトコルを利用していますが、そこでのチャレンジ認証でMD4ハッシュを利用しています。
MD4はずいぶん前にハッシュ衝突（強衝突耐性を破る）の脆弱性(https://en.wikipedia.org/wiki/MD4#:~:text=MD4%20collision%20example%5Bedit%5D)が指摘されていて、使用は避けるべきだそうです。
もっとも、「問題点3」の図より、MD4ハッシュはTLSで暗号化されて送信されるので、TLSに脆弱性が生じない限りセキュリティを気にする必要はないと思います。

## 問題点5：Windows 11で接続できないことがある

初回の認証で何らかの原因でEAPによる認証が失敗すると、以下の画像のようにEAP-PEAPシーケンスを始める前のAssociation Responseで門前払いされてしまいます。

![スクリーンショット 2022-11-23 9.59.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448172/4e857dac-3556-5d6a-c566-eb0ae3dda4ed.png)


特にWindowsはID/パスワードの入力をAPに接続試行中にユーザに求めてくるの（他のOSは接続試行前=SSIDや暗号化方式設定時に求めてくる）で、そこでID/パスワードを誤るかAP/サーバ側のタイムアウト設定値を超えてしまうと認証に失敗し、しばらく接続できなくなりました。この時、WindowsはID/パスワードの入力を複数回求めてきて、結局「接続できません」と表示されます。

---
おわり
不正確な点、ご指摘、ご質問がありましたらコメントお願いします。