攻撃は主に2つのステップで構成されます。最初のステップはキーパーを変更すること、2番目のステップはトークンを引き出すこと(アンロック関数を実行すること)です。2番目のステップは完全に分析されています。最初のステップについては、Kevinが、ハッシュ衝突がハッカーがputCurEpochConPubKeyBytes関数を呼び出すために使用した巧妙なトリックの1つであることを指摘しました。しかし、そもそも攻撃者がこの呼び出しを行うための有効なトランザクションをどのようにして取得できたのかは、依然として不明です。
このブログでは、Ontologyからの悪意のあるトランザクション(0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c)を使用して、プロセス全体を説明します。
要約すると、以下のことがわかりました。
- Ontologyのリレイヤーには、Ontologyチェーンからのトランザクションに対する十分な検証メカニズムがありません。
- 攻撃者は、Polyチェーン上に有効なブロックが存在する限り、Ethereumリレイヤーを経由せずに、
EthCrossChainData内のputCurEpochConPubKeyBytes関数を直接呼び出すことができます。 - Kevinが指摘したハッシュ衝突。
免責事項:このブログには、公開されているソースコードとオンチェーントランザクションに基づいた、当社のチームによる分析結果が含まれています。Poly Networkからの追加情報がない限り、当社の結果を検証することはできません。
0x.1 トランザクションとコントラクト
攻撃フロー
Ontologyトランザクション -> Ontologyリレイヤー -> Polyチェーン -> Ethereumリレイヤー -> Ethereum
Ethereum
0x838bf9e95cb12dd76a54c9f9d2e3082eaf928270: EthCrossChainManager
0xcf2afe102057ba5c16f899271045a0a37fcb10f2: EthCrossChainData
0x250e76987d838a75310c34bf422ea9f1ac4cc906: LockProxy
トランザクション: 0xb1f70464bd95b774c6ce60fc706eb5f9e35cb5f06e6cfe7c17dcda46ffd59581
Ontology
トランザクション: 0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c
Poly
トランザクション: 0x1a72a0cf65e4c08bb8aab2c20da0085d7aee3dc69369651e2e08eb798497cc80
0x2. 攻撃フロー
Ethereumで発生した攻撃を例にとります。 これは3つのチェーン(およびそれに対応するリレイヤー)、つまりOntologyチェーン、Polyチェーン、Ethereumが関与する「クロスチェーン攻撃」です。
攻撃フロー全体は3つのステップで構成されます。
- 攻撃者はまず、Ontologyチェーン上で悪意のあるトランザクション(0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c)を開始しました。
- 次に、攻撃者はEthereum上の
EthCrossChainDataコントラクトに格納されているキーパーの公開鍵を変更しました。 - 最後に、攻撃者は暗号資産を盗むための悪意のあるトランザクションを作成しました。
0x2.1 最初のステップ
攻撃者はまず、悪意のあるペイロードを含むクロスチェーントランザクション(0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c)をOntologyから開始しました。

このペイロードには、特別に作成された関数名(6631で始まり、変換後はf1121318093)が含まれていることに気づくかもしれません。この名前は非常に巧妙です。なぜなら、攻撃者は関数のシグネチャのハッシュ衝突を利用して、Ethereum上のEthCrossChainDataコントラクトのputCurEpochConPubKeyBytes関数を呼び出すためにそれを使用するからです。ここでは、ハッシュ衝突の詳細については説明しません。なぜなら、それはすでに多くの議論がなされているからです。
その後、このトランザクションはOntologyチェーンリレイヤーによって正常に受け入れられました。厳密な検証は存在しません。その結果、Polyチェーン上で有効な新しいトランザクション(0x1a72a0cf65e4c08bb8aab2c20da0085d7aee3dc69369651e2e08eb798497cc80)となりました。
その新しいトランザクションはEthereumリレイヤーによって認識され、拒否されました。なぜなら、Ethereumリレイヤーは宛先コントラクトアドレス(この場合はEthCrossChainData)を検証しましたが、許可されるのはLockProxyのみだったからです。
このように、処理は終了しました。しかし、悪意のあるペイロードを持つトランザクションはPolyチェーンに保存されており、攻撃を開始するために悪用される可能性があります。
0x2.2 2番目のステップ
攻撃者は、Polyチェーン上に格納されていた悪意のあるトランザクションデータを入力として、EthCrossChainManagerコントラクトのverifyHeaderAndExecuteTx関数を呼び出すトランザクションをEthereumに手動で送信しました。有効なPolyチェーントランザクションとして、verifyHeaderAndExecuteTx関数内の署名検証(およびマークルプルーフ)をバイパスすることができました。その後、EthCrossChainDataコントラクトのputCurEpochConPubKeyBytes関数が呼び出され、元の4つのキーパーが攻撃者が制御する新しいキーパー(0xA87fB85A93Ca072Cd4e5F0D4f178Bc831Df8a00B)に変更されました。
0x2.3 3番目のステップ
キーパーの変更後、攻撃者はPolyチェーンを使用せずに直接verifyHeaderAndExecuteTx関数を呼び出すことができました。最終的に、LockProxyコントラクトのunlock関数が呼び出され、Ethereumから大量のデジタル資産が盗まれました。詳細な分析は、以前のレポートで確認できます。
0x3. リレイヤー
OntologyとEthereumのリレイヤーはどちらもGoで実装されています。しかし、十分な検証が欠けているため、
- 攻撃者は悪意のあるトランザクションを構築し、それをPolyチェーンにパックすることができます。
- 攻撃者はEthereum上の
EthCrossChainDataスマートコントラクト内の関数を直接呼び出すことができます。
0x3.1 OntologyリレイヤーはOntologyからのクロスチェーントランザクションを盲目的に信頼する
ont_relayerは、Ontologyチェーンからのクロスチェーントランザクションをリッスンし、それらをPolyチェーンに送信する役割を担っています。
- SideはOntologyチェーンを意味します。AllianceはPolyチェーンを意味します。
CrossChainContractAddressはOntologyチェーン上のネイティブスマートコントラクト(番号09)です。

