За прошедшую неделю (2026/09/07 - 2026/09/13) мы зафиксировали 2 инцидента безопасности с общим оценочным ущербом около $320M.
| Дата | Инцидент | Тип | Оценочный ущерб |
|---|---|---|---|
| 2026/09/06 * | Liquid Network | Некорректное построение ключа кэша | ~$320M |
| 2026/09/11 | Symbiosis | Некорректная проверка офчейн-депозита | ~$770K |
*Инцидент Liquid Network произошёл 6 сентября и не был освещён в отчёте за прошлую неделю. Он включён сюда для полноты картины.
Лучший аудитор безопасности для Web3
Проверьте дизайн, код и бизнес-логику перед запуском
Главное событие недели: Liquid Network
Инцидент Liquid Network был выбран из-за тонкого механизма коллизии ключа кэша и значительных потерь, которые он вызвал. Составив транзакцию, данные проверки которой заново разбивали байты более ранней транзакции, злоумышленник добился того, что для доказательства, которое ни разу не проверялось, был возвращён кэшированный результат проверки.
6 сентября 2026 года Liquid Network, сайдчейн Bitcoin, подвергся эксплойту на сумму около $320M [1]. Коллизия в кэше проверки rangeproof в Elements, программном обеспечении узла, на котором работает Liquid, позволила вердикту valid, зафиксированному для одного выхода, быть возвращённым для второго, совершенно другого выхода, доказательство которого, таким образом, было принято без какой-либо проверки. Злоумышленник использовал это для создания 4000 L-BTC, не обеспеченных биткоином, а затем вывел их через обычный механизм peg-out сети. На следующий день 3400 BTC были возвращены федерации, оставив у злоумышленника около 598.5 BTC [2].
Предыстория
Liquid Network — это сайдчейн Bitcoin, построенный на Elements, платформе блокчейна с открытым исходным кодом, производной от кодовой базы Bitcoin. Он существует для того, чтобы перемещать биткоин быстрее, дешевле и приватнее, чем позволяет основная цепь. Биткоин пересекает границу между двумя сетями через двустороннюю привязку (peg): при peg-in пользователь блокирует BTC у федерации сети — фиксированной группы проверенных организаций, совместно хранящих средства привязки — и получает эквивалентное количество L-BTC; при peg-out пользователь уничтожает L-BTC, а федерация высвобождает соответствующий BTC. Блоки Liquid не майнятся. Ротируемый набор функциональных узлов федерации предлагает их, а порог подписей федерации финализирует их.
Как и Bitcoin, Liquid отслеживает средства как непотраченные выходы транзакций (UTXO), а не как балансы счетов. Транзакция называет существующие непотраченные выходы, разблокирует их и создаёт новые, причём создаваемая ценность должна равняться потребляемой. Каждый выход несёт скрипт блокировки — scriptPubKey. Выход, скрипт которого начинается с OP_RETURN, никогда не может быть потрачен; он существует для передачи данных, и peg-out выражается именно таким типом выхода.
Liquid также по умолчанию скрывает суммы. Вместо того чтобы записывать цифру в выход, он записывает обязательство Педерсена (Pedersen commitment) для этой цифры. Обязательства аддитивны, поэтому узел может подтвердить, что входы и выходы транзакции сбалансированы, не узнав ни одного из задействованных значений. Это свойство работает в обе стороны: обязательство может точно так же скрывать значение, ведущее себя как отрицательное, что позволило бы выходам транзакции превысить входы и всё равно оставаться сбалансированными. Поэтому каждый выход, сумма которого скрыта, несёт rangeproof — доказательство того, что зафиксированная сумма лежит в диапазоне [0, 2^64). Проверка rangeproof затратна, а один и тот же выход проверяется более одного раза — когда транзакция попадает в мемпул, и снова когда она приходит в блоке — поэтому Elements ведёт кэш уже принятых доказательств, ключом для которого служат само доказательство и данные, по которым оно проверялось.
Анализ уязвимости
Дефект заключается в том, как Elements — программное обеспечение узла, которое запускает каждый участник Liquid — кэширует уже проверенные rangeproof.
CachingRangeProofChecker::VerifyRangeProof() вычисляет запись кэша для находящегося перед ним выхода и, в случае совпадения, немедленно возвращает успех, не трогая само доказательство:

