Back to Blog

The Further Analysis of the Poly Network Attack

Code Auditing
August 12, 2021

The attack consists of two main steps. The first step is to change the keeper and the second step is to withdraw the tokens (executing the unlock function). The second step has been fully analyzed. For the first step, Kevin has pointed out that the hash collision is one smart trick used by the hacker to invoke the putCurEpochConPubKeyBytes function. However, why the attacker can have a valid transaction to make this call in the first place is still unknown.

In this blog, we use the malicious transaction from Ontology (0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c) to illustrate the whole process.

In summary, we find that:

  • The Ontology relayer does not have enough validation mechanisms for the transaction from the Ontology chain.
  • The attacker can directly invoke the putCurEpochConPubKeyBytes function in EthCrossChainData without going through the Ethereum relayer, as long as there is a valid block on the Poly chain.
  • The hash collision as pointed out by Kevin

Disclaimer: This blog contains the analysis results by our team, which are based on the publicly available source code and the on-chain transactions. Without the further information from Poly Network, we are not able to verify our result.

0x.1 Transactions and Contracts

Attack Flow

Ontology transaction -> Ontology relayer -> Poly chain -> Ethereum relayer -> Ethereum

Ethereum

0x838bf9e95cb12dd76a54c9f9d2e3082eaf928270: EthCrossChainManager
0xcf2afe102057ba5c16f899271045a0a37fcb10f2: EthCrossChainData
0x250e76987d838a75310c34bf422ea9f1ac4cc906: LockProxy
 
Transaction: 0xb1f70464bd95b774c6ce60fc706eb5f9e35cb5f06e6cfe7c17dcda46ffd59581

Ontology

Transaction: 0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c

Poly

Transaction: 0x1a72a0cf65e4c08bb8aab2c20da0085d7aee3dc69369651e2e08eb798497cc80

0x2. Attack Flow

Take the attack occurred on Ethereum as an example. This is a cross-chain attack involving three chains (and their corresponding relayers), i.e., Ontology Chain, Poly Chain and Ethereum.

The whole attack flow consists of three steps:

  1. the attacker first initiated a malicious transaction (0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c) on Ontology Chain;
  2. the attacker then modified keeper's public key stored in the EthCrossChainData contract on Ethereum;
  3. the attacker finally crafted a malicious transaction to harvest crypto assets.

0x2.1 The First Step

The attacker first initiated a cross-chain transaction (0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c) from Ontology, which includes a malicious payload:

You may notice that this payload contains a crafted function name (starting with 6631, i.e., f1121318093 after conversion). This name is definitely meticulous, because the attacker would use it to invoke the putCurEpochConPubKeyBytes function (see the EthCrossChainData contract on Ethereum) by exploiting the hash collision of function signatures. Here we will not illustrate the details of hash collision stuff, because it has been discussed a lot.

After that, this transaction was successfully accepted by the Ontology Chain Relayer. Note that there does NOT exist any strict verification. As a result, it became a valid new transaction (0x1a72a0cf65e4c08bb8aab2c20da0085d7aee3dc69369651e2e08eb798497cc80) on Poly Chain.

The new transaction was then perceived and REJECTED by the Ethereum Relayer. Because the Ethereum Relayer verified the destination contract address (i.e., EthCrossChainData in this case), however, only LockProxy would be allowed.

As such, the processing was terminated. However, the transaction with the malicious payload has been stored on Ploy Chain, which can be exploited to launch attack.

0x2.2 The Second Step

The attacker manually sent transaction to Ethereum by invoking the verifyHeaderAndExecuteTx function of the EthCrossChainManager contract. The malicious transaction data stored on Poly Chain was used as the input. As a valid Poly Chain transaction, it was able to bypass the verification (including the signatures and the merkle proof) in the verifyHeaderAndExecuteTx function. After that, the putCurEpochConPubKeyBytes function of the EthCrossChainData contract was invoked to modify the original four keepers to a new one (i.e., 0xA87fB85A93Ca072Cd4e5F0D4f178Bc831Df8a00B) controlled by the attacker.

0x2.3 The Third Step

After the modification of the keeper, the attacker was able to directly call the verifyHeaderAndExecuteTx function without using Poly Chain. Finally, the unlock function of the LockProxy contract was invoked to stole huge amount of digital assets from Ethereum. The detailed analysis can be found in our previous report.

0x3. Relayer

Both Ontology and Ethereum relayers are implemented in Go. However, they lack of enough validation so that

  • The attacker can construct a malicious transaction, which will be packed into the Poly chain
  • The attacker can directly invoke the functions in the EthCrossChainData smart contract on Ethereum

0x3.1 Ontology Relayer Blindly Trusts the Cross-chain Transactions from Ontology

