IoT Security 10 · フォールトインジェクション攻撃 — if 文が飛ぶ

Chapter 10

フォールトインジェクション攻撃 — if 文が飛ぶ

この章がなぜ必要なのか——「保護機能そのもの」が狙われるから.

セキュアブート、読み出し保護、デバッグ無効化、鍵の権限チェック—— これらはすべて、最終的にはプログラム上の分岐に落ちる。

if (verify() != OK) halt();

この 1 行を飛ばせたら、全部が無効になる。

そして、それは数万円の機材でできる。

この章で使う既出の用語(定義は各リンク先). 数百万〜数千万円(01 章 2 節)、セキュアブート(02 章 3 節)、不可能(04 章 3 節)、署名検証(04 章 4 節)、完全性(06 章 6 節)、サイドチャネル(07 章 8 節)、フォールト(07 章 8 節)、AES(08 章 8 節)、マスキング(08 章 6 節)、乱数(08 章 5 節)、処理時間(09 章 1 節)、必要(09 章 6 節)

1. 原理

電圧グリッチが命令を飛ばす仕組みVDD数十〜数百 nsグリッチ3.3 VCLK命令LDRCMP署名を比較B.NE fail不一致なら停止NOP実行されないBLX appアプリへ跳ぶ「署名が一致しなかったのに、一致したときと同じ道を通る」——判定そのものを壊すのではなく、判定の結果を使う命令を消す
電圧を一瞬だけ落とすと、その瞬間にフェッチされた命令が実行されない。狙うのは暗号ではなく、その直後の if である

CPU は、電源電圧・クロック・温度が仕様の範囲内にあることを前提に動く。 その前提を一瞬だけ壊すと、次のようなことが起きる。

現象内容
命令スキップフェッチした命令が実行されない、または NOP になる
データ破損レジスタやメモリの読み書きでビットが化ける
分岐の反転条件分岐の判定が逆になる
ループカウンタの改変ループが早く終わる/終わらない
アドレスの化けジャンプ先が変わる

攻撃手法

攻撃手法の位置づけ — 費用と空間分解能費用 →空間分解能 →数千円数万円数十〜数百万円数千万円低高温度・電源の酷使電圧グリッチクロックグリッチEMFI(電磁パルス)ボディバイアス注入レーザー(LFI)ここが問題——「愛好家でも届く」領域
怖いのは高価な手法ではない。数万円の電圧グリッチが公開ツールで再現できてしまうこと、つまり 02 章のクラス 2 が実行できることが問題になる
手法やること費用の目安精度
電圧グリッチ電源電圧を数十〜数百 ns だけ落とす/上げる数万円中
クロックグリッチクロックに短いパルスを挿入する数万円中〜高(外部クロックが必要)
電磁パルス (EMFI)近接コイルで強い磁界パルスを与える数十万〜数百万円高(空間分解能あり)
レーザー (LFI)チップを開封し、レーザーを照射する数百万〜数千万円極めて高い(1 ビット単位)
温度・電源の極端な操作動作範囲外にする数千円低
ボディバイアス注入基板側から電位を注入する数百万円高

電圧グリッチのハードルの低さが問題である.

ChipWhisperer などのオープンソースツールと数万円のハードウェアで、 誰でも実験できる。手法は公開され、チュートリアルも豊富にある。

つまり 02 章のクラス 2(愛好家)でも実行可能な攻撃になっている。 「専門ラボが必要」という時代ではない。

グリッチによる分岐スキップを見る

グリッチのタイミングと幅を変えて、成功する条件と対策の効果を確かめられる。

2. 何が狙われるか

標的 1: セキュアブートの検証

if (verify_signature(image) != OK) {
    halt();                      /* ← ここに来なければ通る */
}
jump_to(image);

攻撃者は署名検証の直後を狙ってグリッチを打つ。 halt() への分岐が飛べば、不正なファームウェアが起動する。

標的 2: 読み出し保護の判定

/* ブート ROM の中(イメージ) */
if (read_protection_level() > 0) {
    disable_debug_port();        /* ← ここを飛ばせたら? */
}

飛ばせたら、デバッグポートが開いたまま起動する。 SWD を繋いでフラッシュを吸い出せる。

これは理論上の話ではない.

複数のベンダの MCU で、電圧グリッチによる読み出し保護のバイパスが 公開実証されている。