Эта запись формируется функцией ComputeEntryRangeProof(), которая последовательно записывает четыре поля в единый поток SHA-256 и финализирует дайджест:

Четыре поля — это само доказательство rangeproof, обязательство Педерсена для суммы, генератор актива, идентифицирующий, какой актив содержит выход, и scriptPubKey, включённый для того, чтобы принятое доказательство было привязано к конкретному скрипту блокировки.
Ни одно из четырёх полей не несёт префикса длины или разделителя. Rangeproof и scriptPubKey имеют переменную длину; обязательство Педерсена и генератор актива всегда представляют собой 33-байтовые точки. Поэтому дайджест фиксирует только конкатенированные байты, без каких-либо меток, отмечающих, где заканчивается одно поле и начинается следующее. Два разных набора из четырёх полей, которые случайно конкатенируются в одну и ту же последовательность байтов, дают одну и ту же запись, и та проверка, которая пройдёт первой, оставляет вердикт, который заберёт другая. Хешер инициализируется случайной солью, уникальной для каждого узла, но эта соль добавляется в начало обоих потоков одинаково, так что она меняет дайджест, но не различает два входных значения. Дефект был устранён коммитом 94000967 [3].
Анализ атаки
Приведённый ниже анализ основан на транзакциях 271147...187ec5 и f24a4b...0a183f, обе были приняты узлами, работающими на уязвимом коде.
- Шаг 1: Злоумышленник опубликовал транзакцию, первый выход которой является
OP_RETURN, несущим 67 байт данных.
Её scriptPubKey выглядит как 6a 43 — опкод OP_RETURN, за которым следует пуш из 67 байт, — затем два 33-байтовых значения и завершающий 6a. Эти 67 отправленных в стек байт не являются данными, нужными этой транзакции. Это обязательство по значению (value commitment) и генератор актива выхода, который ещё не существовал. Проверка этого выхода сохранила вердикт valid под записью для его собственных четырёх полей.
- Шаг 2: Затем злоумышленник отправил инфляционную транзакцию. Её выход
OP_RETURNимеетscriptPubKey, состоящий из единственного байта6a, а его обязательство по значению в точности равно 33-байтовому значению, вложенному в пуш предыдущей транзакции. Поле rangeproof было построено из rangeproof, обязательства и генератора актива предыдущей транзакции, за которыми следуют два байта6a 43.

