見出し画像

SIMアプレットから通信を遮断したい!

お久しぶりです。
コモン・クリエーション株式会社でCTOをしている @mohemohe です。

前回のnoteから9ヶ月も経っていました。

今後もこのような感じで技術的な話を発信して行く予定です。

とか書いたのにめっちゃ飛んでいます。どうして。

今回は、最近弊社内でもホットなSIMアプレットの話です。
応用するとSIMスワッピング対策にもなるかもしれません。

IoTとSIM

IoT機器で通信を行うには、Wi-Fiを使う方法や、ZigBeeを使う方法、携帯回線を使う方法などが考えられます。

携帯回線を使う方法では、定額プランのSIMと、従量課金プランのSIMがありますが、IoTは通信量が少ないことから、従量課金のSIMの方がコストで有利なケースがあります。

従量課金の場合は、SIMが盗難に遭った場合に大量に通信され、予期せぬ請求が発生する可能性があるため、対策が必要です。

端末の通信をSIMから止める

携帯端末がキャリア ネットワークに接続するには、IMSI, Ki, OPcの組み合わせが利用されており、それぞれ以下のような役割があります。

  • IMSI: 加入者の識別番号・ネットワーク内でのアクセス制御の元

  • Ki: SIM に割り当てられた一意の認証キー

  • OPc: SIM に割り当てられた一意の認証キー

3つの組み合わせのどれかが異なっていると、SIMとキャリア ネットワークの間の認証が失敗し、通信が遮断されます。

つまり、SIMアプレットからIMSI, Ki, OPcを書き換えれば、SIMから端末の通信を遮断できることになります。

KiとOPcは認証に使われているため、書き換えるのは難しいですが、IMSIの書き換えは比較的簡単です。

IMSIを書き換えるには

SIMの中にはMF, DF, EFといったファイル構造があります。

ETSI TS 151 011 V4.14.0より引用

IMSIの場所はETSI TS 131 102 V17.6.0に記載されており、USIM ADF内のEF_IMSIに存在することがわかります。

USIM ADFにアクセスし、EF_IMSIを書き換えるのが今回の目標です。

ETSI TS 131 102 V17.6.0より引用

実際にIMSIを書き換える

今回はNTTコミュニケーションズ株式会社より提供されている、IoT Connect Mobile Type SのSIMで試してみます。

このSIMのUSIM ADFの情報はSDPFのサンプルにある通り、以下の値です。

A0000000871002FFFFFFFF8903020000

USIM ADFに接続するには、 uicc.access.FileView インスタンスの作成時にこの値を指定します。

byte[] usimAID = {
        (byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x87, (byte) 0x10, (byte) 0x02, (byte) 0xFF,
        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x89, (byte) 0x03, (byte) 0x02, (byte) 0x00, (byte) 0x00};
FileView fv = UICCSystem.getTheFileView(new AID(usimAID, (short) 0, (byte) 16), JCSystem.CLEAR_ON_RESET);

IMSIのファイルIDの指定は、 uicc.usim.access.USIMConstants.EF_IMSI で定義されているので、こちらをそのまま指定できます。

書き込むIMSIの値は、認証が通らないような値にしますが、同一キャリア内の他のユーザーの値と重複しないような値にするのが安全です。

IMSIはMCC, MNC, MSINから構成されており、

総務省 IoT時代の電気通信番号を巡る現状等についてより引用
  • MCC: 441

  • MNC: 91

東京オリンピック・パラリンピック競技大会用に割り当てられたものは、今後使われるわけがないので、これを拝借して書き込みます。
(ちょっと行儀が悪いですが……)

byte[] invalidImsi = {
        (byte) 0x08, (byte) 0x49, (byte) 0x14, (byte) 0x19, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
fv.select(USIMConstants.FID_EF_IMSI);
fv.updateBinary((short) 0, buffer, (short) 0, (short) 9);

これでIMSIの書き換えは完了です。

端末のモデムを再起動する

IMSIを書き換えただけだと、接続中の回線の認証はそのままなので、通信もできてしまいます。
したがって、IMSIが変更されたことを端末に伝える必要があります。

ETSI TS 101 267の 6.4.7.1 EFIMSI changing procedure では、IMSIが変更された場合に、

  • SIM Initialization and File Change Notification. If EFIMSI is part of the file change notification, the ME shall invoke the MM Restart procedure defined in 03.22 [28].

  • SIM Initialization and Full File Change Notification. The ME shall invoke the MM Restart procedure defined in 03.22 [28].

  • SIM Reset. Normal SIM Reset procedure is carried out

の3つを行う必要があるとされています。

ただし、 SIM Initialization and File Change Notification は、 SIM Initialization and Full File Change Notification に含まれているので、実際には後者2つを行えばOKです。
この2つはライブラリで定数化されていないので、定義してから実行します。

byte USIM_INITIALIZATION_AND_FULL_FILE_CHANGE_NOTIFICATION = (byte) 0x00;
byte UICC_RESET = (byte) 0x04;

ProactiveHandler ph = ProactiveHandlerSystem.getTheHandler();
ph.init(PRO_CMD_REFRESH, USIM_INITIALIZATION_AND_FULL_FILE_CHANGE_NOTIFICATION, DEV_ID_ME);
ph.send();

ph.init(PRO_CMD_REFRESH, UICC_RESET, DEV_ID_ME);
ph.send();

ちょっと待った!

近年のキャリア ネットワークでは、IMSIでの認証後に、TMSIという値を使って認証を行っています。
TMSIは、キャリア以外の第三者がユーザーを特定したり、盗聴を行ったりするのを防ぐ目的で使用されています。
このTMSIは、大きくIMSIとLOCI(Location Information・位置情報)の2つから生成されています。
IMSIだけを書き換えても、TMSIの値がそのままだと、認証が継続する場合があります。

EF_LOCI, EF_PSLOCIの値も書き換えて、TMSIを再生成させます。
EF_LOCI, EF_PSLOCIのファイルサイズもEF_IMSIと同様にETSI TS 131 102 V17.6.0に記載されています。

fv.select(USIMConstants.FID_EF_LOCI);
fv.updateBinary((short) 0, tmpBuffer, (short) 0, (short) 11);

fv.select(USIMConstants.FID_EF_PSLOCI);
fv.updateBinary((short) 0, tmpBuffer, (short) 0, (short) 14);

できあがり

実際に通信を止めてみましょう。
iPhoneでの実行結果は以下のような感じです。
※圏外表記になるまで10秒程度かかります。

今回のアプレットはオープンソースで公開しています。
興味のある方は、手元でビルドしてお試しください。

https://github.com/common-creation/sim-applet-imsi-test

コモンクリエーション株式会社について
https://note.com/common_creation/n/n69214d7a1dc2
▶︎採用情報はこちら


【この記事を書いたのは】
mohemohe
過酷な自宅警備の傍ら、コモン・クリエーション株式会社でCTOをしている。