Вечером 31 декабря 2023 года (UTC) атакующий опустошил хранилище Orbit Bridge в Ethereum: около $81,5 млн в DAI, WBTC, ETH, USDC и USDT12. Orbit подтвердила инцидент той же ночью13.
Хранилище опустело не одной транзакцией. Крупные выводы заняли 17 минут, а первые шаги атакующего были за полчаса до них. Этот пост отвечает на вопросы: когда сеть впервые показала, что что-то не так, сколько ещё лежало в хранилище в этот момент и что могла сделать команда.
Как устроено хранилище Orbit Bridge
Orbit Bridge, который ведёт корейская компания Ozys, переносит токены между Orbit Chain и другими сетями. В Ethereum все перенесённые токены лежат в одном контракте — хранилище 0x1Bf6…489a2.
- Депозит. Пользователь отправляет токены в хранилище в Ethereum и получает обёрнутые токены в Orbit Chain.
- Вывод. Пользователь сжигает обёрнутые токены в Orbit Chain. Валидаторы моста видят сжигание, и каждый подписывает сообщение о выводе.
- Выдача. Затем кто угодно может вызвать
withdrawу хранилища с этими подписями. Хранилище проверяет, что подписало достаточно его владельцев, помечает сообщение использованным, чтобы его нельзя было повторить, и отправляет токены.
Хранилище — мультисиг. До атаки у него было 10 владельцев и нужно было 7 подписей: мы прочитали getOwners() и required() на блоке 18907899, за один блок до первой транзакции атакующего4. Десять ключей и порог 70% звучат надёжно. Надёжны они, только пока никто в одиночку не может получить семь из них.
Хранилище не отличает настоящее сжигание в Orbit Chain от выдуманного. Оно видит только подписи. Есть семь верных подписей — токены уходят.
Атака по шагам
Время — UTC, время блоков Ethereum. Каждую транзакцию ниже мы прочитали в сети сами4. Каждый вывод вызывал withdraw у хранилища минимум с семью подписями его владельцев, и хранилище приняло их все.
20:40:35 — проба на 0,012 WBTC
С адреса 0xdc54…c522 атакующий вывел 0,0120879 WBTC на 0x9ca5…4049 (0x9e51…b589). Через полчаса тот же адрес получил 230 WBTC. Несколько сотен долларов — это проба: атакующий проверил, что подписи работают.
20:52:47 — 30 ETH
Основной адрес атакующего 0x9263…6AfF вывел 30,095 ETH на 0x27E2…5710 (0x958a…5f16). Нативный ETH уходит из хранилища внутренним переводом, без события Transfer. По данным Rekt, именно 20:52 Orbit называет временем первого несанкционированного доступа2.
С 21:07:59 до 21:25:35 — хранилище пустеет
| Время | Что ушло из хранилища | Транзакция |
|---|---|---|
| 21:07:59 | 10 000 000 DAI | 0xafdc…07ca |
| 21:10:59 | 230,88 WBTC (~$9,8 млн) | 0xe0ba…71f9 |
| 21:16:59 | 9 500 ETH (~$21,5 млн) | 0x639d…ae0a |
| 21:25:11 | 10 000 000 USDC | 0x64a6…3f0e |
| 21:25:35 | 30 000 000 USDT | 0xd8ca…0ac8 |
Суммы в долларах — по данным Orbit и Rekt на момент инцидента12. WBTC ушёл на адрес пробы, остальное — каждый раз на новый адрес.
Здесь порог по сумме работает. $10 млн в DAI из хранилища моста одной транзакцией — это не обычный трафик пользователей.
То же правило на WBTC, USDC и USDT и правило «Общий баланс адреса в $ ниже порога» на хранилище с порогом около $75 млн сработали бы примерно тогда же — от DAI и WBTC. Ещё один алерт в ту же минуту не добавляет времени. Он подтверждает, что это не ошибка.
22:21:35 — хранилище выключено
В 22:21:35 адрес 0x4C35…4d05 вызвал changeActivate(false) у хранилища (0xedfc…13f4). С этого блока хранилище отказывало в выводе. withdraw на 300 000 USDC в 22:26:23 откатился (0x0958…705b)4.
Это было через 74 минуты после вывода DAI и через 56 минут после последнего вывода. Хранилище к тому времени почти опустело.
После хранилища — куда ушли деньги
Атакующий начал выводить средства дальше в течение получаса: USDC с 21:50, USDT с 22:13, WBTC с 22:444. По данным Rekt, стейблкоины и WBTC обменяли на ETH, а адреса атакующего изначально пополнили через Tornado Cash2. В июне 2024 года атакующий отправил в Tornado Cash 8 671 ETH, около $32 млн6.
Сообщений о заморозке этих адресов эмитентами USDT и USDC мы не нашли.
Что Orbit сказала о причине
Ozys заявила, что ошибки в контрактах не было7. В январе 2024 года она подала в суд на своего бывшего директора по информационной безопасности. По её словам, он ослабил правила файрвола в ноябре 2023 года и ушёл из компании в начале декабря, за три недели до атаки7. Как атакующий получил семь верных подписей, публично не объяснено. Мы не гадаем.
Что не изменило бы ни одно правило
Как поставить такие правила в SmartAlert
- Добавьте каждый токен, который хранит хранилище. Для Orbit это DAI, WBTC, USDC и USDT.
- На каждом создайте правило перевод больше суммы в $ с
from= ваше хранилище. Порог берите из своего трафика: выше самого крупного обычного вывода пользователя и сильно ниже суммы в хранилище. Сумма считается по цене на блоке перевода, поэтому порог не плывёт вместе с ценой. Тариф Pro и выше. - Проверьте порог до включения. Правило в $ читает цены только с момента создания, поэтому на прошлых переводах его не проверить. Подберите порог правилом крупный перевод в токенах: оно показывает на прошлых данных, как часто сработало бы. Если каждый день — поднимите порог.
- Добавьте и нативный ETH. ETH не выпускает
Transfer. Добавьте хранилище в «Адреса» и создайте правило «Общий баланс адреса в $ ниже порога» с порогом чуть ниже обычной суммы. - Отправляйте эти алерты тем, кто может выключить хранилище. Telegram бесплатный. Держатель этого ключа должен получать их напрямую, а не через общий канал, который читают утром.
Все рецепты алертов.
Переезжаете с OpenZeppelin Defender? Как каждый монитор Defender переносится в правило SmartAlert — в руководстве по переезду, а сравнение с Defender — отдельной страницей.
Источники
- 1.Orbit Chain, Official statement regarding Orbit Bridge exploit: суммы и украденные токены. ↩
- 2.Rekt, Orbit Bridge — Rekt: хронология, 20:52 как первый несанкционированный доступ, обмен в ETH, пополнение атакующего через Tornado Cash. ↩
- 3.Orbit Chain в X, сообщение об инциденте. ↩
- 4.Проверено SmartAlert в сети:
required()= 7 иgetOwners()= 10 адресов у хранилища на блоке 18907899 (архивныйeth_call); каждая транзакция в посте — время блока, отправитель, получатель и сумма — через Blockscout API; откатившийся вывод в 22:26:23. ↩ - 5.SmartAlert, задержка алерта в Ethereum: 12 подтверждений (около 144 с), опрос головы сети до 12 с, запись до 15 с (P95), проверка правила раз в 60 с на Pro. ↩
- 6.Forklog, Orbit hacker funnels $32 million into Tornado Cash. ↩
- 7.The Block, материал об Ozys и её бывшем директоре по безопасности: ошибки в контрактах нет, изменение файрвола в ноябре 2023 года, уход в декабре, иск. ↩
- 8.CertiK, Bybit incident technical analysis. ↩
- 9.KiloEx, Security incident root cause analysis. ↩