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といったファイル構造があります。
IMSIの場所はETSI TS 131 102 V17.6.0に記載されており、USIM ADF内のEF_IMSIに存在することがわかります。
USIM ADFにアクセスし、EF_IMSIを書き換えるのが今回の目標です。
実際に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から構成されており、
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