Poly Network Hack Analysis – Largest Crypto Hack

On 10th August 2021, Poly Network suffered from a hack that caused a loss of over 600 million dollars. The hack happened across multiple blockchains including Ethereum, Binance Smart Chain, and Polygon. This is the largest crypto hack yet.

Poly Network is a Blockchain interoperability project that allows people to send transactions across blockchains. One of their key use case is a cross-blockchain bridge that allows you to move assets from one blockchain to another by locking tokens on one blockchain and unlocking them on a different one. The attacker managed to unlock tokens on various blockchains without locking the corresponding amounts on other blockchains.

Details

Poly Network project is a flexible project and allows general message passing across blockchains. It uses trusted entities called “keepers” to facilitate cross-blockchain transactions. The user originally does the transaction on a source blockchain, and it is repeated on the destination blockchain via Poly Network. The keepers sign blocks of the source blockchain that contains the original transaction. The user then submits this signed block to a smart contract (EthCrossChainManager) on the destination blockchain that checks the signature’s validity. If the signatures are valid, the contract executes a similar transaction on the destination blockchain.

This can be any transaction. There is no filtering done on transactions. Since this transaction is executed by EthCrossChainManager on the destination blockchain rather than the user, the users can impersonate EthCrossChainManager and do almost any transaction. This design makes it imperative to ensure that EthCrossChainManager does not have any special permissions that the users could exploit by impersonating it. However, EthCrossChainManager smart contract has the permission to change the keepers stored in the EthCrossChainData contract. This allowed the attacker to swap the original keepers for a malicious keeper controlled by the attacker.

Once the keeper was in the attacker’s control, the attacker could do arbitrary cross-chain transactions on the destination blockchains even if no such transaction took place on the source blockchain. The attacker did that by signing fake blocks of the source blockchain that contained arbitrary transactions of the attacker’s choice. These blocks were not valid blocks of the source blockchains. However, since EthCrossChainManager only ensures that the block is signed by the keepers rather than verifying the full block, it accepted them as valid blocks and executed the arbitrary cross-chain transactions inserted by the attacker.

The attacker created fake transactions that allowed them to unlock tokens on the destination blockchain without locking them on the source blockchain. The LockProxy contract manages the locking and unlocking of tokens. It unlocks tokens on the destination blockchain when called by the EthCrossChainManager of the source blockchain via a cross-blockchain call. The EthCrossChainManager contract creates such a call only if the user locks their tokens on the source blockchain. The attacker bypassed the lock by creating a fake call from EthCrossChainManager to the LockProxy using the malicious keeper. The LockProxy contract on the destination chain accepted the cross-chain call and unlocked the tokens. LockProxy assumed that the user has already locked the corresponding amount of tokens on the source blockchain.

Now, the source blockchain has tokens that were supposed to be locked but are actually unlocked. These tokens are no longer backed by any collateral. The attacker has basically managed to duplicate tokens across two networks. The original, valuable tokens are on the destination blockchain, under the attacker’s control. Other users are left with uncollateralized, duplicate tokens on source blockchains. As a result, people who have these tokens in any form (holding in their wallets, providing liquidity, etc.) are the ones who suffered a loss. The price of these bridged tokens (pTokens like pEth) is expected to fall sharply. It’s best to wait and let this situation play out because the attacker might refund the funds in which case, pTokens should go back to peg.

The root cause of this exploit is that the EthCrossChainManager contract is allowed to change the Keepers. It is expected behavior/risk that the whole protocol will be compromised if the keepers get compromised. In this case, the attacker managed to directly change who the keepers are rather than compromising the existing keepers. Imagine someone had the ability to change your password. Even if they didn’t know your password, they would just change it to one they know and then use that to log in.

