Back to Blog

~$18 млн потеряно: jaredFromSubway, Aztec и другие | Еженедельник BlockSec

Code Auditing
25 июня 2026 г.
10 min read
Key Insights
  • 3 заметных инцидента в сетях Ethereum и BNB Chain с общими потерями около $18,3 млн.

  • Типы атак включают: ненадлежащее управление разрешениями (approvals) в контрактах MEV-ботов, некорректную привязку публичных входных данных ZK и неправильную конфигурацию токена, приводящую к манипуляции резервами.

  • Выделенный инцидент с jaredFromSubway демонстрирует паттерн атаки с обращёнными разрешениями по сравнению с традиционными эксплойтами в DeFi. В традиционных случаях злоумышленники используют уязвимости в доверенных DeFi-контрактах, чтобы вывести активы, которые пользователи одобрили для этих контрактов. Здесь же MEV-бот проактивно выдавал разрешения на собственные активы в пользу ненадёжных сторонних контрактов в рамках своих арбитражных операций, а злоумышленник накапливал эти исходящие разрешения, чтобы вывести ~$15 млн.

За прошедшую неделю (2026/06/15 - 2026/06/21) мы зафиксировали 3 notable инцидента в сфере безопасности с общими потерями около $18,3M.

Дата Инцидент Тип Предполагаемые потери
2026/06/18 Aztec Некорректная привязка публичных входных данных ~$2,2M
2026/06/20 LABUBU Token Неправильная конфигурация ~$1,1M
2026/06/20 jaredFromSubway Ненадлежащее управление разрешениями ~$15M
  • Aztec: Выбран потому, что протокол был взломан второй раз за три дня — на этот раз через схему escape hatch, что подчёркивает повторяющиеся проблемы с привязкой ZK-доказательств.
  • jaredFromSubway: Выбран потому, что контракт MEV-бота предоставлял разрешения ненадёжным токен-контрактам без проверки их использования или отзыва остаточных лимитов, что позволило атакующему накапливать и выводить ~$15M.

Лучший аудитор безопасности для Web3

Проверьте дизайн, код и бизнес-логику перед запуском

Главное событие недели: jaredFromSubway

В отличие от традиционных эксплойтов с разрешениями — когда атакующие злоупотребляют уязвимостями в доверенных DeFi-контрактах для вывода активов, которые пользователи одобрили этим контрактам, — данная атака направлена в обратную сторону: MEV-бот проактивно предоставлял разрешения на собственные активы ненадёжным сторонним контрактам в рамках арбитражных операций. Атакующий создал поддельную торговую среду (по сути, ханипот), где фиктивные пулы для обмена генерировали настоящие события Swap и Sync, тогда как поддельные токены никогда не использовали предоставленные лимиты, накапливая эти направленные наружу разрешения перед их финальным сбором; общие потери составили ~$15M.

20 июня 2026 года jaredFromSubway, оператор MEV-бота на Ethereum, потерял около $15M [1]. По результатам on-chain анализа, основная причина — ненадлежащее управление разрешениями в контракте бота: разрешения были предоставлены ненадёжным контрактам-обёрткам, которые так их и не использовали, а атакующий накапливал эти неиспользованные лимиты до тех пор, пока не вывел реальные балансы бота в одной транзакции.

Предыстория

jaredFromSubway — широко известный оператор MEV-бота на Ethereum, специализирующийся на сэндвич-атаках и on-chain арбитраже. Пострадавший контракт (0x1f2f...f387) является одним из его рабочих кошельков, на котором хранятся крупные рабочие балансы в WETH, USDC и USDT.

MEV-боты подобного рода должны динамически взаимодействовать с произвольными новыми токенами и пулами, появляющимися в сети. Они отслеживают мемпул, симулируют транзакции и автоматически одобряют взаимодействия с токенами для захвата арбитражных возможностей. Эта операционная модель основывается на предположении, что токены ведут себя ожидаемым образом: при выполнении обмена токен-контракт потребляет предоставленный лимит, вызывая transferFrom.

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

Основная причина — ненадлежащее управление разрешениями MEV-бота при взаимодействии с ненадёжными контрактами.

