著者:enze

編集者:Liz

序論

最近、JavaScript エコシステムで広く使用されている elliptic 暗号ライブラリに深刻なセキュリティ脆弱性(GHSA-vjh7-7g9h-fjfh)が発見されました。攻撃者は特定の入力を構築することにより、一度の署名で秘密鍵を抽出し、被害者のデジタル資産やアイデンティティ証明を完全に掌握できるようになります。

この脆弱性の根本原因は、elliptic ライブラリによる非標準入力の処理の欠陥にあり、これにより ECDSA 署名におけるランダム数 k が重複する可能性があります。ECDSA アルゴリズムの安全性は k の一意性に極度に依存しているため、一度 k が重複すると、秘密鍵が直接導出され、取り返しのつかないセキュリティリスクを引き起こします。

この記事では、脆弱性の原理、原因、影響を分析し、修正提案を提供して、開発者がセキュリティリスクを軽減する手助けをします。

脆弱性の背景

elliptic ライブラリの紹介

elliptic は JavaScript エコシステムで広く使用されている楕円曲線暗号ライブラリで、複数の曲線(secp256k1、ed25519 など)をサポートしています。このライブラリは、多くの暗号通貨ウォレット、認証システム、Web3 アプリケーションなどで使用されています。

影響範囲

  • 影響を受けるバージョン:elliptic <= 6.6.0

  • 影響を受ける曲線:secp256k1、ed25519 など

  • 影響を受けるシナリオ:外部入力を受けて ECDSA 署名を行うすべてのアプリケーション(特に外部入力を受けて署名を行うシステム)

アプリケーションが elliptic に依存して ECDSA 署名を行い、ユーザーがフィルタリングされていないメッセージを署名内容として提供できる場合、この脆弱性の影響を受ける可能性が非常に高いです。

脆弱性の原理

ECDSA 署名におけるランダム数 k

あなたはユニークな印章(秘密鍵)を持っていると想像してください。ファイルに署名するたびに、独特な印泥(ランダム数 k)を使用する必要があります。生成された印跡は二つの数字 r と s になります。これが署名結果です。

このプロセスにはいくつかの重要なポイントがあります:

  • 印章(秘密鍵)は常に変わらない —— あなたのアイデンティティを表します。

  • 各署名には全く新しい印泥(ランダム数 k は決して再利用してはいけません)を使用する必要があります。

  • 同じファイルに署名しても、署名結果(r, s)は異なる必要があります。

  • 誰でもあなたの印跡があなたの印章によって作成されたかを検証できます(公開鍵で署名を検証)。

  • しかし、誰も印跡からあなたの印章(秘密鍵)を逆算することはできません、k が安全であれば。

k の安全性を確保するために、ECDSA は RFC 6979 規格を採用し、決定的乱数生成器(HMAC_DRBG または HKDF)を使用して k を計算します。k を生成する際には、秘密鍵 d とメッセージ m を種として使用し、同じ入力の下で k を固定します。計算プロセスは次のとおりです:

重要点:

1. k はランダムで一意でなければならず、そうでなければ秘密鍵が漏洩する可能性があります。

2. k が二回の署名で同じである場合、攻撃者は二つの署名 (r, s1) と (r, s2) を通じて秘密鍵 d を直接復元できます。

数学的導出:

ユーザーが異なる二つのメッセージ m1 と m2 に署名したと仮定しますが、ランダム数 k が偶然にも同じであった場合、署名結果は次のようになります:

攻撃者は s1 と s2 の差を計算できます:

m1、m2、s1、s2 がすべて既知であるため、攻撃者は k を解くことができます:

次に、k を元の署名方程式に代入し、秘密鍵 d を解きます:

脆弱性分析

elliptic ライブラリは、各署名の k を一意にするために HMAC_DRBG(決定的乱数生成器)を使用する必要があります。しかし、入力処理の誤りにより、特定の条件下で異なるメッセージが同じ k を生成する可能性があり、秘密鍵が漏洩する恐れがあります。

k 生成コード