Таким образом, четыре поля этого выхода конкатенируются в ту же последовательность байтов, что и у первой транзакции, но разделённую по-другому:
Primer: P0 │ C0 │ X │ S0, where S0 = 6a 43 <33-byte C1> <33-byte X> 6a
Inflation: P1 │ C1 │ X │ S1, where P1 = P0 ‖ C0 ‖ X ‖ 6a 43 and S1 = 6a
Both concatenate to: P0 ‖ C0 ‖ X ‖ 6a 43 ‖ C1 ‖ X ‖ 6a
Кэш вернул более ранний вердикт, а P1 так и не был проверен — это вообще не rangeproof. C1 фиксирует небольшую отрицательную сумму. Другой выход транзакции, обычный выход pay-to-witness-public-key-hash на адрес злоумышленника ex1q7kg...qa2w, нёс крупное положительное обязательство с настоящим rangeproof, и эти два значения компенсировали друг друга, так что транзакция сбалансировалась. В итоге появилось 4000 непотраченных L-BTC, не обеспеченных биткоином.
- Шаг 3: Злоумышленник осуществил peg-out. Две транзакции сожгли
3,996.01834922и2.65138358L-BTC через обычные выходы peg-outOP_RETURN, всего3,998.66973280L-BTC. Функциональные узлы, авторизующие peg-out, проверили их с помощью того же уязвимого кода и высвободили биткоин:3,995.99999857BTC и2.49749857BTC поступили злоумышленнику в сети Bitcoin, всего3,998.49749714BTC.
Сеть приостановила работу и разделилась на две цепи на время урегулирования инцидента [4], а затем возобновила работу с отклонённым недействительным peg-out [2]: обозреватель блоков сегодня показывает подтверждённую первичную транзакцию, но не инфляционную транзакцию, хотя высвобожденный ею биткоин уже покинул кошелёк федерации. На следующий день в 16:09:25 UTC злоумышленник вернул 3,400.00000000 BTC в кошелёк привязки федерации, оставив себе оставшиеся ~598.5 BTC [2], в то время как обе стороны вели переговоры через сообщения, встроенные в транзакции Bitcoin [4].
Заключение
Злоумышленник не взломал никакой криптографии и не скомпрометировал никакой ключ. Дефект заключался в том, как кэш rangeproof идентифицировал то, что уже было проверено. Его ключ конкатенировал четыре поля — два из которых имели переменную длину — в один поток хеширования без каких-либо меток, отмечающих, где заканчивается каждое поле, так что второй набор полей мог заново разбить те же байты и попасть в ту же запись. Сохранённый вердикт valid затем возвращался для доказательства, которое ни один узел никогда не проверял, а как только rangeproof перестаёт ограничивать сумму за собой, конфиденциальный реестр теряет единственное, что удерживало его скрытые значения неотрицательными. Один сконструированный выход превратился в 4000 L-BTC, а двусторонняя привязка превратила их в биткоин в основной цепи.
Любой идентификатор, производный от более чем одного входного значения переменной длины, должен фиксировать границы между ними — либо путём добавления к каждому полю префикса длины, либо путём хеширования полей в отдельные слоты фиксированного размера. Без этого получение производного значения не является инъективным, и два разных входа могут быть ошибочно приняты за один. Это требование наиболее критично там, где кэш заменяет собой проверку, поскольку в этом случае попадание в кэш — это решение её не выполнять.
Бесплатное сканирование безопасности
Быстрая проверка безопасности с помощью нашего собственного автоматизированного аналитического движка.
Сканировать бесплатноДругие инциденты этой недели
Symbiosis
11 сентября 2026 года биткоин-маршрут кросс-чейн моста Symbiosis подвергся эксплойту в его развёртываниях в BNB Smart Chain, Ethereum и Rootstock, что обошлось поставщикам ликвидности и пользователям примерно в 9.97 BTC (~$770K по курсу ~$77K на 11 сентября) [5]. Офчейн-код, определяющий, сколько синтетического биткоина чеканить исходя из депозита в Bitcoin, брал идентичность вносителя из той части транзакции, которую контролирует сам вноситель, что позволило злоумышленнику выдать себя за администратора и опустить минимальную комиссию ниже нуля; затем эта комиссия вычиталась из суммы депозита без проверки знака, так что вычитание фактически становилось прибавлением.
Предыстория
Symbiosis — это протокол кросс-чейн ликвидности, перемещающий стоимость между цепями путём блокировки и чеканки, а не путём передачи самого актива: в цепях со смарт-контрактами токены пользователя блокируются контрактом Portal в исходной цепи, а контракт Synthesis чеканит эквивалентное количество синтетического токена, sToken, в целевой цепи; сжигание sToken высвобождает оригинал [6]. Bitcoin поступает через этот маршрут в виде syBTC — токена с 8 знаками после запятой, соответствующими наименьшей единице самого биткоина, выпускаемого в BNB Smart Chain, Ethereum и Rootstock и объединяемого в пулы с BTCB, cbBTC, WBTC и RBTC [5].

