Preface On August 10, 2021, Beijing time, the cross-chain bridge project Poly Network was attacked, with losses exceeding 600 million US dollars. Although the attackers subsequently repaid the stolen digital currency, this is still the largest attack in the history of blockchain. Since the entire attack process involves different blockchain platforms and there are complex interactions between contracts and Relayers, the existing analysis report has not been able to clearly sort out the complete process of the attack and the root cause of the vulnerability. The entire attack is divided into two main stages, including modifying the keeper signature and the final withdrawal. In the second stage, since the keeper signature has been modified, the attacker can directly construct a malicious withdrawal transaction. For details, please refer to our previous report. However, there is currently no detailed article explaining how the transaction that modifies the keeper signature is finally executed on the target chain. This step is the core step of the attack. This report starts with modifying the Keeper signature transaction (Ontology chain transaction 0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c), and analyzes the underlying principles and the nature of the vulnerability. We found the following reasons why Keeper can be modified: The relayer on the source chain (Ontology) does not perform semantic verification on the transactions on the chain, so malicious transactions including modification of the keeper can be packaged into the poly chain Although the relayer on the target chain (Ethereum) verifies the transaction, the attacker can directly call the EthCrossChainManager contract on Ethereum and finally call the EthCrossChainData contract to complete the signature modification. The attacker carefully obtained a function signature that could cause a hash conflict, and then called putCurEpochConPubKeyBytes to modify the signature [2] The interaction process involving transactions and contracts is as follows: Ontology Trading->Ontology Relayer->Poly Chain->Ethereum Relayer->Ethereum Ethereum 0x838bf9e95cb12dd76a54c9f9d2e3082eaf928270: EthCrossChainManager 0xcf2afe102057ba5c16f899271045a0a37fcb10f2: EthCrossChainData 0x250e76987d838a75310c34bf422ea9f1ac4cc906: LockProxy 0xb1f70464bd95b774c6ce60fc706eb5f9e35cb5f06e6cfe7c17dcda46ffd59581: modify keeper's transaction Ontology 0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c: Modify keeper's transaction Poly 0x1a72a0cf65e4c08bb8aab2c20da0085d7aee3dc69369651e2e08eb798497cc80: Modify the transaction attack process of Keeper The entire attack can be roughly divided into three steps. The first step is to generate a malicious transaction (0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c) on the Ontology chain, the second step is to modify the Keeper signature in the Ethereum EthCrossChainData contract, and the third step is to construct a malicious transaction to launch the final attack and withdraw coins. Step 1 The attacker first initiated a cross-chain transaction on Ontology (0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c), which contained an attack payload: It can be seen that the transaction contains a carefully designed function name (the number starting with 6631 in the figure, which is converted to f1121318093), the purpose of which is to call the putCurEpochConPubKeyBytes function (which belongs to the EthCrossChainData contract on Ethereum) by causing a hash collision. There have been many discussions on the details of hash function collisions on the Internet, please refer to [2]. Subsequently, the transaction is received by the Ontology Relayer. Note that there is no strict verification here. The transaction will be successfully uploaded to the Poly Chain through the Relayer (0x1a72a0cf65e4c08bb8aab2c20da0085d7aee3dc69369651e2e08eb798497cc80). The Ethereum Relayer will sense the generation of the new block. However, this transaction was rejected by Ethereum Relayer because Ethereum Relayer has a check on the target contract address and only allows the LockProxy contract as the target address, while the attacker passed in the EthCrossChainData address. Therefore, the attacker’s attack path is interrupted here. However, as mentioned earlier, the attack transaction containing the malicious payload has been successfully uploaded to the Poly Chain and can be further exploited. Step 2: The attacker manually initiates a transaction, calling the verifyHeaderAndExecuteTx function in the EthCrossChainManager contract, and using the attack transaction data saved in the Ploy Chain block in the previous step as input. Since this block is a legal block on the poly chain, it can pass the verification of the signature and merkle proof in verifyHeaderAndExecuteTx. Then execute the putCurEpochConPubKeyBytes function in the EthCrossChainData contract to modify the original 4 keepers to the address specified by themselves (0xA87fB85A93Ca072Cd4e5F0D4f178Bc831Df8a00B). Step 3: After the keeper is modified, the attacker directly calls the verifyHeaderAndExecuteTx function on the target chain (without going through the poly chain - because the keeper has been modified, the attacker can arbitrarily sign blocks on the poly chain that are reasonable in the eyes of the target chain), and finally calls the Unlock function (belonging to the LockProxy contract), transferring a large amount of funds, causing serious losses to the project party. For specific attack details, please refer to our previous report [1]. Relayer code analysis During this attack, both Ontology and Ethereum have Relayers responsible for putting transactions from Ontology on the poly chain and putting transactions on the poly chain on Ethereum. These two Relayers are service processes implemented in the Go language. However, we found that both relayers lacked effective verification. An attacker can construct a malicious cross-chain transaction on Ontology and successfully package it into the poly chain. Although the Relayer in Ethereum has a verification function, attackers can directly interact with the on-chain contracts on Ethereum and directly execute malicious functions. Ontology Relayer fully trusts cross-chain transactions on Ontology Poly Network’s ont_relayer (https://github.com/polynetwork/ont-relayer) is responsible for monitoring cross-chain transactions on the Ontology chain and packaging them into the Poly Chain. Note: In Ontology Relayer, Side refers to Ontology Chain; Alliance refers to Poly Chain. CrossChainContractAddress is a smart contract with the original number 09 on the Ontology chain. In the above figure, when Ontology Relayer starts, three Goroutines are started to monitor cross-chain transactions between Ontology Chain and Poly Chain, and to check the status of cross-chain transactions on Poly Chain. In this report, we only focus on the code logic of monitoring Side on line 69. In the figure above, Ontology Relayer calls the RPC interface provided by the Ontology chain (line 215, calling the SDK function GetSmartContractEventByBlock) to obtain the smart contract event triggered in the block; then lines 228 and 232 indicate that Ontology Relayer only listens to the makeFromOntProof event triggered by CrossChainContractAddress on the Ontology Chain; In the figure above, when processing cross-chain transactions on Ontology Chain, Ontology Relayer performs a total of five checks, including two RPC request checks sent to Ontology Chain (check 1 and check 4), and three checks to see if the parameters are empty (check 2, check 3, and check 5). These five checks are all regular checks, and no semantic checks are performed on cross-chain transactions from Ontology Chain; Lines 167 and 171 extract the transaction parameter information (proof, auditPath) required for execution on the target chain; Line 183 sends a transaction to Poly Chain; After constructing the transaction on Poly Chain, Ontology Relayer initiates an RPC request to Poly Chain to send the transaction (line 164, function call SendTransaction); This Goroutine named ProcessToAliianceCheckAndRetry only resends failed transactions and still does not perform any semantic verification on cross-chain transactions from the Ontology Chain. So far, we can see that ont-relayer monitors all makeFromOntProof events triggered by CrossChainContractAddress from Ontology Chain, and forwards the transaction to Poly Chain without any semantic verification. Any cross-chain transaction sent by anyone to Ontology will trigger the makeFromOntProof event of CrossChainContractAddress, so Ontology Relayer will forward all cross-chain transactions from Ontology to Poly chain. Invalid validation in Ethereum Relayer Ethereum Relayer is responsible for monitoring Poly Chain and forwarding cross-chain transactions whose target chain is Ethereum to Ethereum. Ethereum Relayer starts a Goroutine to monitor Poly Chain; Ethereum Relayer monitors all cross-chain transactions on Poly Chain whose target chain is Ethereum (lines 275 to 278); Ethereum Relayer verifies whether the target contract of the cross-chain transaction is one of the contracts specified in config.TargetContracts. If not, the cross-chain transaction will not be sent to Ethereum (line 315). Although Ethereum Relayer has done some verification on cross-chain transactions on Poly Chain, such as limiting the target contract, unlike Poly Chain, anyone can send transactions to the EthCrossChainManager contract on Ethereum. In other words, the verification done by Ethereum Relayer here has no practical significance. As long as the cross-chain transaction containing malicious payload is successfully packaged into Poly Chain (although not forwarded to the Ethereum chain by relay), anyone can directly use the packaged block data to send the payload to the Ethereum EthCrossChainManager contract and execute it (in this process, the merkle proof can be verified because it is the poly chain block data that has been normally uploaded to the chain). The attacker took advantage of the above two flaws to complete steps one and two in the attack process. In conclusion, through a complete review and detailed analysis of the entire attack process, we believe that the incomplete verification of the Relayer is the root cause of the attack. Other aspects (such as the use of hash conflicts, etc.) are more of a more exciting attack technique. In short, cross-chain verification and authentication are the key to the security of the cross-chain system, and it is worth the community's efforts. (BlockSec Team) |