Автор:enze

Редактор:Liz

Введение

Недавно в широко используемой криптобиблиотеке elliptic в экосистеме JavaScript была обнаружена серьезная уязвимость в безопасности (GHSA-vjh7-7g9h-fjfh). Атакующий может, сконструировав определенный вход, извлечь закрытый ключ, подписав всего лишь один раз, полностью контролируя цифровые активы жертвы или удостоверяющие данные личности.

Основная причина этой уязвимости заключается в недостатках обработки нестандартных входных данных в библиотеке elliptic, что приводит к возможному повторению случайного числа k в ECDSA подписи. Поскольку безопасность алгоритма ECDSA крайне зависит от уникальности k, как только k повторяется, закрытый ключ можно будет непосредственно вывести, что приведет к необратимому риску безопасности.

В данной статье будет проанализирована природа уязвимости, ее причины и последствия, а также предложены рекомендации по исправлению, чтобы помочь разработчикам снизить риски безопасности.

Контекст уязвимости

Введение в библиотеку elliptic

Elliptic - это широко используемая библиотека криптографии на эллиптических кривых в экосистеме JavaScript, поддерживающая различные кривые (например, secp256k1, ed25519). Эта библиотека используется множеством криптовалютных кошельков, систем аутентификации, приложений Web3 и других.

Область воздействия

  • Затронутые версии:elliptic <= 6.6.0

  • Затронутые кривые:secp256k1, ed25519 и другие

  • Сценарии воздействия:все приложения, которые принимают внешние входные данные для ECDSA подписей (особенно системы, которые принимают внешние входные данные для подписей)

Если приложение зависит от elliptic для ECDSA подписей и позволяет пользователю предоставлять непроверенные сообщения в качестве содержимого подписи, то оно, скорее всего, будет подвержено этой уязвимости.

Принцип уязвимости

Случайное число k в ECDSA подписи

Представьте, что у вас есть уникальная печать (закрытый ключ), и каждый раз, когда вы подписываете документы, вам нужно использовать уникальную печать (случайное число k). Оставленный след будет генерировать два числа r и s, что и является результатом подписи.

В этом процессе есть несколько ключевых моментов:

  • Печать (закрытый ключ) всегда неизменна - это представляет вашу личность.

  • Каждая подпись должна использовать совершенно новую печать (случайное число k никогда не должно повторяться).

  • Даже если подписать один и тот же документ, результаты подписи (r, s) должны быть разными.

  • Каждый может проверить, что след был оставлен вашей печатью (публичный ключ проверяет подпись).

  • Но никто не может вывести вашу печать (закрытый ключ) из следа, при условии, что k безопасно.

Чтобы обеспечить безопасность k, ECDSA использует стандарт RFC 6979, применяя детерминированный генератор случайных чисел (HMAC_DRBG или HKDF) для вычисления k. При генерации k используются закрытый ключ d и сообщение m в качестве семени, чтобы гарантировать фиксированное значение k при одинаковых входных данных. Процесс вычисления следующий:

Ключевые моменты:

1. k должно быть случайным и уникальным, иначе это приведет к утечке закрытого ключа.

2. Если k одинаково при двух подписьах, атакующий может просто восстановить закрытый ключ d с помощью двух подписей (r, s1) и (r, s2).

Математическое обоснование:

Предположим, что пользователь подписал два разных сообщения m1 и m2, но случайное число k оказалось одинаковым, тогда результат подписи будет следующим:

Атакующий может вычислить разницу между s1 и s2:

Поскольку m1, m2, s1 и s2 известны, атакующий может вычислить k:

Затем подставьте k в исходное уравнение подписи, чтобы решить для закрытого ключа d:

Анализ уязвимости

Библиотека elliptic должна использовать HMAC_DRBG (детерминированный генератор случайных чисел), чтобы гарантировать уникальность k для каждой подписи. Однако из-за ошибок в обработке входных данных в некоторых случаях разные сообщения могут генерировать одно и то же k, что приводит к утечке закрытого ключа.

Код генерации k

В методе sign() библиотеки elliptic 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, вычислите k из (r, s1) и (r, s2).

4. Дальнейшее вычисление закрытого ключа d с использованием k, атака успешна.

Пример кода:

Вывод выполнения:

Рекомендации по исправлению

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