The ont_relayer is responsible for listening to cross-chain transactions from the Ontology chain and sending them to the Poly chain.

  • Side means the Ontology chain; Alliance means the Poly chain
  • CrossChainContractAddress is the native smart contract (number 09) on the Ontology chain

The above figure shows that the Ontology relayer starts two routines to listen to cross-chain transactions from and to the Ontology chain, and the routine to check the status of the cross-chain transaction (line 71).

In the above figure, the Ontology relayer invokes the RPC interface exposed by the Ontology chain (line 215 GetSmartContractEventByBlock) to obtain events on the chain. From line 228 and 232, we can see that this routine only listens to the makeFromOntProof event triggered by CrossChainContractAddress.

In the above figure, when processing the cross-chain transaction, there are five checks. The first two are checking the RPC requests (check 1 and 4) to the Ontology chain and three checks for whether the parameters are null (check 2, 3 and 5). However, there does not exist any check for the semantics in the cross-chain transaction, i.e., whether the contract and method name are reasonable. At last, it sends the transaction to the Poly chain (line 183)

The Ontology relayer constructs and sends the transaction to the Poly chain using the RPC interface (line 164 - SendTransaction).

The function ProcessToAliianceCheckAndRetry only checks whether the transaction has failed. If so, it will resend the transaction.

In summary, ont-relayer listens to all the makeFromOntProof events triggered by CrossChainContractAddress from the Ontology chain. Then the transaction will be sent to the Poly chain. Note that, the cross-chain transaction from anyone on the Ontology chain will trigger the makeFromOntProof event, which results in being sent to the Poly chain.

0x3.2 Bypass Ethereum Relayer

Ethereum Relayer is responsible for listening transactions from the Poly chain and then sending the transaction to Ethereum.

Ethereum Relayer starts a Goroutine to monitor the Poly Chain;

It monitors the cross-chain transaction whose target is Ethereum (line 275 - 278). Then it checks whether the target contract (ToContractAddress) is the one of the contracts that are configured in config.TargetContracts. If not, the cross-chain transaction will not be sent to the target chain (Ethereum).

However, the attacker can directly interact with the target chain and invoke the function in EthCrossChainManager. In another world, the check in the Ethereum relayer can be bypassed. As long as the malicious transaction has been packed on the poly chain (which is achieved in the previous step through the Ontology relayer), the attacker can directly interact with EthCrossChainManager. During this process, the signature verification (ECCUtils.verifySig) and merkle prove (ECCUtils.merkleProve) can pass, since there is a valid transaction on the Poly chain.

By using the previous two methods, the attacker can successfully invoke the ToContractAddress.method on Ethereum. Combining with the hash collision, the putCurEpochConPubKeyBytes function is eventually invoked to change the keeper.

Credits

Yufeng Hu, Siwei Wu, Lei Wu, Yajin Zhou @ BlockSec

Official website: https://blocksec.com/

Official Twitter account: https://twitter.com/BlockSecTeam

Sign up for the latest updates
~$1.35M Lost: BarnBridge, DeFiTuna | BlockSec Weekly
Security Insights

~$1.35M Lost: BarnBridge, DeFiTuna | BlockSec Weekly

This weekly report covers 2 security incidents from July 13 to July 19, 2026, with approximately $1.35M in total losses on Ethereum and Solana. DeFiTuna, a Solana lending protocol, lost ~$570K because the position health check treated a zero-value position as healthy regardless of outstanding debt; the attacker used controlled swap routing and a separate low-liquidity pool to trigger this defect. BarnBridge lost ~$776K after an attacker exploited the protocol's deprecated but still-active governance system on Ethereum to pass a malicious proposal and drain user-approved USDC.

~$800K Lost: Hinkal Double-Spend | BlockSec Weekly
Security Insights

~$800K Lost: Hinkal Double-Spend | BlockSec Weekly

This weekly security report covers 1 notable incident from June 29 to July 5, 2026, with approximately $800K in total losses on Ethereum. The Hinkal shielded-pool protocol was drained through a double-spend attack that likely exploited a flaw in the legacy note format, allowing the attacker to derive multiple nullifiers from a single deposit. The report analyzes the probable circuit-level vulnerability, the attack flow, and broader implications for nullifier-based privacy protocols.

Newsletter - June 2026
Security Insights

Newsletter - June 2026

This monthly report covers the three largest security incidents in June 2026, totaling approximately $22M in confirmed losses. A sophisticated honeypot attack drained ~$15M from JaredFromSubway's MEV bot by exploiting unchecked token allowances. Two legacy Aztec rollup deployments lost ~$4.35M through proof-settlement boundary gaps. SecondFi's Ed25519 implementation flaw exposed wallet private keys, resulting in ~$2.4M drained from 374 wallets. All three incidents share a common pattern: security guarantees that appeared intact on the surface but were never actually enforced.

Best Security Auditor for Web3

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

BlockSec Audit