Уязвимость воспроизведения подписи в смарт-контракте | БлокАудит

Полезный прием — подписывать сообщения вне сети и иметь контракт, требующий этой подписи перед выполнением функции.
Например, этот метод используется, чтобы:
- уменьшить количество транзакций в цепочке
- транзакция без газа, называемаяmeta transaction
Давайте посмотрим, о чем мы говорим
Блокчейны в значительной степени полагаются на криптографические подписи. Транзакции подписываются соответствующими закрытыми ключами, что позволяет связать отправителей транзакций со своими учетными записями. Бухгалтерия блокчейна была бы неработоспособна без этой функции.
Цифровые подписи также часто проверяются непосредственно в смарт-контрактах Ethereum, что позволяет одному или нескольким верификаторам авторизовать действия, отправляя автономные подписи ( или даже подписи, сгенерированные другим смарт-контрактом ).
Это часто используется в хранилищах с несколькими подписями или контрактах с голосованием для одновременной отправки нескольких подписей или для делегирования авторизации.
Атаки с повторным воспроизведением подписи являются распространенной уязвимостью в таких реализациях.
Проверка подписи в смарт-контрактах иногда требуется для повышения удобства использования или экономии затрат на газ. Безопасная реализация должна предотвращать атаки с повторным воспроизведением подписи.
Например: -
Отслеживание всех обработанных хэшей сообщений и разрешение обработки только новых хэшей сообщений. Злоумышленник может атаковать контракт, в котором отсутствует такой контроль, и получить хэш сообщения, отправленный другим пользователем и обработанный несколько раз.
Криптографические цифровые подписи
Цифровые подписи — это примитивы открытого ключа аутентификации сообщений. В физическом мире рукописные подписи обычно используются в написанных от руки или напечатанных сообщениях. Они используются для привязки подписавшего к сообщению.
Цифровая подпись — это криптографическое значение, генерируемое данными и секретным ключом, известным только подписавшемуся.
Модель цифровой подписи
Полная процедура подробно объясняется в следующих пунктах:

- Каждый пользователь этой схемы имеет набор открытых и закрытых ключей.
- Пары ключей, используемые для шифрования/дешифрования и подписи/проверки, часто отличаются друг от друга. Открытый ключ называется ключом проверки, а закрытый ключ называется ключом подписи.
- Данные отправляются в хеш-функцию подписывающей стороной, которая создает хэш.
- Затем алгоритм подписи генерирует цифровую подпись для предоставленного хэша, используя значение хеш-функции и ключ подписи. Данные получают подпись, и оба впоследствии передаются верификатору.
- Алгоритм проверки передается верификатором вместе с цифровой подписью и ключом проверки. Результат алгоритма проверки — нечто полезное. Для полученных данных верификатор также использует тот же алгоритм хеширования для создания хеш-значения.
- Это хеш-значение и результаты процесса проверки сравниваются для проверки. Верификатор определяет, является ли цифровая подпись законной, на основе результатов сравнения.
- Никто другой не может использовать «закрытый» ключ подписывающего лица для установления цифровой подписи, поэтому подписывающее лицо не может впоследствии отозвать свою подпись данных.
Уровень протокола
Только транзакции с действительными подписями включаются в свежие блоки благодаря сети Ethereum . Для транзакций это предлагает следующие атрибуты безопасности:
- Аутентификация : подпись используется узлами Ethereum для подтверждения того, что лицо, подписывающее транзакцию, является владельцем закрытого ключа, связанного с их публичным адресом. Поэтому разработчики могут быть уверены, что msg.sender является подлинным.
- Целостность : Целостность — это условие того, что транзакция не была изменена после подписания; в противном случае подпись недействительна.
- Неотказуемость: подпись транзакции и любые изменения состояния, сделанные подписывающей стороной, владеющей закрытым ключом, не могут быть оспорены. Закрытый ключ принадлежит общедоступному адресу, указанному в поле « От ».
Одна и та же подпись может использоваться несколько раз для выполнения функции. Это может быть вредно, если подписывающая сторона намеревалась единожды утвердить транзакцию.
Пример кода
Давайте рассмотрим ошибку.
function unlock(
address _to,
uint256 _amount,
uint8[] _v,
bytes32[] _r,
bytes32[] _s
)
external
{
require(_v.length >= 20);
bytes32 hashData = keccak256(_to, _amount);
for (uint i = 0; i < _v.length; i++) {
address recAddr = ecrecover(hashData, _v[i], _r[i], _s[i]);
require(_isValidator(recAddr));
}
to.transfer(_amount);
}
Сообщение, подписанное валидаторами с использованием техники ECDSA, и есть проблема с вышеупомянутым кодом. Адрес получателя и необходимые деньги являются единственной информацией в сообщении. Ничто в сообщении не может быть использовано, чтобы избежать повторного использования одной и той же подписи. Рассмотрим следующий случай:
- Сэм переводит 2000 ETH из связанной сети обратно в цепочку Ethereum, используя такое же количество валюты.
- Обработкой этой межблочной транзакции занимается Том, ретранслятор. Чтобы высвободить 2000 ETH из контракта и отправить их Сэму, он собирает необходимые подписи валидатора, блокирует нужную сумму в подключенной цепочке, а затем использует функцию разблокировки.
- В блокчейне транзакцию, содержащую массивы значений подписи, видят все.
- Теперь, когда Сэм скопировал массивы сигнатур, он может самостоятельно отправить запрос на разблокировку. И снова процесс разблокировки будет успешным, в результате чего Сэму будет переведено 2000 ETH.
- Сэм может продолжать в том же духе, пока контракт не будет исчерпан.

