Back to Blog

Мост Harmony Cross-Shard ONE Mint + потеря ключей на ~$47М | BlockSec

Code Auditing
August 19, 2026
7 min read
Key Insights
  • На этой неделе представлено 5 значимых инцидентов безопасности с общей оценкой убытков около $47 млн. Три из них были компрометациями приватных ключей, при которых средства пользователей были выведены напрямую (Unknown Whale Wallet ~$25 млн, Kite ~$14 млн и Coinsbuy ~$7.9 млн), в то время как основной рассматриваемый инцидент, Harmony, представлял собой ошибку в реализации цепочки, из-за которой поддельный ONE не имеет реализуемой или подтверждённой суммы убытков (см. примечание к таблице).

  • Целевые шарды Harmony определяли маркер «потрачено» для межшардовой квитанции на основе неаутентифицированных полей доказательства (MerkleProof.ShardID и MerkleProof.BlockNum), а не подписанного заголовка исходного блока, поэтому злоумышленник мог повторно воспроизвести уже зачисленную квитанцию, изменив только эти поля, и создать нативный ONE без соответствующего списания на исходном шарде. Второй недочёт, связанный с проверкой кворума на этапе до стейкинга, заключался в том, что учитывался полный размер комитета, а не количество валидаторов, фактически включённых в битовую маску подписантов.

  • Реализация цепочки должна аутентифицировать каждое поле, используемое для идентификации уже израсходованного состояния, и определять кворум по валидаторам, которые действительно подписали, а не по полному составу комитета.

За прошедшую неделю (10.08.2026 - 16.08.2026) в центре внимания оказались 5 заметных инцидентов безопасности с общими потерями примерно $47 млн.

Дата Инцидент Тип Оценочные потери
2026/08/10 Coinsbuy Компрометация приватного ключа ~$7.9M
2026/08/10 Unknown Whale Wallet Компрометация приватного ключа ~$25M
2026/08/12 Harmony Проблема валидации Неизвестно*
2026/08/13 Kite Компрометация приватного ключа ~$14M
2026/08/15 Fox Изъян бизнес-логики ~$117K

*Harmony сообщила о подтверждённом первом раунде эмиссии 4 млрд ONE и о более широкой реконструкции, согласно которой было создано примерно 3.01 трлн ONE, из которых примерно 2.385 трлн ONE были переведены [1]. При цене до инцидента ~$0.001183 номинальная стоимость поддельных токенов составляет ~$3.56 млрд, однако это примерно в 200 раз превышает общее предложение ONE и значительно превышает рыночную капитализацию токена, поэтому такие потери не являются ни реализуемыми, ни подтверждёнными реализованными. С тех пор Harmony откатила блокчейн до контрольной точки, предшествовавшей атаке, отбросив поддельное состояние [2]. Поэтому Harmony исключена из общей суммы потерь.

Причины выбора

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

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

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

Главное событие недели: Harmony

Harmony оказалась в центре внимания на этой неделе, поскольку изъян находился в самой реализации блокчейна, а не в прикладном контракте — необычно опасный режим сбоя, поскольку он может привести к инфляции базового актива целого Layer-1.

12 августа 2026 года Harmony, шардированный блокчейн Layer-1, подверглась несанкционированной эмиссии своего нативного токена ONE из-за уязвимости повторного воспроизведения межшардовых квитанций. Целевые шарды идентифицировали уже использованную квитанцию по полям, не покрытым подписью исходного комитета, поэтому злоумышленник мог повторно предъявить подлинную, ранее зачисленную квитанцию, изменив только эти поля, и получить повторное зачисление без соответствующего списания на исходном шарде. Harmony проводит сверку подтверждённой первой волны эмиссии в размере 4 млрд ONE с более широкой реконструкцией, согласно которой было создано примерно 3.01 трлн ONE, из которых примерно 2.385 трлн ONE были переведены кошельком с поддельной эмиссией; ни одна из этих сумм не является подтверждённым реализованным убытком, и окончательное воздействие всё ещё зависит от отката и сверки с биржами [1][2]. В ходе расследования Harmony выявила отдельную уязвимость проверки кворума на этапе до стейкинга, хотя не подтвердила, полагался ли эксплойт на неё.

Предыстория

