On August 10, a hacker stole more than $600 million through Poly Network and a bizarre series of events unfolded in the aftermath.
In this article, Kraken Security Labs dives into the technical details of how the attacker was able to steal such a large amount of funds and the subsequent fix Poly Network implemented to ensure that this type of exploit does not occur in the future.
Mechanics of the Poly Network Hack
The Poly Network allows calls to contracts across chains: For example, before the hack, it was possible to call any method on any contract on Ethereum from Ontology.
Being able to call any public method on any contract is not in and of itself a bad thing – in theory, any Ethereum transaction could do that. However, on Poly Network, cross-chain transactions to Ethereum were executed by a contract called EthCrossChainManager (Manager), which has some special permissions.
Manager is the owner of another contract called EthCrossChainData (Data). Data oversees the golden eggs, also known as the “keepers,” which are the entities (identified by their public key) that manage Poly Network wallets. And Data exposes functionality to Manager to update the list of keepers.
The attacker found that, as any transaction is executed by Manager, he could update the list of keepers in Data. He then created a cross-chain transaction on Ontology that would get executed on Ethereum and set himself, and only himself, as the keeper of all Poly Network funds on Ethereum. This then gave him the necessary permissions to transfer all funds to his own wallet.
In the aftermath, Poly Network now limits which contracts and which methods can be called through cross-chain transactions, preventing the attack from occurring again.
The Poly Network, Simplified
Poly Network utilizes so-called “relayers” to forward information between chains. For example, to perform an Ontology to Ethereum cross-chain transaction, a user would interact with Poly Network’s cross-chain manager contract on Ontology. Then, a relayer takes the information from the smart contract and sends it to the Ethereum blockchain. Between both chains, the Poly Network chain syncs the current block headers of all participating chains.
Poly Network Smart Contracts
EthCrossChainManager is the Ethereum contract invoked on a cross-chain transaction. It takes the incoming transaction, validates it and then executes whatever method and target contract was specified in the transaction.
When a relayer transfers a transaction into Ethereum, it does so by using the Manager contract. The relayer will call the method verifyHeaderAndExecuteTx on Manager, which will validate the header of the transaction, ensure that the transaction has not already been executed, and then execute the transaction using the _executeCrossChainTx method.
This is where it gets interesting. There are no restrictions on which methods can be called by a cross-chain transaction. Any method name can be supplied, and will then be appended with the fixed argument list (bytes,bytes,uint64):
Basically, any method can be called that has the signature (bytes,bytes,uint64).
To execute the method, for example kraken(bytes,bytes,uint64), the contract converts it into a Solidity method ID, a 4-byte unique identifier of each method in a contract.
This method-ID is generated by keccak-hashing the method name, including the signature and then taking the first four bytes of the resulting hash:
While the hash itself is considered cryptographically secure, the truncation (only getting the first four bytes) of the hash opens it up to collisions. As we can freely choose the method name, we can brute-force a string that will generate the same method ID as another method – and with the 4-byte truncation, we very likely need less than 4,294,967,296 attempts (ignoring the birthday paradox, which would potentially make an attack even easier).
In short, we can call any method, with any argument signature, on any contract on Ethereum – and this is exactly what the Poly attacker did. He supplied the method name f1121318093, which produces the method ID 0x41973cd9. But, what’s so special about this method?
The Poly attacker discovered that Manager (the contract that will execute our method call) is also the owner of a contract called Data.
Manager controls some very important information: public keys belonging to Keepers, who are the entities that manage the funds in Poly Network wallets. With the Keeper’s private keys, the Poly hacker was able to move funds out of the Poly Network wallets into his own wallet.
The important point to note is that Data contains a method that lets the owner, Manager, of the contract replace the Keepers’ public keys.
The Solidity method ID that putCurEpochConPubKeyBytes has,
0x41973cd9, is the exact same method ID that the attacker-supplied method name f1121318093 resolved to.
As Manager is allowed to execute the method putCurEpochConPubKeyBytes, the attacker was able to upload his own public key, gaining full control over the Ethereum funds.
In the Ontology Transaction f771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c, the attacker performed a cross-chain transaction from the Ontology network to Ethereum, calling the method with the name f1121318093 on the contract EthCrossChainData. This method name was then converted into the Solidity ID for putCurEpochConPubKeyBytes.
Normally, putCurEpochConPubKeyBytes, being a privileged method of Data, couldn’t be called by any contract. But as Manager is both the owner of EthCrossChainData and is the context in which the cross-chain call is executed, the Poly hacker call was successfully executed.
By providing his own public key as the argument to putCurEpochConPubKeyBytes, the attacker became the only Keeper and in doing so gained full control of the Ontology funds. He then proceeded to move all Ethereum funds to his own wallet and then repeated the same for other networks, draining more than $610 million U.S. dollars in total.
The Poly Network Fix
In the aftermath of the attack the Poly Network developers published a fix to Manager. Instead of allowing any contract and methods to be called, there is now a whitelist of both, limiting what a cross-chain transaction can do.
A Collection of Vulnerabilities
The attack that resulted in the catastrophic loss of funds was made possible by a handful of design decisions and vulnerabilities:
- Data was owned by Manager, even though Manager has significant exposure and is used to call into externally-provided smart contracts.
- An implicit assumption was made that only methods with a (bytes,bytes,uint64) signature can be called.
- The ability to call any contract and any method allowed Manager to be used to access privileged methods on the contracts it owned.
In the aftermath, the attacker decided to return all funds to Poly Network, who then offered him a job as their Chief Security Advisor.
The Poly Network attack reveals the importance of securing smart contract solutions, and emphasizes the need for strong audits and other security testing measures when developing and implementing crypto protocols.
Kraken Security Labs is an elite team of security researchers that aims to protect and grow the cryptocurrency ecosystem by testing common third-party products and services; working with vendors to fix those issues; and informing the public about ways they can best protect themselves. Learn more about Kraken Security Labs and remember to always keep a security-first mindset.