Бот выполняет различные арбитражные пути через пулы и роутеры Uniswap. В большинстве взаимодействий бот напрямую передаёт токены в пулы через transfer, где сам бот является msg.sender и разрешение не требуется. Однако взаимодействие с токен-контрактами типа обёртки следует модели pull: бот вызывает wrapper.wrapTo(), и внутри этого вызова контракт-обёртка вызывает realToken.transferFrom(bot, wrapper, amount) для получения реальных токенов бота. Поскольку msg.sender во время transferFrom — это контракт-обёртка, а не бот, необходимо предварительное approve:

  1. <real_token>.approve(tokenContract, amount) — предоставить лимит на реальный токен контракту-обёртке
  2. tokenContract.wrapTo() → многоступенчатый swap() через пулы → tokenContract.unwrap() — обернуть реальный токен, провести через пулы и развернуть обратно в реальный токен

Бот предполагал, что wrapTo() потребит лимит через transferFrom, как это делает корректно работающая обёртка. Однако бот никогда не проверял, был ли лимит фактически использован после операции, и не отзывал остаточные разрешения. Если wrapTo() не вызывает transferFrom, полный лимит сохраняется после операции и становится постоянной поверхностью атаки — любой контракт, имеющий такой лимит, может впоследствии вызвать transferFrom и переместить реальные активы бота.

Анализ атаки

На основе on-chain реконструкции атакующий создал поддельную торговую среду из трёх компонентов для эксплуатации описанной выше уязвимости:

  1. Поддельные токены-обёртки: Каждый поддельный токен использовал имя реального токена, но добавлял префикс f к символу (например, имя USD Coin с символом fUSDC для USDC). Он реализовывал wrapTo() и unwrap() для имитации легитимной обёртки, а также функцию withdraw(), доступную только атакующему, которая выводила неиспользованные лимиты через transferFrom.

  2. Поддельные пулы для обмена: Атакующий развернул около 44 пулов в стиле Uniswap V2 через самостоятельно развёрнутую фабрику. Эти пулы составляли пары из поддельных токенов друг с другом, формируя убедительные маршруты обмена. При вызове swap() пулы генерировали настоящие события Sync и Swap, неотличимые от легитимных сделок.

  3. Специально сформированная прибыль атакующего: Во время unwrap() поддельный токен отправлял небольшое количество реальных токенов обратно боту через transfer. Бот получал реальную прибыль, но она была специально сформирована атакующим, а не заработана в результате рыночного арбитража.

Атакующий управлял этими компонентами через переключатель getStatus() для каждого блока во внешнем контракте. getStatus() возвращал 1 при вызове в том же блоке, что и транзакция активации (которая устанавливала _getStatus = block.number), и 0 в остальных случаях. Когда getStatus() == 0, wrapTo() вызывал transferFrom в штатном режиме и лимит потреблялся. Когда getStatus() == 1, wrapTo() пропускал transferFrom — лимит не потреблялся — тогда как unwrap() по-прежнему возвращал боту специально сформированные токены. Атакующий, по всей видимости, использовал взятки строителю блоков для размещения транзакции активации в том же блоке, что и транзакция бота, когда нужно было накапливать лимиты.

Атака проходила в три фазы:

Фаза 1: Развёртывание инфраструктуры атаки

  • Шаг 1: Атакующий развернул инфраструктуру в блоках с 25354424 по 25354519. Это включало развёртывание контракта поддельной фабрики токенов (0x81f2...0091), создание ~44 поддельных пулов Uniswap V2 через самостоятельно развёрнутую фабрику, пополнение пулов начальными балансами токенов для успешного выполнения вызовов swap(), а также отправку 0,01 ETH на контракт сбора (0xb84d...df52) для покрытия газа и взятки строителю.

  • Шаг 2: Атакующий массово создавал поддельные токены-обёртки через CREATE2, каждый из которых имитировал реальный токен (используя реальное имя, но добавляя префикс f к символу) и содержал функцию withdraw(), доступную только атакующему. CREATE2 обеспечивал детерминированные адреса, по которым контракт сбора мог итерироваться.