Атака с воспроизведением сигнатуры описана в приведенном выше примере. Это возможно, поскольку нет способа определить, является ли это конкретное подписанное сообщение уникальным и использовалось ли оно ранее.
Подпишите сообщения с nonce
адресом контракта.
public uint256 nonce;
function unlock(
address _to,
uint256 _amount,
uint256 _nonce,
uint8[] _v,
bytes32[] _r,
bytes32[] _s
)
external
{
require(_v.length >= 20);
require(_nonce == nonce++);
bytes32 hashData = keccak256(_to, _amount, _nonce);
for (uint i = 0; i < _v.length; i++) {
address recAddr = ecrecover(hashData, _v[i], _r[i], _s[i]);
require(_isValidator(recAddr));
}
to.transfer(_amount);
}
Для защиты от атак с воспроизведением сигнатуры примите во внимание следующие рекомендации:
- Храните каждый хэш сообщения, который был обработан смарт-контрактом. При получении новых сообщений сверяйтесь с уже существующими и применяйте бизнес-логику только в том случае, если это хэш нового сообщения.
- Включите адрес контракта, который обрабатывает сообщение. Это гарантирует, что сообщение может быть использовано только в одном контракте.
- Ни при каких обстоятельствах не генерируйте хэш сообщения, включая подпись. Функция
ecrecover
восприимчива к гибкости подписи.
Неуникальные подписи могут быть воспроизведены в различных ситуациях, как видно из приведенного выше примера. Чтобы избежать повторных атак, в большинстве ситуаций крайне важно убедиться, что сигнатуры специально соответствуют каждому вызову. Кроме того, по этой причине в каждую транзакцию Ethereum включается одноразовый номер.
Использованная литература: -
https://solidity-by-example.org/hacks/signature-replay/
https://blog.finxter.com/smart-contract-replay-attack-solidity/
https://swcregistry.io/docs/SWC-121
Суета с безопасностью web3!!! Связаться с нами!!!
БлокАудит :- Почему мы??
BlockAudit обладает ресурсами и знаниями для создания решений кибербезопасности, которые экономят миллионы долларов.
Линкедин | Веб -сайт | Твиттер