代表的なものとして、あるベンダの MCU では APPROTECT(アクセスポート保護)が単一のレジスタ判定に依存しており、 グリッチで解除できることが 2020 年に公表された。 同社はその後、保護の判定をハードウェアで冗長化した改良版を出している。

「保護機能がある」だけでは足りない。「フォールト耐性があるか」を確認すること。

標的 3: PIN / パスワードの比較

if (memcmp(input_pin, stored_pin, 4) == 0) {
    unlock();
}

比較の結果を保持するレジスタをグリッチで書き換えれば、通る。

標的 4: ループカウンタ

for (i = 0; i < 32; i++) {
    key[i] = read_key_byte(i);
}

ループ回数を減らせば、鍵の一部が初期値(0 など)のままになる。 探索空間が劇的に小さくなる。

標的 5: 暗号演算そのもの(DFA)

差分故障解析 (Differential Fault Analysis) は、 暗号演算の途中に故障を注入し、正常な暗号文と故障暗号文の差分から鍵を求める。

暗号必要な故障暗号文
AES-128理論上、1〜2 個(最終ラウンド近傍への 1 バイト故障)
RSA-CRT1 個(有名な Bellcore 攻撃。CRT の片側だけ壊すと素因数が求まる)
ECDSA数個

RSA-CRT の Bellcore 攻撃は特に有名である.