Фаза 2: Формирование доверия и накопление разрешений

  • Шаг 3 (начальное доверие): В самых ранних транзакциях (например, 0x542d...362b в блоке 25354425) поддельные токены не имели переключателя getStatus() — wrapTo() вызывал transferFrom напрямую, потребляя лимит. Бот одобрял, оборачивал, менял, разворачивал и получал прибыль в штатном режиме. Это позиционировало поддельные токены как прибыльные торговые возможности.

  • Шаг 4 (укрепление доверия): В последующих транзакциях (например, 0x085e...37e51) переключатель getStatus() был развёрнут, но возвращал 0 (другой блок от активации). wrapTo() по-прежнему вызывал transferFrom и потреблял лимит. Бот продолжал получать прибыль и сохранял взаимодействие.

  • Шаг 5 (накопление): Начиная с 0x8560...1915 в блоке 25360519, атакующий размещал транзакцию активации в том же блоке, что и транзакция бота, через взятку строителю, заставляя getStatus() возвращать 1. В этом режиме wrapTo() пропускал transferFrom — лимит не потреблялся — но unwrap() по-прежнему отправлял боту небольшое количество реальных токенов. Бот видел прибыльную операцию и оставлял разрешение на месте. Примерно за 600 блоков (~13 транзакций) бот повторял эту схему для WETH, USDC и USDT, накапливая неиспользованные лимиты на все три реальных актива.

Фаза 3: Сбор

  • Шаг 6: Атакующий вызвал withdraw() на всех поддельных токенах в транзакции сбора 0x2be870...cf3e65, используя неиспользованные лимиты для вызова transferFrom и перемещения реальных балансов бота к атакующему. Взятка строителю в размере 0,01 ETH была включена для гарантии включения в блок. В результате сбора из пострадавшего контракта было извлечено 1 474,58 WETH + 2 870 573 USDC + 2 035 760 USDT (~$7,5M).

Выявленная транзакция атаки приводит к потерям ~$7,5M, а общий ущерб составляет около $15M согласно заявлению jaredFromSubway [1].

Заключение

Основной причиной этого инцидента стало ненадлежащее управление разрешениями MEV-бота при взаимодействии с ненадёжными контрактами. В отличие от традиционных эксплойтов с разрешениями, когда атакующие злоупотребляют уязвимостями в доверенных DeFi-контрактах для вывода одобренных пользователями активов, данная атака работает в обратном направлении: бот проактивно одобрял собственные активы ненадёжным сторонним контрактам в рамках арбитражных операций. Атакующий накапливал эти направленные наружу неиспользованные разрешения и собрал их в одной транзакции.

Для снижения аналогичных рисков в будущем контракты ботов, взаимодействующие с ненадёжными токен-контрактами, должны проверять, что разрешения были использованы после каждой операции, и отзывать любые остаточные лимиты. Неиспользованные лимиты после внешне успешной сделки — явный признак вредоносного поведения токена.

Начните работу с Phalcon Explorer

Погружайтесь в транзакции, чтобы действовать разумно

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

Другие инциденты недели

Aztec

18 июня 2026 года отсутствующее ограничение равенства в ZK-схеме escape hatch протокола Aztec позволило атакующему вывести около $2,2M (1 158 ETH, 150K DAI и ~0,47 renBTC) из устаревшего контракта RollupProcessor на Ethereum [2], [3]. Это был второй эксплойт Aztec за три дня (первый, описанный в нашем предыдущем отчёте, был направлен против обновлённого RollupProcessorV3) через связанную ошибку привязки публичных входных данных ZK-схемы.

Предыстория

Устаревший RollupProcessor Aztec включает функцию escapeHatch: механизм безопасности, позволяющий любому пользователю отправить доказательство в рамках одной транзакции, когда оператор роллапа прекращает обработку. В отличие от processRollup (требующего авторизованного провайдера), escape hatch открывается в периодических окнах на основе номера блока и может быть вызван кем угодно:

function escapeHatch(
    bytes calldata proofData,
    bytes calldata signatures,
    bytes calldata viewingKeys
) external override whenNotPaused {
    (bool isOpen, ) = getEscapeHatchStatus();
    require(isOpen, 'Rollup Processor: ESCAPE_BLOCK_RANGE_INCORRECT');
    processRollupProof(proofData, signatures, viewingKeys);
}