Technical Analysis

  • The EthCrossChainManager contract has a verifyHeaderAndExecuteTx function that allows users to execute arbitrary cross chain transactions.
  • EthCrossChainManager verifies that the block containing this transaction has been signed by more than two thirds of the trusted keepers before executing the transaction.
  • The keepers are supposed to sign only the blocks that are real and exist on the source blockchain.
  • Once the user executes a transaction on source blockchain, they can take the real block from the source blockchain, have the keepers sign it and then do a cross-chain transaction on the destination blockchain using the keeper’s signature.
  • The arbitrary transaction is supposed to call only the functions that take (bytes,bytes,uint64) as parameters. The contract tries to ensure this by generating the target function signature using abi.encodePacked(_method, "(bytes,bytes,uint64)").
  • This is an incorrct security assumption which can be easily bypassed. Function signatures are only 4 byte long in solidity. They are meant to be generated by hashing the function name and parameters together but that is not enforced by the EVM. It is very easy to find collisions. For example, putCurEpochConPubKeyBytes(bytes) and f1121318093(bytes,bytes,uint64) generate the same function signature.
  • The list of trusted keepers is stored in the EthCrossChainData contract and can only be changed by the Owner of the contract by calling putCurEpochConPubKeyBytes(bytes).
  • Since EthCrossChainManager allows users to execute arbitrary transactions, it is not supposed to be owner of anything but was made owner of the EthCrossChainData contract. The attacker exploited a combination of this fact and incorrect function signature security assumption to make the EthCrossChainManager call EthCrossChainData’s putCurEpochConPubKeyBytes(bytes) function and change the keepers to a single keeper that they control.
  • The attacker did this by normally calling a function like f1121318093(bytes,bytes,uint64) on a source blockchain (Ontology Blockchain, Allegedly) that triggers the function signature collission. The transaction most likely reverted because such a function doesn’t exist in the EthCrossChainData contract. However, even reverted transactions are part of the block.
  • The keepers signed the block containing the attackers transaction because it was a legit transaction (Albeit it probably reverted).
  • The attacker then used this signature to do a cross-chain transaction on the destination blockchains (Ethereum, Binance Smart Chain, and Polygon). Since these transactions were dispatched via EthCrossChainManager, the onlyOwner check in EthCrossChainData was fulfilled and the trusted keepers were swapped to a malicious keeper.
  • Having control of the Keepers meant that the attacker could trick EthCrossChainManager contract into executing cross-chain transactions that aren’t even real on the source blockchain.
  • The LockProxy contract has an unlock function that accepts cross-chain calls from the EthCrossChainManager of source blockchains.
  • The attacker created fake blocks that had transactions from the EthCrossChainManager of soure blockchains, calling the unlock function of LockProxy.
  • the trusted keepers would not have signed such bogus blocks but since the attacker controls the malicious keeper, they signed the block.
  • The EthCrossChainManager verified that the block was signed by the keeper and then executed the transaction. This resulted in LockProxy unlocking and sending the tokens to the attacker.
  • The attacker repeated this with various tokens accross multiple blockchains.
  • Also worth noting that the EthCrossChainManager contract mandatarily fills the last two params of the arbitrary transaction with _fromContractAddr and _fromChainId params. This meant that the encoded keepers data was not ideally typed. The EthCrossChainManager contract use a custom decoding and deserializing scheme when extracting the keepers’ addresses from the encoded data. This attack was only made possible due to the lineant decoding and deserialization scheme. If the encoded data was type checked more strictly, the transactions shoudl’ve failed.

Addendum

The hack started as early as Aug-10-2021 09:33:07 AM +UTC and the drainage continued till at least Aug-10-2021 10:29:21 AM +UTC. If there were stricter monitoring and alerting mechanisms in place, the damage could potentially have been minimized as EthCrossChainManager is a pausible contract.

I originally posted a Twitter thread with my early findings. I alleged that only a single keeper was used, and that keeper got compromised because it was signing invalid blocks. This analysis was based on just one transaction that triggered a cross-chain unlock. I did not look at previous transactions and missed the crucial bit where the attacker swapped the trusted keepers with a malicious keeper.

My original thoughts were that this is a boring, traditional hack. Due to this, I didn’t bother analyzing more transactions. After learning that the attacker switched the keepers rather than compromising the keeper’s keys, it became a fascinating hack to analyze!

Right now, it seems like the attacker is negotiating a partial return of funds to Poly Network by sending messages over the Ethereum network. You can track the conversations via this google sheet created by a community member.

The attacker apparently has linked wallets that were used on centralized exchanges and people are trying to gather real information about the attacker from the exchanges. It is possible that the attacker used stolen accounts but it’s also possible that this direction of investigation will reveal the attacker’s real Identity. Earlier, the attacker posted some troll comments via their Ethereum address and also tipped a different troller. This is a bit unusual behavior since more interactions imply more chances of a slip-up. It is possible that the attacker has already slipped up and is now in damage control mode. It is also possible that they are just trolling. All of this is speculation at this point but quite interesting, nonetheless.

H/T kelvinfichter

Updates

  1. The attacker is now claiming to be a whitehat and is negotiating returning all funds. They have already refunded over $250 million.
  2. The hacker has returned all funds (except for the frozen USDT). They were offered a $500k bug bounty reward which they declined. The hacker can now be labelled as a white hat. Perfect example of a chaotic neutral, I guess.

Leave a Comment

Your email address will not be published. Required fields are marked *