RSA の署名生成を中国剰余定理(CRT)で高速化する実装で、 \(p\) 側か \(q\) 側の片方だけを故障させると、 正しい署名 \(S\) と誤った署名 \(S'\) から

\[ \gcd(S - S', N) = p \]

として素因数が 1 回で求まる。鍵が完全に破られる。

対策は単純: 署名を作ったら、その場で検証してから出力する。 (\(S^e \bmod N\) が元のメッセージに戻るか確認する。) これは多くのライブラリで既に実装されている。

3. 対策 — ソフトウェア側

対策 1: 冗長な検証

冗長な検証が効く理由単純な 1 回判定verify(sig) の結果を判定if (ok) { boot(); }boot()1 発1 回のグリッチが当たれば突破される成功率 数 % でも、何万回でも試せる冗長化 + ランダム遅延1 回目の判定(!= 0xA5A5 なら停止)ランダム遅延(0〜数百 µs)2 回目の判定(別の変数・別の場所)boot()2 発とも当てる必要成功率が p なら p²、しかもタイミングが揺れる
グリッチ対策の本質は「1 回の成功では足りなくする」こと。判定を 2 回に分け、間にランダム遅延を挟むだけで成功確率は劇的に下がる
/* 悪い: 単一の分岐に全部を賭けている */
if (verify(img) != OK) halt();
jump_to(img);

/* 良い: 二重チェック + 冗長な値 + デフォルト失敗 */
#define OK_MAGIC    0xA5C36E9Bu       /* ハミング距離の大きい値 */
#define FAIL_MAGIC  0x5A3C91B6u

volatile uint32_t r1 = FAIL_MAGIC, r2 = FAIL_MAGIC;

r1 = verify(img);
random_delay();
r2 = verify(img);                     /* 2 回検証する */

if (r1 != OK_MAGIC)      halt();
if (r2 != OK_MAGIC)      halt();
if (r1 != r2)            halt();
if ((r1 ^ r2) != 0)      halt();      /* 冗長な確認 */
if (r1 + r2 != 2 * OK_MAGIC) halt();  /* さらに冗長 */

jump_to(img);
技法効果
デフォルトを失敗にする初期化を飛ばされても安全側に落ちる
成功値に特殊なビットパターン単一ビット反転では成功値にならない
同じ検証を 2 回行う2 回とも同じタイミングで飛ばすのは難しい
ランダム遅延を挟むタイミングを狙いにくくする
冗長な条件を並べるすべてを飛ばす必要がある

対策 2: 制御フロー完全性(CFI)

「本来通るはずの経路を通ったか」を検証する。

/* 各関門でカウンタを進める */
volatile uint32_t flow_counter = 0;

void secure_boot(void)
{
    flow_counter = 0;

    if (check_header(img) != OK) halt();
    flow_counter += 0x11;

    if (check_key_hash(img) != OK) halt();
    flow_counter += 0x22;

    if (check_signature(img) != OK) halt();
    flow_counter += 0x44;

    if (check_svn(img) != OK) halt();
    flow_counter += 0x88;

    /* ★ すべての関門を通ったことを確認する */
    if (flow_counter != (0x11 + 0x22 + 0x44 + 0x88)) halt();

    jump_to(img);
}

関門を 1 つ飛ばすと、カウンタの値が合わなくなる。

対策 3: 秘密の二重化

/* 鍵を 2 通りの形で保持し、使う前に照合する */
uint8_t key[32];
uint8_t key_complement[32];      /* ビット反転版 */

load_key(key, key_complement);

for (int i = 0; i < 32; i++) {
    if ((key[i] ^ key_complement[i]) != 0xFF) halt();   /* 破損を検出 */
}

対策 4: 暗号演算の結果検証

/* RSA 署名を作ったら、その場で検証する(Bellcore 対策) */
rsa_sign(msg, sig);
if (rsa_verify(msg, sig) != OK) {
    /* 故障が起きた。署名を出力してはいけない */
    zeroize(sig);
    halt();
}
output(sig);
/* AES も、暗号化 → 復号 → 一致確認(コストは 2 倍になる) */
aes_encrypt(pt, ct);
aes_decrypt(ct, check);
if (memcmp_ct(pt, check, 16) != 0) halt();

どこまでやるかはコストとのトレードオフである.

全部の暗号演算を二重化すると性能が半分になる。

原則: 「一度きりで、取り返しがつかない処理」を重点的に守る。

4. 対策 — ハードウェア側

ハードウェア側の防御 — 検出して即座に止めるダイCPU コア ACPU コア Bロックステップ照合SRAM / Flash — ECC・パリティアクティブシールド(配線が切れたら検出)電圧モニタ範囲外で即リセットクロックモニタ異常なパルスを検出温度センサ動作範囲外を検出光センサ開封を検出タンパー検出検出したら鍵を消去試行回数の制限繰り返しを許さない汎用 MCU のブラウンアウト検出は「電源異常」への対策であって、攻撃への対策ではない数十 ns のグリッチはブラウンアウト検出の応答より速い。ここが MCU とセキュアエレメントの差が最も出る領域になる
攻撃を防ぐのではなく、攻撃の兆候(電圧・クロック・温度・光の異常)を検出して止める。専用回路の有無は選定時にデータシートで確認する

シリコンレベルの対策は、ベンダが実装するものである。選定時に確認する。

対策内容
電圧モニタ電源電圧が範囲外になったら検出してリセット
クロックモニタクロック周波数の異常を検出(グリッチ検出)
温度センサ動作範囲外の温度を検出
光センサ開封+レーザー照射を検出(パッケージが開いたら光が入る)
アクティブシールドチップ表面に配線を張り、断線したら検出(プロービング対策)
メモリの ECC / パリティビット反転を検出・訂正
冗長な CPU(ロックステップ)2 つの CPU が同じ命令を実行し、結果を照合
タンパー検出検出時に鍵を即座に消去する
ベンダ・製品特徴
Arm Cortex-M35P物理セキュリティ機能(パリティ、アンチタンパリング)を持つ M33 派生
Silicon Labs Secure Vault Highアンチタンパ、DPA/DFA 対策
セキュアエレメント全般センサ群 + シールド + 冗長設計(22 章)
スマートカード IC最も対策が厚い。CC AVA_VAN.5

MCU とセキュアエレメントの差が最も出るのが、この分野である.

汎用 MCU は「電圧が下がったらリセットする」程度のブラウンアウト検出しか持たないことが多い。 これは電源異常への対策であって、攻撃への対策ではない。

数十ナノ秒のグリッチは、ブラウンアウト検出の応答より速い。

セキュアエレメントは、グリッチを検出するための専用回路を持ち、 検出したら即座に鍵を消去する。設計思想が違う。

5. 攻撃の実際のフロー

攻撃の実際のフローと、それぞれを潰す対策① 標的の特定どの命令を飛ばすか② トリガー確立打つ基準を見つける③ パラメータ探索数千〜数万回試す④ 成功の検出外れたと分かるか⑤ 再現性の確保数 % でも実用FW を読ませない06・11 章ランダム遅延基準を揺らす試行回数の制限異常検出でロック応答を一様にする失敗の見分けを消す冗長チェック1 発では足りなくする最も効くのは実は ① ——どこを狙えばよいか分からなければ、②〜⑤ に進めない
攻撃者の作業を段階に分けると、対策の効き所が見える。ただしブート ROM は同じチップを買えば誰でも解析できるので、① の防御は ROM を狙う攻撃には効かない

攻撃者の作業を知っておくと、対策の効き所が分かる。

段階やることここを潰す対策
1. 標的の特定どの命令を飛ばしたいかを決める(ファームウェア解析)ファームウェアを読ませない(06 章・11 章)
2. トリガーの確立グリッチを打つタイミングの基準を見つける(GPIO、通信、電源波形)ランダム遅延でタイミングを揺らす
3. パラメータ探索電圧・幅・遅延を総当たりで試す(数千〜数万回)試行回数の制限、異常検出でロック
4. 成功の検出「保護が外れた」ことをどう知るか失敗時の応答を一様にする
5. 再現性の確保成功率を上げる(数 % でも実用になる)冗長チェックで成功確率を下げる

段階 1 が実は最大の防御点である.

ファームウェアを読めなければ、どこを狙えばよいか分からない。 「署名検証の後の分岐」がどのアドレスにあるかを知るには、 まずファームウェアを入手して解析する必要がある。

だから 11 章(ファームウェア抽出の防止)が、 フォールト攻撃への間接的だが強力な対策になる。

ただし——ブート ROM は多くのベンダで解析されている(同じチップを買えば誰でも読める)。 ROM を狙う攻撃には、この防御は効かない。

6. 「グリッチ耐性」を評価する

SoC を選定するとき、次を確認する。

確認項目どこを見るか
物理攻撃耐性の第三者評価PSA Certified Level 3、SESIP 4 以上、CC AVA_VAN.4 以上
保護機能の冗長性セキュリティアプリケーションノート。「フォールト耐性」の記述
公知の攻撃事例学会論文、CVE、セキュリティ研究者の公開資料
シリコンリビジョン過去に破られた品種は、改版で対策されていることがある
ハードウェア検出機構電圧/クロック/温度/光センサの有無

公知の攻撃事例を調べることを強く推奨する.

MCU のグリッチ攻撃は、セキュリティカンファレンス (CHES、Black Hat、DEF CON、hardwear.io など)で継続的に発表されている。

「候補チップ名 + glitch」「候補チップ名 + fault injection」で検索して、 公開された攻撃事例があるかを確認するのは、選定時の重要な作業である。

事例があること自体は必ずしも失格ではない(対策版が出ていることも多い)。 重要なのは、ベンダが対応したかどうかである。

7. サイドチャネルとの関係

サイドチャネル(09 章)フォールト(本章)
性格受動的(観測するだけ)能動的(介入する)
検出ほぼ不可能検出できる(センサで)
標的主に暗号演算保護の判定ロジック全般
必要な試行数百〜数百万回の正常な実行数千〜数万回の故障を起こす試行
主な対策マスキング、定時間実装冗長化、CFI、センサ

組み合わせると強力になる.

DPA 対策のマスキングを、フォールトで無効化するという複合攻撃がある。 マスク生成の乱数を 0 に固定できれば、マスキングは効かなくなる。

だから高セキュリティ実装では、 「乱数が本当にランダムか」を検証してから使うといった対策も入れる。

対策どうしが干渉することもある。 たとえば「エラー時に即座に停止する」フォールト対策が、 「エラーの有無で処理時間が変わる」タイミング攻撃の窓口になる。 両方を考慮した設計が必要で、これが専門性を要する理由である。

8. この章のまとめ

ポイント内容
原理電圧・クロック・温度を一瞬崩す → 命令スキップ、データ化け、分岐反転
ハードル電圧グリッチは数万円。クラス 2(愛好家)でも実行可能
主な標的セキュアブートの検証、読み出し保護の判定、PIN 比較、ループ、暗号演算
実例複数ベンダの MCU で読み出し保護のグリッチ突破が公開実証されている
DFAAES は1〜2 個の故障暗号文で、RSA-CRT は1 個で破れる
RSA-CRT の対策署名を作ったら、その場で検証してから出力する
ソフト対策デフォルト失敗・二重検証・ハミング距離の大きい成功値・CFI カウンタ
どこを守るか一度きりで取り返しがつかない処理(ブート検証、LCS 遷移、デバッグ閉鎖)
ハード対策電圧/クロック/温度/光センサ、アクティブシールド、ロックステップ
最大の防御点ファームウェアを読ませない。狙う場所が分からなければ攻撃できない
選定時PSA L3 / SESIP 4 / AVA_VAN.4 以上と、公知の攻撃事例を確認する

次章では、その「ファームウェアを読ませない」を正面から扱う。