Escape hatch использует специальную ZK-схему (escape_hatch_circuit), которая обрабатывает транзакцию join-split: потребляет входные заметки из дерева Меркла и создаёт выходные заметки. Схема должна проверять, что входные заметки существуют в текущем дереве данных (используя old_data_root для проверки членства в дереве Меркла), а затем предоставлять тот же корень в качестве публичного входного значения для проверки L1-контрактом по сравнению с on-chain состоянием.

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

Уязвимость находится в схеме escape hatch (escape_hatch_circuit.cpp). Значение old_data_root превращается в два независимых свидетеля без ограничения равенства, связывающего их.

Первый свидетель (строка 33) передаётся в компонент схемы join-split, где используется для доказательств членства в дереве Меркла для проверки существования входных заметок в дереве данных:

join_split_inputs inputs = {
    // ...
    witness_ct(&composer, tx.js_tx.old_data_root),        // строка 33: первый свидетель
    // ...
};
auto outputs = join_split_circuit_component(composer, inputs);

Второй свидетель (строка 50) создаётся независимо и предоставляется как публичное входное значение (строка 88), которое Solidity-контракт извлекает и проверяет по on-chain корню данных:

auto old_data_root = field_ct(witness_ct(&composer, tx.js_tx.old_data_root));  // строка 50: второй свидетель
// ...
composer.set_public_input(old_data_root.witness_index);    // строка 88: предоставлен как публичное входное значение

В ZK-схеме каждый вызов witness_ct создаёт независимую переменную. Без явного assert_equal между строками 33 и 50 доказывающий может присваивать разные значения этим двум свидетелям. На стороне Solidity validateMerkleRoots проверяет require(oldDataRoot == dataRoot), используя только публичное входное значение из строки 50, без доступа к значению, используемому в строке 33.

Тот же шаблон без привязки существует также для input_owner и output_owner: эти значения засвидетельствованы в строках 38–39 (переданы в join_split_circuit_component для проверки владения) и снова в строках 111–112 (предоставлены как независимые публичные свидетели). Однако мы не обнаружили практического пути эксплуатации этого пробела.

Анализ атаки

Схема escape hatch была удалена из кодовой базы aztec-connect [4], но развёрнутый контракт верификатора по-прежнему содержит ключ верификации EscapeHatchVk, поэтому доказательства, сгенерированные с помощью уязвимой схемы, могут проходить on-chain верификацию. На момент атаки контракт был неактивен 142 дня, и окно escape hatch было открыто. Адрес атакующего был создан всего за 14 часов до эксплойта через Union Chain [2]. Атака состоит из трёх основных шагов:

  • Шаг 1: Атакующий построил поддельное дерево Меркла, содержащее заметки произвольной стоимости, принадлежащие самому атакующему. Эти заметки не существовали в реальном on-chain дереве данных (дереве Меркла, хранящем все действительные заметки).

  • Шаг 2: Атакующий сгенерировал доказательство escape hatch, эксплуатируя несвязанные свидетели, описанные выше. Свидетель строки 33 (используемый для проверки членства в дереве Меркла в компоненте join-split) был установлен на поддельный корень дерева Меркла (проверка членства прошла успешно, поскольку сфабрикованные заметки существовали в поддельном дереве, а проверки владения прошли успешно, поскольку атакующий держал ключи подписи). Свидетель строки 50 (предоставленный как публичное входное значение, проверяемое Solidity) был установлен на реальный on-chain корень данных (проверка Solidity require(oldDataRoot == dataRoot) прошла успешно, поскольку это значение совпадало с сохранённым корнем контракта).

  • Шаг 3: При выполнении обеих проверок — схемы и Solidity — доказательство было успешно верифицировано. Контракт обработал транзакцию escape hatch как легитимную и освободил средства.

Атакующий повторил этот процесс в трёх транзакциях (0x9e1d6a...6b03ca, 0xab306c...59c2b5, 0x5c196c...4705c3), нацеливаясь на разные активы и извлекая 1 158 ETH, 150K DAI и ~0,47 renBTC соответственно, что в сумме составляет около $2,2M.

Заключение

