Безопасность Web3: с нуляЧасть 8 из 12SmartAlert6 мин чтения

Взлом одной транзакцией: Radiant, Infini, KiloEx и Bybit, и чего мониторинг не может

Некоторые взломы не дают предупреждения: первый сигнал и потеря — одна транзакция. Четыре случая, почему алерт опаздывает и что помогает.

*Часть 8 из 12 серии «Безопасность Web3: с нуля».*

В частях 5–7 речь шла в основном об атаках с зазором: украденный ключ меняет владельца, а минуты или часы спустя уходят деньги. В этом зазоре и живёт монитор. Эта часть — об атаках без зазора и о том, чего монитор честно не может. Она последняя в блоке, поэтому скажем прямо.

Как это устроено

Транзакция в Ethereum или BNB Chain атомарна. Всё внутри неё либо происходит, либо откатывается. Одна транзакция может вызвать множество контрактов, взять флэш-заём, сменить владельца, обновить прокси и отправить токены. Снаружи в цепи потом виден один блок с одним результатом.

Когда вся атака умещается в одну транзакцию или в несколько в одном блоке, никакого «до» нет. Первый признак в цепи и потеря — один и тот же момент.

Есть и жёсткий нижний предел у любого монитора, который читает подтверждённые блоки. SmartAlert ждёт 12 подтверждений в Ethereum, около 144 секунд, прежде чем считать блок окончательным: блок, который уйдёт в реорганизацию, дал бы ложную тревогу. После этого идут опрос и вычисление правила. Алерт приходит примерно через 2,5–4 минуты после блока в Ethereum и менее чем через 2 минуты в BNB Chain. Если атака короче, алерт приходит вместе с потерей или после неё.

Четыре случая

Время везде UTC. Суммы — по данным протокола или Rekt.

Radiant Capital, октябрь 2024: всё в одной транзакции

16 октября 2024 в 17:11:00 одна транзакция в BNB Chain сменила владельца контракта-провайдера протокола, обновила контракт и отправила токены атакующему12. Всего ушло около $50–58 млн в BNB Chain и Arbitrum вместе. Сигнал, который дало бы правило на смену владельца, и потеря сидят в одной транзакции. Алерт приходит после неё.

Infini, февраль 2025: роль использовал её же владелец

24 февраля 2025 в 02:15:59 два вызова withdrawToReserve в одном блоке (21913141) вывели $49,5 млн из Infini в Ethereum34. Адрес, который их сделал, владел ролью 114 дней. До атаки в цепи ничего не менялось: ни нового владельца, ни новой роли. Кошелёк атакующего был профинансирован через Tornado Cash в 00:57:47, но этого адреса никто заранее не знал.

KiloEx, апрель 2025: одна секунда в трёх сетях

Мы разбирали его в части 7. Около $7,5 млн ушло из BNB Chain, opBNB и Base за минуту, первые два перевода в одну секунду. После алерта на первый вывод оставалось уйти около $0,4 млн5.

Bybit, февраль 2025: самый длинный зазор в этом блоке, и всё равно мало

21 февраля 2025 в 14:13:35 на Safe Bybit выполнили execTransaction с delegatecall от исполнителя, которым этот Safe раньше не пользовался6. В 14:15:11 последовал тестовый перевод, а в 14:16:11 — основной вывод около $1,46 млрд. От первого признака до вывода 2 минуты 36 секунд, это меньше, чем 144 секунды ожидания подтверждений плюс опрос67. Из четырёх это лучший случай, и всё равно времени не хватает: алерт на первую транзакцию пришёл бы около 14:16–14:17:30, когда деньги уже уходили.

Zoth из части 5 в той же группе: 36 секунд между Upgraded и выводом8.

Чего монитор подтверждённых блоков не может

  • Предупредить заранее. В этих случаях «заранее» нет. Первый алерт — это сообщение о потере, а не прогноз.
  • Что-то остановить. SmartAlert присылает алерты. Он не держит ваши ключи и не может поставить контракт на паузу.
  • Увидеть транзакцию до того, как её включили в блок. Он читает блоки глубиной в 12 подтверждений, а не mempool.

Более быстрые подтверждения помогли бы лишь отчасти. Если убрать 144 секунды ожидания, алерт по Bybit сдвинулся бы примерно на две минуты. Для Radiant, Infini и KiloEx это не изменило бы ничего.

