February 14

DeFi. Безопасность. Взлом Aperture Finance & SwapNet

DeFi. Взломы

Перевод

Это вольный перевод отчёта. См. оригинал: https://blocksec.com/blog/17m-closed-source-smart-contract-exploit-arbitrary-call-swapnet-aperture

Введение

25 января 2026 года мы обнаружили серию подозрительных транзакций, нацеленных на контракты-жертвы, развёрнутые SwapNet и Aperture Finance в сетях Ethereum, Arbitrum, Base и BSC, с совокупными потерями, превышающими $17 млн.

(Если оценивать высокоуровнево) первопричина обоих инцидентов проста и уже была обозначена в наших первоначальных алертах [1, 2]: контракты-жертвы предоставляют возможность произвольного вызова из-за недостаточной валидации входных данных, что позволяет злоумышленникам злоупотреблять существующими токенными разрешениями (approvals) и вызывать transferFrom для вывода активов.

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

Более того, последующие пост-мортемы, опубликованные пострадавшими проектами [3, 4], в основном сосредоточены на мерах по устранению последствий и восстановлению, уделяя ограниченное внимание базовым техническим деталям.

В результате остаётся ряд неотвеченных вопросов, включая то, как были сконструированы уязвимые пути вызовов и почему существующие проверки не смогли предотвратить эксплуатацию.

В данном отчёте мы приводим более детальный технический анализ на основе декомпилированного байткода и ончейн-трасс исполнения. Хотя отсутствие исходного кода ограничивает видимость, анализ на уровне байткода достаточен для реконструкции уязвимой логики и выявляет несколько интересных наблюдений о дизайне контрактов, которые не очевидны из высокоуровневых алертов.

Мы начинаем с глубокого разбора инцидента SwapNet, после чего приводим детальный анализ инцидента Aperture Finance.

Инцидент SwapNet

Контекст

SwapNet [5] - это DEX-агрегатор, предназначенный для поиска оптимальных маршрутов свопов путём агрегирования ликвидности из нескольких ончейн-источников, включая AMM и приватных маркет-мейкеров. Протокол также позволяет пользователям указывать кастомные роутеры или пулы при выполнении свопов, обеспечивая дополнительную гибкость.

Анализ первопричины

Данный инцидент обусловлен недостаточной валидацией пользовательских входных данных, что позволяет атакующему инициировать вызовы transferFrom() с произвольными параметрами.

В результате активы, которые ранее были одобрены (approved) для контрактов-жертв (например, 0x616000e384Ef1C2B52f5f3A88D57a3B64F23757e), могли быть переведены злоумышленнику.

Согласно декомпилированному байткоду, функция 0x87395540() не содержит корректной проверки критически важных входных параметров. Подменяя ожидаемый адрес роутера или пула адресом токена (например, USDC), контракт ошибочно рассматривает токен как допустимую цель исполнения. Это приводит к выполнению низкоуровневого вызова с calldata, контролируемым атакующим.

Код 00
Код 01

В результате контракт-жертва выполняет вызовы вида
approvedAsset.transferFrom(victim, attacker, amount),
что позволяет атакующему вывести (siphon) все активы, на которые ранее было выдано разрешение (approval).

Ход атаки

Против SwapNet было зафиксировано несколько атак. В качестве примера здесь используется транзакция в сети Base:
0xc15df1d131e98d24aa0f107a67e33e66cf2ea27903338cc437a3665b6404dd57.

Атакующий просто вызвал функцию 0x87395540() контракта-жертвы, передав вредоносные входные данные. Этот вызов состоит из двух основных этапов.

1. Ключевая внутренняя переменная (например, v51) была установлена в значение USDC, что позволило обойти предусмотренную логику маршрутизации.

Код 02

Был выполнен низкоуровневый вызов с calldata, контролируемой атакующим, в результате чего был вызван USDC.transferFrom(), и все одобренные (approved) USDC были выведены.

Код 03

Сводка потерь, транзакции и затронутые контракты

Инцидент с SwapNet привёл к ориентировочным потерям в размере около $13,41M в нескольких сетях. В таблицах ниже приведено резюме ключевых транзакций эксплойта и адресов задействованных контрактов-жертв.

Код 04
Код 05

Инцидент Aperture Finance

Контекст