Основной причиной этого инцидента стало отсутствующее ограничение равенства между двумя свидетелями old_data_root в схеме escape hatch. Один свидетель использовался для проверки членства приватных заметок внутри компонента join-split, другой предоставлялся как публичное входное значение, проверяемое Solidity. Без ограничения, связывающего их, атакующий доказал владение сфабрикованными заметками по отношению к поддельному дереву Меркла, тогда как L1-контракт видел действительный on-chain корень. Примечательно, что удаление уязвимой схемы из исходного кода не нейтрализовало уже развёрнутый контракт верификатора — функция escapeHatch в устаревшем RollupProcessor остаётся вызываемой всякий раз, когда открыто её окно по номеру блока.

Для снижения аналогичных рисков в будущем: когда одно и то же логическое значение присутствует в нескольких точках ZK-схемы, все экземпляры должны быть явно ограничены как равные — независимые вызовы witness_ct для одного и того же значения являются пробелом в привязке. Аудиты схем должны систематически проверять, что каждое публичное входное значение связано с внутрисхемным значением, которое оно представляет.

Начните работу с Phalcon Security

Обнаруживайте каждую угрозу, получайте важные оповещения и блокируйте атаки.

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

Ссылки

О BlockSec

BlockSec — поставщик комплексных решений в области безопасности блокчейна и крипто-комплаенса. Мы создаём продукты и услуги, которые помогают клиентам проводить аудит кода (включая смарт-контракты, блокчейн и кошельки), перехватывать атаки в режиме реального времени, анализировать инциденты, отслеживать незаконные средства и выполнять требования AML/CFT на протяжении всего жизненного цикла протоколов и платформ.

BlockSec опубликовал множество работ по безопасности блокчейна на престижных конференциях, сообщил о нескольких атаках нулевого дня на DeFi-приложения, предотвратил несколько взломов, спасая более 20 миллионов долларов, и обеспечил безопасность криптовалют на миллиарды долларов.

Sign up for the latest updates
Потери на сумму около 3,87 млн USD: взломы Autheo Network и UNCX | Еженедельный обзор BlockSec
Security Insights

Потери на сумму около 3,87 млн USD: взломы Autheo Network и UNCX | Еженедельный обзор BlockSec

В этом еженедельном отчёте рассматриваются 3 инцидента безопасности за период 12.10.2026–18.10.2026. Общий оценочный ущерб составил около 3,87 млн USD. Ущерб Autheo Network от компрометации закрытого ключа составил примерно 1,76 млн USD, Qubic Wallet потерял примерно 1,30 млн USD из-за ненадёжной генерации seed, а ошибка учёта в контрактах Smart Token Vesting V2 сети UNCX Network на Ethereum, BNB Chain, Base и Robinhood Chain привела к ущербу примерно в 807 тыс. USD. В подробном анализе инцидента UNCX объясняется, как некорректный расчёт совокупного количества долей смешивал транши вестинга, созданные при разных соотношениях токенов и долей, и позволял выводить из общих пулов больше положенного.

Потеряно около $542 тыс.: эксплуатация уязвимости MakerDaoBot | Еженедельный обзор BlockSec
Security Insights

Потеряно около $542 тыс.: эксплуатация уязвимости MakerDaoBot | Еженедельный обзор BlockSec

В Ethereum за 05–11.10.2026 произошёл 1 инцидент; ущерб — около $542 тыс. Атакующий передал вредоносный адаптер функции MakerDaoBot без контроля доступа. Получив права на балансы MakerDAO, адаптер закрыл 4 аукциона ETH-A, конвертировал 200 ETH-A в WETH; атакующий вывел 200 ETH.

~418 млн $ потеряно: эксплойты Bitget и NEAR Intents | BlockSec Weekly
Security Insights

~418 млн $ потеряно: эксплойты Bitget и NEAR Intents | BlockSec Weekly

Этот отчёт за две недели фиксирует 8 инцидентов с 21 сентября по 4 октября 2026 г., убытки ≈$418,4М в Ethereum, BNB Chain, Solana, Bitcoin, TRON, XRP Ledger, Zcash, Avalanche, NEAR. Разбор: уязвимость продукта безопасности Bitget ($387,5М) и ошибка NEAR Intents ($3,87М).

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit