Back to Blog

Еженедельный обзор инцидентов безопасности Web3 | 6 апреля – 12 апреля 2026 г.

Code Auditing
April 15, 2026
13 min read
Key Insights

За прошедшую неделю (06.04.2026 - 12.04.2026) компания BlockSec зафиксировала и проанализировала четыре инцидента, связанных с атаками, общий оценочный ущерб от которых составил примерно $928,6 тыс. В таблице ниже представлены краткие сведения об этих инцидентах, а подробный анализ каждого случая приведен в следующих подразделах.

Дата Инцидент Тип Оценочный ущерб
05.04.2026* Инцидент с Denaria Асимметрия округления и
небезопасное приведение типов
~$165,6 тыс.
07.04.2026 Инцидент с токеном HB Ошибка бизнес-логики и
манипуляция ценой
~$193 тыс.
07.04.2026 Инцидент с Squid Multicall Произвольные вызовы ~$517 тыс.
11.04.2026 Инцидент с XBIT Проблема контроля доступа ~$53 тыс.

*Инцидент с Denaria не был включен в отчет на прошлой неделе и добавлен сейчас для полноты картины.

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

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

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


Еженедельный обзор: Инцидент с Denaria

Бессрочный DEX Denaria представил новые механизмы учета, подкрепленные сложной логикой кода. Однако рефакторинг после аудита привел к асимметрии округления, что могло создать редкий граничный случай — отрицательное значение. В конечном итоге это привело к небезопасному приведению типов и позволило осуществить астрономический вывод средств.

5 апреля 2026 года бессрочный DEX Denaria в сети Linea подвергся атаке, повлекшей убытки на сумму около $165,6 тыс. Первопричиной стали две усугубляющие друг друга ошибки в функции getLpLiquidityBalance(): рефакторинг учета баланса LP после аудита внес асимметрию округления, которая могла приводить к небольшому отрицательному промежуточному значению, а небезопасное приведение типов (cast) из int256 в uint256 «тихо» преобразовывало это отрицательное число в почти максимальное целое число без знака вместо того, чтобы вызвать откат (revert). Атакующий воспользовался этим посредством одностороннего депозита LP и длинной позиции (long trade), а затем вывел искусственно завышенную прибыль из хранилища (vault).

Предыстория

Denaria — это бессрочный DEX на Linea, построенный на базе динамического виртуального AMM. Он разделяет торговлю и расчеты на два компонента. Рынок PerpPair управляет позициями пользователей и учетом LP, а Vault хранит обеспечение и производит расчеты реализованной прибыли/убытков (PnL).

Владение LP в PerpPair не представлено явным балансом токенов. Вместо этого протокол восстанавливает состояние каждого LP из матрицы внутреннего учета, состоящей из двух компонентов: стороны активов и стороны стабильных монет. Сторона активов отслеживает долю LP в подверженности пула колебаниям цен, а сторона стабильных монет — его долю в стоимости пула, номинированной в стейблкоинах. Вместе эти два компонента определяют, как отслеживается и рассчитывается стоимость активов LP.

Важно отметить, что эти компоненты не хранятся в виде статических снимков. Каждый раз, когда происходит сделка, PerpPair обновляет свое глобальное ликвидное состояние, и восстановленный баланс каждого LP вычисляется как разница между совокупными глобальными значениями и снимком точки входа LP. Этот механизм учета был введен в ходе рефакторинга после аудита, который заменил оригинальный накопитель на основе долей на прямое отслеживание баланса. Однако путь вычитания в рефакторинге внес асимметрию округления, которая могла привести к тому, что восстановленные компоненты LP становились слегка отрицательными при определенных последовательностях сделок.

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

Уязвимый контракт PerpPair (0xb683...36ae17) содержит две усугубляющие ошибки.

  1. Асимметрия округления в рефакторизованном учете. Рефакторинг после аудита, который внедрил прямое отслеживание баланса, создал асимметрию округления в пути реконструкции вычитанием. При определенных последовательностях сделок глобальное совокупное значение после округления могло стать незначительно меньше, чем снимок точки входа LP, из-за чего вычитание давало небольшой отрицательный результат вместо нуля. Теоретически это значение должно было быть нулевым или близким к нулю; асимметрия была ошибкой, специфичной для этой реализации, а не встроенным свойством вычитающего учета в целом.
  1. Небезопасное приведение типов (signed-to-unsigned cast). В функции getLpLiquidityBalance() восстановленный компонент баланса LP приводился из типа int256 к uint256 без проверки. В Solidity приведение отрицательного int256 к uint256 не вызывает откат; значение преобразуется по модулю 2^256, превращая небольшое отрицательное число, например -1, в почти максимальное целое число без знака (2^256 - 1). Поскольку этот восстановленный баланс подавался в calcPnL() для расчетов, любой отрицательный вход интерпретировался как огромная позиция LP, позволяя атакующему реализовать фиктивно завышенную прибыль и опустошить хранилище.

Ни одна из этих ошибок по отдельности не была бы эксплуатируемой. Асимметрия округления приводила лишь к слегка отрицательному значению, которое при обычной арифметике int256 представляло бы собой незначительную погрешность. Но как только это отрицательное значение попадало в незащищенное приведение типа, оно превращалось в почти максимальное целое число. Последующая проверка границ ограничивала это значение общей ликвидностью пула по стороне активов, по сути преобразуя переполнение в претензию на всю сторону активов рынка.

Анализ атаки

Следующий анализ основан на транзакции атаки 0xcb0744...0c606447.

  • Шаг 1: Атакующий взял флэш-кредит на 60 000 USDC через Aave.

  • Шаг 2: Вспомогательный адрес внес 30 000 USDC в качестве обеспечения и добавил позицию LP только в стейблкоинах в размере 19 980 токенов. Внося средства только на стабильную сторону, вспомогательный адрес гарантировал, что его LP-компонент по стороне активов начинался с нуля, что делало его более подверженным уходу в отрицательную область из-за округления.

  • Шаг 3: Второй вспомогательный адрес внес 15 000 USDC и открыл длинную позицию номиналом 100 000, что вызвало обновление liquidityM, приведшее к ключевой ошибке округления. Большой размер номинала относительно ликвидности пула усилил влияние округления на сделку, опустив восстановленный баланс активов первого вспомогательного адреса чуть ниже нуля.

  • Шаг 4: Затем первый вспомогательный адрес вызвал realizePnL(). Во время реконструкции баланса LP отрицательный lpAssetBalance превратился в почти максимальное uint256. Это значение было ограничено общей ликвидностью рынка по стороне активов, что сгенерировало сильно завышенную прибыль. Фактически протокол «поверил», что LP владеет всеми активами рынка.

  • Шаг 5: Атакующий вывел завышенную прибыль из хранилища.

Эта схема повторялась до тех пор, пока оставшаяся ликвидность хранилища не была исчерпана. После погашения флэш-кредита атакующий получил прибыль в размере около $165,6 тыс.

Заключение

Эта атака в конечном счете была вызвана отсутствием проверки границ при преобразовании знакового числа в беззнаковое. Решение очевидно: заменить прямое приведение на SafeCast.toUint256(), которое вызывает откат при отрицательном значении вместо его «заворачивания».

В более широком смысле, этот инцидент иллюстрирует риск рефакторинга кода после аудита, который проходит без внешнего контроля. Согласно официальному отчету об инциденте, асимметрия округления была внедрена после завершающего внешнего аудита, когда команда заменила оригинальный накопитель на прямое отслеживание баланса. Хотя рефакторинг решил проблему переполнения, он создал новый граничный случай, который не был замечен внутренним тестированием. При рефакторинге основной логики учета новые пути кода должны рассматриваться как критически важные для безопасности и проходить повторный аудит, особенно когда восстановленные значения напрямую влияют на расчеты прибыли или логику вывода средств.

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

Погрузитесь в детали транзакций для безопасных действий

Попробовать сейчас бесплатно

Инцидент с токеном HB

7 апреля 2026 года токен HB, кастомный ERC-20 с встроенными хуками покупки/продажи в сети BNB Chain, был атакован, что привело к убыткам около $193 тыс. Первопричиной стала ошибочная логика расчета вознаграждений: при срабатывании вызывалась функция swapBack(), которая удаляла резервы HB напрямую из пары PancakeSwap, а затем вызывала sync() для переоценки пула. После того как атакующий выкупил большую часть HB из пары, последующее выполнение swapBack() еще больше сократило оставшуюся ликвидность и резко повысило спотовую цену. Затем атакующий продавал крошечные суммы HB в искаженный пул за непропорционально большие суммы USDT, повторяя цикл, пока пара не была опустошена.

Предыстория