Aperture Finance [6] - DeFi-протокол, который управляет позициями концентрированной ликвидности, такими как LP-позиции Uniswap V3, от имени пользователей. Его закрытые смарт-контракты (например, 0xD83d960deBEC397fB149b51F8F37DD3B5CFA8913) позволяют пользователям минтить и управлять позициями Uniswap V3, используя нативные токены.

Предполагаемый рабочий процесс минтинга позиций Uniswap V3

При создании (mint) позиций Uniswap V3 через функцию 0x67b34120() контракт следует ожидаемому трёхэтапному процессу:

  1. Обёртывание нативных токенов
  2. Своп нативных токенов через внутреннюю функцию 0x1d33()
  3. Минт позиций Uniswap V3

Проблема возникает на шаге 2: функция 0x1d33() выполняет кастомизированный своп через низкоуровневый вызов, при котором критически важные параметры (например, цель вызова и calldata), по всей видимости, контролируются пользователем и проверяются недостаточно строго, что позволяет выполнять непреднамеренные внешние вызовы. Более подробная информация приводится в следующих разделах.

Анализ первопричины

Аналогично случаю со SwapNet, инцидент Aperture Finance обусловлен недостаточной валидацией входных данных при низкоуровневых вызовах. При вызове 0x67b34120() внутренняя функция 0x1d33() выполняет низкоуровневый вызов, используя calldata, предоставленную пользователем, не накладывая жёстких ограничений на цель вызова или селектор функции.

Код 06

Как показано на рисунке ниже, calldata, используемая для инициирования низкоуровневого вызова, полностью формируется на основе входных данных, переданных атакующим.

Код 07

Это позволяет атакующим сконструировать вредоносную calldata, в результате чего выполняется approvedToken.transferFrom(victim, attacker, amount)
в контексте контракта-жертвы. В итоге могут быть выведены не только ERC-20 токены, но и одобренные NFT-позиции Uniswap V3.

Ход атаки

Против Aperture Finance было зафиксировано несколько атак. В этом разделе в качестве репрезентативного примера используется транзакция в сети Ethereum:
0x8f28a7f604f1b3890c2275eec54cd7deb40935183a856074c0a06e4b5f72f25a.

Атакующий создал атакующий контракт:
0x5c92884dFE0795db5ee095E68414d6aaBf398130.

Этот контракт вызвал функцию 0x67b34120() с вредоносными входными данными и отправил 100 wei ETH (то есть msg.value == 100).

a) Нативный ETH был обёрнут в WETH посредством вызова функции WETH.deposit().

Код 08

b) Была вызвана внутренняя функция 0x1d33() для выполнения низкоуровневого вызова. На этом этапе в контексте контракта-жертвы выполняется вызов
WBTC.transferFrom(victim, attacker, amount), что позволяет атакующему вывести (siphon) одобренные токены.

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

Код 09

В завершение была вызвана функция NonfungiblePositionManager.mint() для минта позиции в пользу атакующего с использованием 100 wei WETH.

Интересные наблюдения

Сравнивая нормальные и аномальные транзакции минта, мы заметили, что в обоих случаях токены были одобрены (approved) одному и тому же спендеру (например, OKX DEX: TokenApprove), но при этом указывались разные адреса роутеров (то есть DexRouter и WBTC).

Это указывает на то, что контракт, вероятно, осуществляет проверку спендера в approve, но не валидирует фактическую цель исполнения вызова, оставляя критическую брешь, которую можно эксплуатировать через произвольные (arbitrary) вызовы.

Обычная транзакция:
https://app.blocksec.com/phalcon/explorer/tx/eth/0xc823b703c716fa9078e1d71714b734557bd540ddd1e41590dd73da7c5aba0200

Код 10

Аномальная транзакция: https://app.blocksec.com/phalcon/explorer/tx/eth/0x8f28a7f604f1b3890c2275eec54cd7deb40935183a856074c0a06e4b5f72f25a

Код 11

Сводка потерь, транзакции и затронутые контракты

Инцидент с Aperture Finance привёл к ориентировочным совокупным потерям в размере около $3,67 млн в нескольких сетях. В таблицах ниже приведено краткое описание ключевых транзакций эксплойта и соответствующих адресов контрактов-жертв.

Данные 1
Данные 2

Заключение

Хотя инциденты SwapNet и Aperture Finance затронули разные протоколы и сети, базовая проблема в обоих случаях не является сложной:

низкоуровневые вызовы, контролируемые пользователем, в сочетании с недостаточной валидацией входных данных в контрактах, на которые выданы токенные разрешения (approvals)

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

До!