Ловушка для бота: JaredFromSubway лишился $7,5 млн и не заметил этого


20 июня 2026 года автоматический торговый бот JaredFromSubway, работающий в сети Ethereum, потерял 4 424 $ETH (около $7,5 млн) из-за уязвимости, связанной с механизмом выдачи разрешений на использование цифровых активов. Злоумышленник заранее развернул поддельные пулы для обмена токенов и создал фальшивые цифровые активы, которые выглядели как привлекательные возможности для арбитражной торговли. В результате автоматическая стратегия бота начала взаимодействовать с вредоносными смарт-контрактами и предоставила им разрешения на доступ к своим средствам.
Основная проблема заключалась в том, что после выполнения операций эти разрешения не были полностью использованы или отозваны. Благодаря этому злоумышленник позже смог активировать подготовленные заранее контракты и воспользоваться оставшимися разрешениями, чтобы вывести принадлежащие боту запасы WETH, $USDC и $USDT. В GetBlock AML Research детально восстановили алгоритм атаки на MEV-бота JaredFromSubway.
Что нужно знать о MEV-ботах
Jaredfromsubway.eth представляет собой так называемого MEV-бота (Maximal Extractable Value) — автоматическую программу, которая постоянно отслеживает еще не подтвержденные транзакции в блокчейне и изменяет порядок их исполнения, чтобы получить прибыль.
Основная специализация этого бота — так называемая сэндвич-атака. Ее суть заключается в том, что бот размещает одну сделку непосредственно перед транзакцией пользователя и еще одну сразу после нее, зарабатывая на изменении цены актива, которое вызывает операция жертвы.
Когда пользователь отправляет заявку на обмен криптовалюты через децентрализованную биржу, эта транзакция некоторое время остается видимой в сети до ее окончательного включения в блокчейн. MEV-боты постоянно анализируют такие неподтвержденные операции, моделируют их влияние на ликвидность торговых пулов и пытаются определить ситуации, в которых действия пользователя способны изменить цену актива настолько, чтобы на этом можно было заработать.
Ключевые адреса
В ходе расследования были выделены следующие адреса, участвовавшие в атаке.
| Адреса, используемые злоумышленником для атаки |
| 0x3e37f4A10d771Ba9dE44b6d301410b1BEdeA65d0 |
| 0x74Dc5b93586D248D5Aec64b3586736FF0A0D0e65 |
| 0x71d4416A7A85e08a5Fe7227Ca3B44Fc639e94e97 |
| 0xd8C125efCBc99408eC8723E9BBd81d1E8D39D845 |
| 0xe3Da36E4bd1a5738fa5D6Ef4F0e4dF40bDeB5f17 |
| 0x139bE50D7c0829E0db6E9A444454517e24AbeE7a |
| 0x5aF38735B215b00aa7C9f93fEd7ee415CeCB36e1 |
| 0x19Ff2017803a832eE968454848DAb48fF39C881A |
| 0x7B4F24a4522bc2AEE9D1686FFB7d6c9dAC514e20 |
Вспомогательные контракты
0x81f248ff583d3f8592ea0354a7b8dbe66de40091 — контракт для создания поддельных токенов.
0xEEE172A069f5171b407eBf47e8FCFB637E944ec2 — контракт для управления фальшивой децентрализованной биржей.
Примеры использованных токенов
- Caps — пример поддельного токена.
- fCap — пример поддельного «обернутого» токена.
- Два различных контракта fWETH — поддельные производные токены WETH, использовавшиеся как приманка для получения разрешений.
Тщательная подготовка к атаке
Чтобы заинтересовать MEV-ботов, злоумышленнику требовалось создать искусственную ситуацию, которая выглядела бы как выгодная возможность для арбитражной торговли. Для этого были развернуты несколько вспомогательных смарт-контрактов.
Один контракт отвечал за выпуск поддельных токенов. Второй использовался для координации их состояния и последующего извлечения прибыли. Третий создавал фиктивные торговые пулы.
Четвертый выполнял операции обмена внутри этих пулов. После этого злоумышленник создал 131 контракт поддельных токенов, которые должны были выступать приманкой для автоматических торговых алгоритмов.
Снаружи эти токены выглядели как аналоги популярных «обернутых» активов WETH и $USDC. Их логика была построена таким образом, что пользователь получал один фальшивый токен за каждый внесенный WETH или $USDC через функцию wrapTo().
Для выполнения этой операции требовалось предварительно предоставить контракту разрешение на использование средств пользователя. Именно вокруг этой процедуры и строилась вся схема.
Пока специальный внутренний параметр owner_a_0_19 не был настроен, функция wrapTo() работала совершенно нормально. Она вызывала внутреннюю функцию safeTransferFrom(), переводила средства и полностью использовала выданное разрешение. Со стороны все выглядело абсолютно корректно.
После изменения значения owner_a_0_19 управление переходило специальному координирующему контракту. При этом функция getStatus() начинала возвращать значение, соответствующее текущему номеру блока в сети Ethereum.
Если возвращаемое значение переставало совпадать с номером текущего блока, вызывалась специальная функция, которая сигнализировала злоумышленнику о необходимости обновить статус. Однако если статус уже соответствовал текущему блоку и был равен единице, происходило самое важное изменение.
Функция wrapTo() больше не вызывала transferFrom().
Другими словами, перевод средств фактически не выполнялся, а ранее выданное разрешение на использование токенов оставалось полностью действительным. Одновременно координирующий контракт сохранял информацию обо всех таких неиспользованных разрешениях, постепенно формируя базу для будущего вывода средств. После этого злоумышленник создал несколько поддельных пулов ликвидности.
Часть из них содержала пары WETH–fCAP, а часть — fWETH–fCAP.
Затем через серию специально рассчитанных сделок баланс активов внутри этих пулов был искусственно нарушен таким образом, чтобы со стороны они выглядели как источник выгодной арбитражной возможности.
Как была проведена атака
На блоке 25354425 автоматический бот jaredfromsubway.eth впервые взаимодействовал с одним из подготовленных злоумышленником поддельных пулов SwapV2Pair_0x0e97.
С точки зрения торгового алгоритма ситуация выглядела крайне привлекательной. Если считать, что один токен fWETH эквивалентен одному настоящему WETH, то распределение активов между двумя поддельными пулами создавало очевидную возможность для арбитражной сделки по маршруту:
fWETH → fCAP → WETH
В одном пуле находилось:
- 0,1 fWETH;
- 0,1 fCAP.
Во втором:
- 0,086034434021378528 fCAP;
- 0,116281374898538093 WETH.
Автоматический алгоритм оценил ситуацию как возможность получить прибыль практически без риска и приступил к выполнению сделки.
Для этого бот предоставил разрешение на использование 7 391 033 126 027 264 wei WETH и выполнил операцию «оборачивания» токенов через функцию wrapTo(). Поддельный контракт выпустил эквивалентное количество фальшивых токенов и отправил их в подготовленный пул SwapV2Pair_0x0e97.
После этого бот обменял 6 863 125 844 328 448 wei fCAP и получил взамен 8 566 811 964 473 344 wei WETH. В результате этой операции алгоритм действительно заработал прибыль — около 0,002351557676892158 WETH.
Самое важное заключалось в том, что на этом этапе все происходило абсолютно корректно. Поскольку специальный параметр owner_a_0_19 еще не был активирован, выданное разрешение полностью расходовалось во время выполнения операции. Для торгового бота это стало подтверждением того, что взаимодействие с новыми контрактами безопасно.
После этого злоумышленник не предпринимал никаких действий почти целые сутки. Он позволил боту неоднократно совершать успешные сделки через подготовленную инфраструктуру, постепенно формируя у алгоритма «доверие» к этим контрактам. Для автоматической системы многочисленные прибыльные операции выглядели как доказательство надежности новых торговых возможностей.
Лишь спустя почти день злоумышленник активировал параметр owner_a_0_19, передав управление координирующему контракту. Начиная с блока 25360428, он стал регулярно вызывать функцию markStatusAndTip(), одновременно выплачивая небольшое вознаграждение майнерам (или валидаторам), чтобы его транзакции гарантированно исполнялись раньше операций MEV-бота.
Каждый такой вызов устанавливал значение _getStatus равным номеру текущего блока. Теперь при выполнении функции wrapTo() происходило принципиально иное поведение. Контракт больше не вызывал transferFrom(), а значит, не использовал выданное ранее разрешение на доступ к средствам.
Со стороны торгового алгоритма все выглядело так же, как и раньше. Сделки успешно завершались. Прибыль продолжала поступать. Никаких ошибок не возникало.
Однако каждое новое разрешение, которое бот выдавал контракту, оставалось полностью действительным и не отзывалось после завершения операции. Таким образом злоумышленник постепенно накапливал все больше неиспользованных разрешений на управление активами жертвы. Причем процесс шел очень быстро.
После первых успешных сделок MEV-бот начал практически полностью доверять созданной инфраструктуре и стал выполнять через нее сотни операций в рамках одной транзакции, стремясь извлечь максимальную прибыль.
При этом злоумышленнику понадобилось вызвать функцию обновления статуса всего около десятка раз, чтобы накопить достаточное количество действующих разрешений. Когда нужный объем доступа был получен, наступил заключительный этап атаки.
На блоке 25360696 злоумышленник одновременно активировал 66 подготовленных контрактов-приманок. Из них 60 уже обладали действующими разрешениями, ранее выданными MEV-ботом. Эти контракты сразу же воспользовались сохраненными разрешениями и начали переводить цифровые активы с кошельков бота на адреса злоумышленника.
В чем заключалась уязвимость
Арбитражный бот автоматически выдавал смарт-контрактам разрешения на использование своих токенов при выполнении операций wrapTo(), предполагая, что во время каждой операции эти разрешения будут полностью израсходованы.
Однако алгоритм никогда не проверял, действительно ли разрешение было использовано, и не отзывал оставшиеся права доступа после завершения сделки. Если вредоносный контракт намеренно не вызывал функцию transferFrom(), разрешение продолжало действовать бессрочно.
Это означало, что спустя любое время злоумышленник мог воспользоваться ранее полученным доступом и беспрепятственно вывести принадлежащие боту средства. Именно эта логическая ошибка стала основной причиной потери миллионов долларов.
Движение похищенных средств
Основной адрес злоумышленника 0x3e37f4A10d771Ba9dE44b6d301410b1BEdeA65d0 смог вывести:
- 2 869 812,43 $USDC;
- 2 032 769,62 $USDT;
- 1 474 WETH (примерно $2,57 млн на момент атаки).
После этого все полученные стейблкоины были обменяны на $ETH. Затем средства были распределены между пятью различными адресами:
| Адреса, используемые для вывода похищенных активов |
| 0x5aF38735B215b00aa7C9f93fEd7ee415CeCB36e1 |
| 0x71d4416A7A85e08a5Fe7227Ca3B44Fc639e94e97 |
| 0x74Dc5b93586D248D5Aec64b3586736FF0A0D0e65 |
| 0xe3Da36E4bd1a5738fa5D6Ef4F0e4dF40bDeB5f17 |
| 0xd8C125efCBc99408eC8723E9BBd81d1E8D39D845 |

Схема распределения похищенных активов между адресами. Визуализация: CertiK
На заключительном этапе злоумышленник отправил 3 000 $ETH в сервис Tornado Cash, предназначенный для сокрытия происхождения криптовалютных средств. Кроме того, еще 1 422 $ETH были обменяны примерно на 2,44 млн DAI. На момент публикации статьи эти средства находятся на адресе 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.