HB — это кастомный токен стандарта ERC-20 в сети BNB Chain, имеющий хуки покупки и продажи в функции _transfer(). Когда пользователи покупают у AMM-пары, _handleBuy() записывает данные о базисе стоимости. При продаже _handleSell() перенаправляет логику в зависимости от состояния пары на разные пути налогообложения и расчетов.

В токен также включен механизм расчета вознаграждений, который может запускать swapBack(). Вместо совершения обычного свопа через маршрутизатор, swapBack() переводит HB напрямую из пары PancakeSwap на адрес PROOF, а затем принудительно синхронизирует пару через sync(). Это позволяет контракту сокращать резервы HB в паре вне обычных торговых потоков AMM и немедленно повышать цену актива.

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

Основная ошибка в контракте токена HB (0x62ce...87a4b0) заключалась в том, что swapBack() получал вознаграждения путем прямого изъятия токенов из AMM-пары, а не из казначейства или через своп с участием маршрутизатора. Поскольку swapBack() был доступен через путь расчета вознаграждений, неторговый путь кода мог напрямую менять резервы пары и искажать спотовую цену.

Когда резервы HB в паре низки, вызов swapBack() еще сильнее сокращает остаток HB и усиливает ценовое искажение, что позволяет продавать крошечные объемы HB по экстремально завышенным ценам.

Анализ атаки

Следующий анализ основан на транзакции атаки 0x19671f...d71594ed.

  • Шаг 1: Атакующий взял крупные суммы в долг в протоколе Venus.

  • Шаг 2: Атакующий перевел около 1 496 HB на контракт токена, увеличив его баланс, чтобы последующий вызов swapBack() мог изъять из пары больше токенов.

  • Шаг 3: Переведя крошечное количество HB в пару PancakeSwap, атакующий запустил _swapAndLiquify(), который обменял примерно 4 163 HB, хранящихся на контракте токена, на 10 USDT и увеличил право атакующего на вознаграждение в HB.

  • Шаг 4: Затем атакующий потратил 72 117 360 USDT на покупку 73 608 753 HB, оставив в паре очень мало ликвидности HB.
  • Шаг 5: Далее атакующий инициировал путь расчета вознаграждений. Для покрытия вознаграждений токен вызвал swapBack(), который изъял дополнительные HB из пары PancakeSwap и принудительно вызвал sync(), резко взвинтив цену HB.
  • Шаг 6: Атакующий напрямую перевел USDT в пару, чтобы восполнить резервы USDT, а затем продал всего 0,000582 HB за 37 582 322 USDT по искаженной цене.

Повторяя шаг 6 и продавая токены HB по искаженной цене, атакующий вывел из пула почти все USDT.

Заключение

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


Инцидент с Squid Multicall

7 апреля 2026 года пользователь Squid потерял около $517 тыс. в разных сетях из-за инцидента, связанного с разрешением (approval). Пользователь по ошибке предоставил разрешение (approve) контракту SquidMulticall вместо предназначенного для этого Squid Router. Это позволило атакующему вызвать не имеющую прав доступа функцию SquidMulticall.run() для выполнения произвольных внешних вызовов. Таким образом, атакующий смог использовать любые права, делегированные контракту, для совершения вызовов transferFrom() против пользователей, предоставивших ему такое разрешение.

Предыстория

В стандартном рабочем процессе Squid пользователи должны предоставлять разрешение Squid Router, в то время как SquidMulticall выступает только как вспомогательный контракт для исполнения. Вспомогательный контракт предназначен для выполнения пакетных вызовов в рамках логики маршрутизации, но он не должен быть тем контрактом-получателем (spender), которому пользователи напрямую дают доступ к токенам.

Поскольку проверки лимитов (allowance) стандарта ERC-20 выполняются только для адреса получателя, любой контракт, который сочетает пользовательские разрешения с возможностью совершения произвольных вызовов без ограничений, создает опасную «черную дыру»: после одобрения контракт становится вектором для вывода токенов, если кто-то может контролировать, какие именно вызовы он совершает.

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

Этот инцидент был вызван не уязвимостью в смарт-контракте. Убытки стали результатом сочетания двух факторов: ошибочного одобрения контракта SquidMulticall вместо маршрутизатора и отсутствия ограничений доступа в функции run(), которая принимает произвольные цели и calldata от любого вызывающего.