elliptic ライブラリの sign() メソッドでは、k は HMAC_DRBG 擬似乱数生成器によって生成され、関連するコードは次のとおりです:

elliptic/lib/elliptic/ec/index.js

HMAC_DRBG の入力パラメーターには主に次のものが含まれます:

  • エントロピー(熵) → ここでは秘密鍵 bkey を使用しており、HMAC_DRBG が十分なランダム性を持つことを保証します。

  • nonce(ランダム数種子) → msg によって計算され、k の最終生成値に影響を与えます。

HMAC_DRBG は決定的であり、同じエントロピーと nonce は同じ k を生成します。もし nonce が異なる署名プロセスで同じであれば、k も同じになり、これが秘密鍵の漏洩につながります。

脆弱性の原因:BN 変換による nonce の再利用

elliptic の実装では、msg は BN(ビッグナンバー)に変換され、その後 nonce は msg.toArray() によって計算されます:

問題点:

  • msg は BN インスタンスですが、nonce は配列です。

  • 異なる BN インスタンスが変換後に同じ配列を生成する可能性があります。

  • これは、異なるメッセージに対して同じ nonce を生成できることを意味し、最終的に k の再利用を招きます。

もし k が異なる二つのメッセージ msg1 と msg2 で再利用されると、前述の数学的公式を用いて秘密鍵 d を復元できます。

攻撃者は、nonce が同じになるような特定の msg を構成でき、これにより k の再利用が発生します。 (r, s1) と (r, s2) の一組を得るだけで、数学的な計算を通じて秘密鍵 d を復元できます。さらに悪いことに、攻撃者は、任意の既知のメッセージ/署名ペアに対してこのような悪意のある msg を構築でき、これは、被害者が一度悪意のあるメッセージに署名するように誘導されれば、攻撃者が秘密鍵を完全に復元し、任意の署名を偽造できることを意味します。

脆弱性利用(PoC)

elliptic ライブラリは、入力タイプの一つとして16進数文字列を使用でき、署名時に msg を BN インスタンスに変換し、さらに配列型に変換します。異なる二つのメッセージが変換後に同じ配列を生成した場合、nonce も同じになり、最終的に k の再利用が発生します。

コード例:

実行出力:

msg1 = "message" および msg2 = "-message" は BN 変換を通じて同じ nonce を生成しました(elliptic の設計では、16進数文字列が可能な入力タイプの一つとして受け入れられます)。nonce が同じであるため、HMAC_DRBG によって生成された k も同じになります。署名結果から、r 値が完全に同じであることが確認でき、これは k の再利用の直接的な表れです。

完全な攻撃フロー:

1. 被害者に通常のメッセージに署名させ、メッセージ msg0 と署名 sig0 を取得します。

2. 攻撃者は悪意のあるメッセージ msg1 を構築し、被害者に署名をさせて sig1 を取得します。

3. k 再利用の脆弱性を利用して、(r, s1) と (r, s2) から k を計算します。

4. k を使用して秘密鍵 d をさらに計算し、攻撃が成功します。

コード例:

実行出力:

修正提案

1. elliptic を 6.6.1 以上にアップグレードします。公式にこの問題は修正されています。

2. 検証されていないメッセージを直接署名することを避け、msg が標準化処理を受けることを確認します。

3. 影響を受ける可能性のある秘密鍵を変更します。

まとめ

elliptic のこの脆弱性は、ECDSA 署名におけるランダム数 k の極端な感度を露呈しています。一度 k が再利用されると、秘密鍵は直接復元でき、攻撃者はユーザーの資産とアイデンティティを完全に制御できるようになります。開発者は脆弱性を迅速に修正し、入力処理を厳格に規定して、ECDSA 署名の安全性を確保する必要があります。最後に、Rabby ウォレットから提供された脆弱性情報に感謝します。

参考資料

[1] https://github.com/indutny/elliptic/security/advisories/GHSA-vjh7-7g9h-fjfh
[2] https://paulmillr.com/posts/deterministic-signatures