上記の図は、OntologyリレイヤーがOntologyチェーンからのクロスチェーントランザクションとOntologyチェーンへのクロスチェーントランザクションをリッスンするために2つのルーチンを開始し、クロスチェーントランザクションのステータスをチェックするルーチン(71行目)を示しています。

上記の図では、OntologyリレイヤーはOntologyチェーンのRPCインターフェース(215行目 GetSmartContractEventByBlock)を呼び出して、チェーン上のイベントを取得しています。228行目と232行目から、このルーチンはCrossChainContractAddressによってトリガーされたmakeFromOntProofイベントのみをリッスンしていることがわかります。

上記の図では、クロスチェーントランザクションを処理する際に5つのチェックがあります。最初の2つはOntologyチェーンへのRPCリクエスト(チェック1と4)をチェックしており、3つのチェックはパラメータがnullであるかどうか(チェック2、3、5)をチェックしています。しかし、クロスチェーントランザクションのセマンティクス、つまりコントラクトとメソッド名が妥当であるかどうかのチェックは存在しません。最後に、トランザクションはPolyチェーンに送信されます(183行目)。

Ontologyリレイヤーは、RPCインターフェース(164行目 - SendTransaction)を使用して、Polyチェーンへのトランザクションを構築して送信します。

ProcessToAliiance**Check**AndRetry関数は、トランザクションが失敗したかどうかのみをチェックします。失敗した場合は、トランザクションを再送信します。
要約すると、ont-relayerはOntologyチェーンからのCrossChainContractAddressによってトリガーされたすべてのmakeFromOntProofイベントをリッスンします。その後、トランザクションはPolyチェーンに送信されます。Ontologyチェーン上の誰からのクロスチェーントランザクションでもmakeFromOntProofイベントをトリガーし、Polyチェーンに送信される結果となることに注意してください。
0x3.2 Ethereumリレイヤーのバイパス
Ethereum Relayerは、Polyチェーンからのトランザクションをリッスンし、次にEthereumにトランザクションを送信する役割を担っています。

EthereumリレイヤーはGoroutineを開始してPolyチェーンを監視します。

これは、宛先がEthereumであるクロスチェーントランザクションを監視します(275〜278行目)。次に、宛先コントラクト(ToContractAddress)がconfig.TargetContractsで構成されているコントラクトのいずれかであるかどうかをチェックします。そうでない場合、クロスチェーントランザクションは宛先チェーン(Ethereum)に送信されません。
しかし、攻撃者は宛先チェーンと直接対話し、EthCrossChainManager内の関数を呼び出すことができます。別の言い方をすれば、Ethereumリレイヤーでのチェックはバイパスできます。悪意のあるトランザクションがPolyチェーンにパックされていれば(これは前のステップでOntologyリレイヤーを通じて達成されます)、攻撃者はEthCrossChainManagerと直接対話できます。このプロセス中に、Polyチェーン上に有効なトランザクションがあるため、署名検証(ECCUtils.verifySig)とマークルプルーフ(ECCUtils.merkleProve)はパスします。
これら2つの方法を組み合わせることで、攻撃者はToContractAddress.methodをEthereum上で正常に呼び出すことができます。ハッシュ衝突と組み合わせることで、最終的にputCurEpochConPubKeyBytes関数が呼び出され、キーパーが変更されます。
謝辞
Yufeng Hu、Siwei Wu、Lei Wu、Yajin Zhou @ BlockSec
公式サイト: https://blocksec.com/ 公式Twitterアカウント: https://twitter.com/BlockSecTeam