Другой класс продукта

В этих случаях сработал бы другой инструмент, и полезно знать, какой:

  • Симуляция в mempool. Читать транзакции до включения в блок, прогонять каждую в симуляции и оценивать, что она сделает с балансами и состоянием вашего контракта.
  • Автоматическая пауза. Если симуляция показывает потерю, контракт-файрвол или guardian блокирует транзакцию или ставит контракт на паузу до того, как она попадёт в блок, без человека в цепочке.

К этому классу относятся, например, Hypernative и Forta Firewall. Они работают иначе, чем алерт: стоят на пути ваших транзакций и действуют от имени контракта, поэтому им нужно право блокировать его или ставить на паузу. SmartAlert к ним не относится, и мы не собираемся утверждать обратное. Если ваша угроза — эксплойт одной транзакцией, ответ не алерт, и смотреть надо в сторону этого класса.

Что всё равно помогает

Даже для этих случаев алерт после первой транзакции полезен по трём причинам.

  1. Атака не всегда закончена. У KiloEx после первых переводов остались хвосты: более мелкие выводы шли до 19:55:33.
  2. Деньги можно заморозить ниже по течению. Биржи, эмитенты стейблкоинов и мосты замораживают средства, если сообщить им в течение часа. Сигнал на третьей минуте намного лучше сигнала на шестидесятой.
  3. Вы узнаёте от своего монитора, а не из Twitter.

А хороший дизайн сокращает ущерб, который не остановит никакой алерт:

  • Таймлок на апгрейды и смену владельца. Он превращает атаку одной транзакцией в атаку с зазором, а зазор — это то, что нужно монитору.
  • Лимиты на вывод за транзакцию или за час. Шлюз Ronin ограничивал каждый вывод примерно 4 000 ETH или 2 млн USDC (см. часть 6).
  • Ревизия ролей. Атакующий Infini владел ролью 114 дней. Алерт на изменение не сработает на роль, выданную давно; её увидит только человек, который прочитает список владельцев ролей. Отчёта «кто сегодня держит какую роль» у нас нет, это ручная работа.

Как поймать

Честно: правила ниже заранее не предупреждают. Они быстро сообщают, и после них остаётся только ограничить ущерб.

  1. Админские события, как в части 5: передача владения, апгрейд прокси, выдача роли. Для Radiant они сработали бы примерно в момент потери.
  2. Safe: новый исполнитель и смена реализации. Правило «Разрешённые отправители» на ExecutionSuccess срабатывает, когда отправитель вне списка ваших владельцев и релейеров; правило «Состояние контракта» на masterCopy() с условием «изменилось» срабатывает при смене реализации Safe. В случае Bybit оба сработали бы в момент вывода, а не до него. Третье — смена владельцев Safe. «Разрешённые отправители» — тариф Business и выше.
  3. Вывод в долларах. Перевод больше суммы в $ и «суммарный баланс адреса в $ ниже порога» на казначействе и хранилищах. Они говорят, какого размера потеря и, при атаках с хвостами, куда она ещё идёт.
  4. Канал к человеку, который может действовать, проверенный ночью. При потере одной транзакцией ближайшие пять минут и есть реагирование, и часть 12 именно о них.

Дальше: часть 9, что мониторить после аудита: чек-лист.

Источники

  1. 1.Radiant Capital в X, заявление об инциденте. ↩
  2. 2.Rekt, Radiant Capital — Rekt II: суммы и ход атаки. ↩
  3. 3.Infini в X, заявление об инциденте. ↩
  4. 4.Rekt, Infini — Rekt: $49,5 млн, роль, которой владели 114 дней. Блок и время: SmartAlert, on-chain. ↩
  5. 5.KiloEx, Security incident root cause analysis. ↩
  6. 6.CertiK, Bybit incident technical analysis: delegatecall, исполнитель, вывод. ↩
  7. 7.SmartAlert, время транзакций Bybit (14:13:35, 14:15:11, 14:16:11), сверено on-chain. ↩
  8. 8.Rekt, Zoth — Rekt. ↩

Попробуйте бесплатно

Достаточно адреса. Первые 14 дней действуют лимиты Pro, а правила проверяются на истории до включения.

Ещё статьи