У Bitcoin ничего этого нет: нет смарт-контрактов, а значит, нет и контракта Portal для блокировки депозитов. Вместо этого используется портал — специально выделенный адрес Bitcoin, играющий ту же роль. Депозит — это обычная транзакция Bitcoin, оплачивающая этот адрес, с инструкциями для целевой цепи, прикреплёнными к ней в виде данных. Офчейн-код, принадлежащий мосту, читает эти данные, чтобы узнать, кто делает депозит, сколько и куда должен пойти синтетический токен, и применяет комиссию портала — вычитаемую из внесённой суммы перед фиксацией суммы к чеканке, — минимум которой является параметром, устанавливаемым его администратором.
Ничто на стороне Bitcoin не видно целевой цепи, поэтому Сеть Ретрансляторов (Relayers Network) переносит получившийся запрос через границу. Запрос поступает в BridgeV2, точку входа моста в цепи, в виде закодированного вызова, который перенаправляется в Synthesis. Авторизация основана на MPC-ключе: подписанты протокола владеют долями единого закрытого ключа и совместно формируют одну подпись для запроса. Точка входа для ретранслируемых запросов несёт единственный модификатор и не делает ничего другого перед отправкой вызова:

Этот модификатор хеширует запрос и требует, чтобы сопровождающая подпись была действительна для MPC-адреса:

Контракт устанавливает лишь то, что запрос подписан MPC-ключом. Сумма, которую несёт запрос, вычисляется на стороне Bitcoin.
Анализ уязвимости
Дефект находится не в контрактах целевой цепи. Он в офчейн-сервисе, который читает депозиты Bitcoin и превращает их в кросс-чейн запросы. Его исходный код публично недоступен, поэтому изложение ниже основано на пост-мортеме команды [5] вместе со сторонним аудиторским отчётом 2024 года, содержащим фрагмент кода, который выглядит связанным с этой проблемой [7].
Два дефекта в пути обработки депозита должны были совпасть, и в пост-мортеме указано, что ни один из них сам по себе не был достаточен.
Первый заключается в том, как идентифицируется вноситель депозита. Инструкции, прикреплённые к депозиту, декодируются для восстановления того, кто делает депозит, и декодер брал эту идентичность из части транзакционных данных, которую контролирует тот, кто тратит вход. Таким образом, вноситель мог назвать себя любой стороной, распознаваемой протоколом, включая администратора портала — роль, устанавливающую минимальную комиссию, которую портал будет принимать.
Второй дефект заключается в том, как применяется эта комиссия. В аудите 2024 года была отмечена строка в decodeWrap(), вычисляющая сумму к чеканке путём вычитания комиссии из значения выхода депозита:
Value: types.Satoshi(tx.TxOut[idx].Value) - info.PortalFee,
Вывод аудита заключался в том, что types.Satoshi — это знаковый целочисленный тип, в то время как структура info, несущая PortalFee, является недоверенной и контролируется пользователем, так что результат может быть искусственно сделан отрицательным и, будучи сериализован для целевой цепи как беззнаковое значение, становится достаточно большим, чтобы отчеканить произвольное количество синтетического биткоина. В аудиторском отчёте эта проблема на тот момент была отмечена как исправленная [7]. Пост-мортем 2026 года описывает сбой того же рода: комиссия вычиталась из депозита без проверки знака [5], поэтому комиссия ниже нуля увеличивала депозит, а не уменьшала его. Похоже, что ничто в пути обработки депозита не ограничивает сумму к чеканке реально полученным количеством биткоина.
Анализ атаки
Двенадцать поддельных чеканок прошли в BNB Smart Chain, Ethereum и Rootstock примерно за четыре минуты [5]. Приведённый ниже анализ основан на одной из них — транзакции 0x9a2bc0...21b9b959 в BNB Smart Chain.

-
Шаг 1: Выдав себя за администратора портала, злоумышленник опустил минимальную комиссию, принимаемую порталом, ниже нуля, чтобы депозит, объявляющий отрицательную комиссию, обрабатывался, а не отклонялся [5].
-
Шаг 2: Злоумышленник внёс депозит в 330 сатоши, что отображается как
0x000000000000014a. Запрос, поступивший в BNB Smart Chain, нёс значение0x400000000000014a— то же самое значение с установленным битом для2^62, то есть4,611,686,018,427,388,234базовых единиц. Разница между двумя значениями в точности равна2^62, так что комиссия, вычтенная из этого депозита, составила-4,611,686,018,427,387,904сатоши:330 - (-4,611,686,018,427,387,904) = 4,611,686,018,427,388,234Запрос был подписан и ретранслирован именно в таком виде.
-
Шаг 3:
receiveRequestV2Signed()проверила MPC-подпись запроса и передала его дальше, дойдя доmetaMintSyntheticTokenBTC(). Эта функция определила синтетическое представление реального токена для ID исходной цепи,syBTC, и вызвалаsynthesize()для полной подписанной суммы. Всё это было переведено на адрес злоумышленника0x025122...5d3Ba2:46,116,860,184.27388234 syBTC. -
Шаг 4: Злоумышленник продал синтетический токен в пулы, содержащие его. Единственный своп через пул Uniswap v4
syBTC/WBTCсовершил обмен18,446,744,072,845,450,682базовых единицsyBTC— примерно вчетверо больше, чем было отчеканено в описанной выше транзакции, — и вывел4.38897292 WBTC.

