作者: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 庫本應使用 HMAC_DRBG(確定性隨機數生成器)確保每次簽名的 k 唯一。然而,由於輸入處理的錯誤,某些情況下不同的消息可能生成相同的 k,導致私鑰泄露。
k 生成代碼
在 elliptic 庫的 sign() 方法中,k 由 HMAC_DRBG 僞隨機數生成器生成,相關代碼如下:
elliptic/lib/elliptic/ec/index.js

HMAC_DRBG 的輸入參數主要包括:
entropy(熵) → 這裏使用的是私鑰 bkey,確保 HMAC_DRBG 具有足夠的隨機性。
nonce(隨機數種子) → 由 msg 計算而來,影響 k 的最終生成值。
由於 HMAC_DRBG 是確定性的,相同的 entropy 和 nonce 會生成相同的 k。如果 nonce 在不同的簽名過程中相同,則 k 也會相同,這將導致私鑰泄露。
漏洞成因:BN 轉換導致 nonce 複用
在 elliptic 的實現中,msg 被轉換爲 BN (Big Number),然後 nonce 由 msg.toArray() 計算:

問題點:
msg 是 BN 實例,但 nonce 是數組。
不同的 BN 實例可能在轉換後生成相同的數組。
這意味着可以爲不同的消息生成相同的 nonce,最終導致 k 複用。
如果 k 在兩個不同的消息 msg1 和 msg2 上覆用,則可以通過上文提到的數學公式恢復私鑰 d。
攻擊者可以構造特殊的 msg,使其 nonce 相同,從而導致 k 複用。只需要獲取一對 (r, s1) 和 (r, s2),即可通過數學計算恢復私鑰 d。更嚴重的是,攻擊者可以針對任何已知的消息/簽名對構造這樣的惡意 msg,這意味着只要誘導受害者簽署一次惡意消息,攻擊者就能完全恢復私鑰,並僞造任意簽名。
漏洞利用(PoC)
elliptic 庫允許使用十六進制字符串作爲輸入類型之一,在簽名時會將 msg 轉換爲 BN 實例,再轉換爲數組類型。如果兩個不同的消息在轉換後生成相同的數組,nonce 也會相同,最終導致 k 複用。
代碼示例:

運行輸出:

msg1 = "message" 和 msg2 = "-message" 經過 BN 轉換後,生成了相同的 nonce(elliptic 的設計接受十六進制字符串作爲可能的輸入類型之一)。由於 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