Harmony — это шардированный блокчейн Layer-1. Каждый шард поддерживает своё независимое состояние, поэтому исходный шард не может напрямую изменять аккаунт на целевом шарде. Для перемещения средств между шардами Harmony использует асинхронную схему на основе квитанций: исходный шард списывает средства с отправителя и формирует межшардовую квитанцию, а целевой шард впоследствии зачисляет средства получателю.

Межшардовая квитанция фиксирует сам перевод:

type CXReceipt struct {
    TxHash    common.Hash
    From      common.Address
    To        *common.Address
    ShardID   uint32
    ToShardID uint32
    Amount    *big.Int
}

Целевой шард не доверяет голой квитанции. Квитанция передаётся внутри CXReceiptsProof, который содержит доказательство Меркла, заголовок исходного блока и подпись фиксации исходного комитета над этим заголовком:

type CXMerkleProof struct {
    BlockNum      *big.Int
    BlockHash     common.Hash
    ShardID       uint32
    CXReceiptHash common.Hash
    ShardIDs      []uint32
    CXShardHashes []common.Hash
}

type CXReceiptsProof struct {
    Receipts     CXReceipts
    MerkleProof  *CXMerkleProof
    Header       *block.Header
    CommitSig    []byte
    CommitBitmap []byte
}

Обычный путь проверки валидирует доказательство относительно подписанного исходного заголовка и подписи его комитета:

VerifyIncomingReceipts()
  -> IsSpent()
  -> ValidateCXReceiptsProof()
      -> VerifyHeaderSignature()
          -> verifySignature()
              -> DecodeSigBitmap()
              -> IsQuorumAchievedByMask()
              -> aggSig.VerifyHash()

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

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

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

Повторное воспроизведение межшардовых квитанций

Защита от повторного воспроизведения записывала и искала маркер «использовано» квитанции, но её ключ формировался из двух изменяемых полей доказательства Меркла:

CXMerkleProof.ShardID
CXMerkleProof.BlockNum

Хардфорк Bloom усилил эту защиту в основной сети на эпохе 2964 (13 июля 2026 года), под контролем флага IsCXMerkleProofReplayFixEpoch [3]: для доказательства, чей подписанный Header.Epoch() соответствует этой эпохе или более поздней, IsSpent() и WriteCXReceiptsProofSpent() формируют ключ маркера «использовано» из подписанного исходного заголовка (Header.ShardID и Header.Number) вместо этих двух полей. Однако усиленное формирование ключа так и не было применено ретроактивно — для доказательства, чей Header.Epoch() предшествовал форку, обе функции сохраняли устаревший резервный вариант с MerkleProof.ShardID и MerkleProof.BlockNum, которые ValidateCXReceiptsProof() для этих эпох никогда не привязывал к подписанному заголовку.

Эти два поля находятся за пределами подписи фиксации, которая покрывает только исходный Header. Изменение Header привело бы к сбою VerifyHeaderSignature(), но изменение MerkleProof.ShardID или MerkleProof.BlockNum не нарушает никакой проверки вообще — ни подпись заголовка, ни валидацию доказательства, которая для этих эпох никогда не связывала их обратно с Header. Поскольку устаревший ключ маркера «использовано» формировался из этих неаутентифицированных полей, идентичность квитанции для целей защиты от повторного воспроизведения оказалась отделена от подписи, аутентифицировавшей остальную часть доказательства: два доказательства с одинаковым подписанным заголовком и квитанциями, но разными значениями MerkleProof.ShardID или MerkleProof.BlockNum, считались различными для целей отслеживания использования.

Проверка кворума на этапе до стейкинга

Вторая уязвимость затрагивала проверку кворума на этапе до стейкинга. Логика uniformVerifier.IsQuorumAchievedByMask() сравнивала порог с

len(mask.Publics)

рассматривая это значение как количество подписавших. Но mask.Publics — это полный список публичных ключей комитета для шарда и эпохи, а не набор валидаторов, фактически подписавших; этот набор представлен битовой маской подписавших. В результате пустая битовая маска подписавших могла всё же преодолеть порог кворума, поскольку проверка измеряла размер комитета, а не количество установленных битов маски. В сочетании с единичной (нулевой) агрегированной BLS-подписью это могло создать видимость достаточного кворума у заголовка источника эпохи до стейкинга.