Направленная дальше, выручка от этого свопа составила примерно 137 ether.
Команда приостановила биткоин-маршрут, и около 15.2 BTC средств портала были перемещены на резервные адреса в течение нескольких часов [5]. Было предложено вознаграждение в размере 20% от любой возвращённой суммы, действующее до 13 сентября [8]. Другие маршруты протокола не пострадали.
Заключение
Ни один ключ не был украден, и ни один контракт не был обманут для выполнения кода, для которого он не был написан. Подпись, проверенная целевой цепью, была подлинной; то, что она авторизовала, было числом, которое офчейн-код уже некорректно вычислил двумя разными способами: сначала когда он считал идентичность вносителя из поля, контролируемого самим вносителем, позволив злоумышленнику выдать себя за администратора портала и опустить его минимальную комиссию ниже нуля, а затем когда эта комиссия вычиталась из депозита без проверки знака, так что отрицательная комиссия увеличивала депозит вместо того, чтобы его уменьшать.
Идентичность, считанная из недоверенных входных данных, не является идентичностью: привилегированная роль может быть установлена только на основании того, что вноситель не может выбрать самостоятельно, например адреса, который фактически авторизовал потраченный вход, сверенного со списком, который ведёт протокол, — а не поля, которое вноситель волен заполнить произвольно. Значение, которое вычитается, нуждается в ограничениях с обеих сторон: комиссия должна быть обязана лежать в диапазоне от нуля до суммы депозита, а сумма к чеканке никогда не должна иметь возможность превышать фактически полученное исходной цепью. Любое из этих ограничений само по себе остановило бы эту атаку.
Источники
[1] https://x.com/Liquid_BTC/status/2096696272447218108
[2] https://x.com/Liquid_BTC/status/2097404704028545175
[3] https://github.com/ElementsProject/elements/commit/94000967f6dc05b1afd435e79b1bbc597e29f816
[4] https://www.coindesk.com/markets/2026/09/08/white-hat-hackers-return-most-of-usd320m-bitcoin-taken-from-liquid-network
[5] https://x.com/symbiosis_fi/status/2099566361940795831
[6] https://docs.symbiosis.finance/crosschain-liquidity-engine/synthesizing-process
[7] https://github.com/symbiosis-finance/audits/blob/master/Symbiosis%20Relayers%20Network/Symbiosis%20Relayers%20Network%202024%20-%20Decurity.pdf
[8] https://x.com/symbiosis_fi/status/2098442463358718264
О компании BlockSec
BlockSec — это универсальный поставщик услуг в области безопасности блокчейна и криптовалютного комплаенса. Мы разрабатываем продукты и услуги, которые помогают клиентам проводить аудит кода (включая смарт-контракты, блокчейн и кошельки), перехватывать атаки в режиме реального времени, анализировать инциденты, отслеживать незаконные средства и выполнять требования AML/CFT на протяжении всего жизненного цикла протоколов и платформ.
BlockSec опубликовала множество работ по безопасности блокчейна на престижных конференциях, сообщила о нескольких атаках нулевого дня на DeFi-приложения, заблокировала несколько взломов, спасая более 20 миллионов долларов, и обеспечила безопасность криптовалют на миллиарды долларов.
-
Официальный сайт: https://blocksec.com/
-
Официальный аккаунт в Twitter: https://twitter.com/BlockSecTeam