SquidMulticall задуман для выполнения пакетных вызовов как конечный этап потока в маршрутизаторе, где входные данные формируются доверенной логикой. При использовании по назначению этот дизайн без ограничений не несет риска. Но ошибочное одобрение полностью меняет ситуацию: MEV-бот обнаружил действующее разрешение, вызвал run() со специально сформированными calldata для совершения transferFrom(victim, attacker, amount) и вывел разрешенные токены во всех сетях без каких-либо дальнейших действий со стороны жертвы.

Анализ атаки

Инцидент затронул пользователя в сетях BNB Chain, Arbitrum, Optimism, Avalanche и Base. Анализ основан на транзакции 0x81d0c4...9b1301e9.

  • Шаг 1: Жертва (0xacc0...f40e98) по ошибке дала одобрение SquidMulticall вместо нужного Squid Router.

  • Шаг 2: MEV-бот обнаружил этот лимит и вызвал SquidMulticall.run() с подготовленными calldata.

  • Шаг 3: Через произвольный вызов SquidMulticall инициировал transferFrom(victim, attacker, amount) и перевел доступные активы с кошелька жертвы.

Заключение

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


Инцидент с XBIT

11 апреля 2026 года токен XBIT в сети BNB Chain был атакован, что привело к убыткам около $53 тыс. Первопричиной стала ошибка открытого контроля доступа (fail-open access control) в контракте XBITVault: проверка авторизации в функции transfer() была условной — она требовала msg.sender == xbitContract только если xbitContract был не нулевым, и молча пропускала вызов в противном случае. Поскольку функция bindXBIT() для инициализации контракта никогда не вызывалась, эта уязвимость была доступна постоянно, позволяя любому внешнему вызывающему перемещать балансы XBIT с любого адреса, включая пару PancakeSwap XBIT/USDT. Атакующий использовал это для изъятия XBIT из пары, а затем постоянно продавал крошечные суммы XBIT обратно в пул за непропорционально крупные суммы USDT.

Предыстория

XBITVault — это не пассивный казначейский контракт, а внутренний бэкенд для системы токенов XBIT, предоставляющий функции вроде transfer(), approve() и mintForXBIT(). По замыслу владельцы должны сначала вызвать bindXBIT() для инициализации хранилища, установив xbitContract, pancakePair, pairContract и xbitDecimals. После инициализации функции, меняющие состояние, должны быть доступны только для привязанного контракта XBIT. Другими словами, модель безопасности хранилища зависит от успешной инициализации перед публичным использованием.

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

Критическая ошибка заключается в условности контроля доступа в уязвимом контракте XBITVault (0xc879...42391a). Функция transfer() проверяет соответствие msg.sender == xbitContract только когда xbitContract != address(0). Это означает, что функция не вызывает откат, когда адрес не задан, и вместо этого становится публично доступной для всех. Поскольку балансы хранятся в _balances, любой внешний пользователь может переместить XBIT с любого адреса на любой другой, при условии, что исходный адрес имеет достаточный баланс.

Планируемым путем инициализации был bindXBIT(), но из-за того, что он не был вызван, хранилище оставалось в неинициализированном состоянии с «открытой» защитой. По сути, это предоставило возможность произвольного перевода балансов любому желающему.

Анализ атаки

Следующий анализ основан на транзакции атаки 0xbc877f...4df1b694.

  • Шаг 1: Через функцию transfer() без ограничения доступа атакующий вывел 1 526 216,569 XBIT из пары XBIT/USDT, не предоставив взамен USDT.

  • Шаг 2: Атакующий вызвал sync() для сжатия резервов XBIT в паре до всего 1-2 единиц.

  • Шаг 3: После того как в паре почти исчезла ликвидность XBIT, атакующий неоднократно продавал XBIT, выкачивая около 53 112 USDT из пары.

Заключение

Этот инцидент был вызван проверкой доступа, зависящей от инициализации, которая работала по принципу «безопасность по умолчанию отключена» (fail-open). Функция transfer() была доступна всем, пока xbitContract был не задан, а из-за того, что bindXBIT() никогда не вызывалась, хранилище на постоянной основе предоставляло публичный примитив для произвольного перевода балансов. Привилегированные функции должны блокировать вызовы до завершения инициализации, а шаги по привязке параметров при развертывании должны быть обязательными требованиями в блокчейне, а не просто операционными допущениями.

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

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

Попробовать сейчас бесплатно

О компании BlockSec

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

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

Best Security Auditor for Web3

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

BlockSec Audit