Анализ атаки

Злоумышленник начал с подлинной, уже обработанной межшардовой квитанции из блока исходного шарда, предшествовавшего хардфорку, а затем воспроизвёл её, изменив только неаутентифицированную идентичность доказательства. Поддельные ONE были эмитированы на четыре кошелька злоумышленника. Следующий анализ основан на транзакциях 0xf3d4e8b1...8242c7f и 0x9a756ef9...b0a4d678.

  • Шаг 1: Злоумышленник получил действительное историческое CXReceiptsProof из блока исходного шарда до хардфорка. Доказательство содержало действительные квитанции, действительный заголовок исходного блока и действительную подпись фиксации исходного комитета.

  • Шаг 2: Целевой шард уже использовал эту квитанцию один раз и записал её маркер «использовано», сформированный из полей доказательства Меркла.

  • Шаг 3: Злоумышленник изменил только неаутентифицированные поля идентичности доказательства, оставив подписанный заголовок и все поля, покрытые подписью, нетронутыми:

    MerkleProof.ShardID
    MerkleProof.BlockNum
  • Шаг 4: Изменённое доказательство было отправлено как входящая квитанция в более позднем блоке целевого шарда. IsSpent() сформировал ключ маркера «использовано» из изменённых полей и не нашёл предыдущий маркер, поэтому квитанция выглядела неиспользованной.

  • Шаг 5: ValidateCXReceiptsProof() всё же принял доказательство, поскольку изменённые поля идентичности не были покрыты подписью, а все аутентифицированные поля остались неизменными: хэш заголовка и хэш исходящей квитанции, хэш блока и хэш квитанции в доказательстве Меркла, хэш квитанций, а также подпись фиксации и битовая маска.

  • Шаг 6: ApplyIncomingReceipt() выполнился на целевом шарде и повторно зачислил получателю средства через db.AddBalance(*cx.To, cx.Amount). Соответствующего списания на исходном шарде не произошло, поскольку исходная межшардовая транзакция уже была выполнена ровно один раз, что привело к инфляции нативного ONE на целевом шарде.

Заключение

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

Патчи закрывают оба пробела [4][5][6]. В более широком смысле, реализация блокчейна должна аутентифицировать каждое поле, используемое для идентификации уже использованного состояния, и должна оценивать кворум по валидаторам, фактически подписавшим, а не по всему комитету.

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

Погружайтесь в транзакции, чтобы действовать разумно

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

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

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

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

Источники

О компании BlockSec

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

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

Sign up for the latest updates
~$1.6M потеряно: эксплойты токена Moke и LpdFi | BlockSec Weekly
Security Insights

~$1.6M потеряно: эксплойты токена Moke и LpdFi | BlockSec Weekly

За неделю 3-9 августа 2026 года в BNB Chain произошло 2 инцидента с общими потерями ~$1,6 млн из-за манипуляций с ценами. LpdFi (~$697K): атакующий использовал резервы PancakeSwap для завышения позиции. Moke Token (~$906K): манипуляция ценой и дублирование дивидендов LP.

Инцидент с COLDCARD: когда «случайная» сид-фраза кошелька оказалась не случайной
Security Insights

Инцидент с COLDCARD: когда «случайная» сид-фраза кошелька оказалась не случайной

Ошибка в прошивке COLDCARD направляла генерацию seed на слабый программный ГСЧ, делая кошельки восстановимыми офлайн. Обновление не устраняет проблему. К 7 августа 2026 г. подтверждённые потери — 1 405 BTC (~$91 млн), оценки до 2 055 BTC.

~$88M Потеряно: Эксплойты COLDCARD и LULA | Еженедельник BlockSec
Security Insights

~$88M Потеряно: Эксплойты COLDCARD и LULA | Еженедельник BlockSec

За неделю 27 июля–2 августа 2026 года два инцидента привели к потерям ~$88M. COLDCARD: ошибка энтропии прошивки аппаратного кошелька — неверная проверка макроса RNG направляла генерацию сида на детерминированный фолбэк, позволив украсть 1370 BTC (~$88M). LULA (BNB Chain): логическая уязвимость позволила вызвать `recycle()`, слив ~$578K из пула PancakeSwap V2.

Best Security Auditor for Web3

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

BlockSec Audit