The following steps are used by Inspex to understand the incident that happened to Poly Network. In this article, we will describe how we investigated the situation from the transaction and our brief observations, not the analysis of the vulnerability or the attack.
For the attack detail, please see in the following tweet (very clever):
The following transaction was used to analyze the incident:
1. Inspex team started the investigation from the address that sent tokens back to the attacker (0x2f7ac9436ba4b548f9582af91ca1ef02cd2f1f03) and found that it is a Lock Proxy contract according to https://github.com/polynetwork/docs/blob/master/config/README.md.
2. Using Tenderly Debugger (https://dashboard.tenderly.co/tx/bsc/0xd59223a8cd2406cfd0563b16e06482b9a3efecfd896d590a3dba1042697de11a/debugger?trace=0.4), Inspex Team found that a function with the signature “0x06af4b9f” was called in 0x2f7ac9436ba4b548f9582af91ca1ef02cd2f1f03.
3. From the source code of
LockProxy contract, we checked the signature “0x06af4b9f” of the function called and found that it is the
unlock() function (https://github.com/polynetwork/eth-contracts/blob/d16252b2b8/contracts/core/lock_proxy/LockProxy.sol#L102-L119). This function is used to mint tokens in the destination chain according to the amount burned from the bridging which is used by the attacker to drain the locked assets.
4. However, the
unlock() function can be called by the address in
managerProxyContract only, and it is stored in storage slot 1 of the
LockProxy contract (0x2f7ac9436ba4b548f9582af91ca1ef02cd2f1f03).
5. By observing storage slot 1, we found that the stored address is 0x7cea671dabfba880af6723bddd6b9f4caa15c87b that matches with
EthCrossChainManager contract by comparing their function signatures (https://github.com/polynetwork/eth-contracts/blob/master/contracts/core/cross_chain_manager/logic/EthCrossChainManager.sol)
6. From Tenderly, we found that the function with the signature “0xd450e04c” was called. By looking up the signature, we found that it is the
verifyHeaderAndExecuteTx() function (https://github.com/polynetwork/eth-contracts/blob/master/contracts/core/cross_chain_manager/logic/EthCrossChainManager.sol#L127-L173)
7. If every conditions inside the
verifyHeaderAndExecuteTx() function are fulfilled, the
_executeCrossChainTx() function will be executed, allowing the execution of other contract functions through the
8. After an analysis in the previous step, we found that
verifyHeaderAndExecuteTx() function will verify a signature in the
headerSig parameter using
ECCUtils.verifySig() function will verify that the header has been signed by n — ( n — 1) / 3 node of Poly chain consensus (n is the number of keys that has been defined in the contract)
10. For the keepers, these keepers have been stored in contract at 0x11e2A718d46EBe97645b87F2363AFE1BF28c2672 which is the
EthCrossChainData contract (https://github.com/polynetwork/eth-contracts/blob/master/contracts/core/cross_chain_manager/data/EthCrossChainData.sol) in the
ConKeepersPkBytes state variable.
11. After inspecting
ConKeepersPkBytes, we got a value
0x010000000000000014a87fb85a93ca072cd4e5f0d4f178bc831df8a00b00003a which begins with 8-byte data that represents a number of keeper, 0100000000000000 can be converted to 1 with Little-Endian conversion.
12. The contract owner of
EthCrossChainData contract (0x11e2A718d46EBe97645b87F2363AFE1BF28c2672) is the same with
LockProxy contract that is
EthCrossChainManager contract (0x7cea671dabfba880af6723bddd6b9f4caa15c87b).
- Only the people who own the private key of the public key set in the
EthCrossChainDatacan pass the validations in the
- The keeper can be set using the
putCurEpochConPubKeyBytes()function, which can only be executed by the contract owner (https://github.com/polynetwork/eth-contracts/blob/master/contracts/core/cross_chain_manager/data/EthCrossChainData.sol#L45-L48).
- The contract owner of
EthCrossChainDatacontract is also the
EthCrossChainManagercontract. This means that anyone who can send the cross-chain transaction through the
EthCrossChainManagercontract, is able to execute
putCurEpochConPubKeyBytes()function and change the keeper value (public key) in the
- After the incident, In the
EthCrossChainDatacontract, there was only 1 keeper stored. This means that the
verifyHeaderAndExecuteTx()function can be called using just one holder of the private key.
Inspex is formed by a team of cybersecurity experts highly experienced in various fields of cybersecurity. We provide blockchain and smart contract professional services at the highest quality to enhance the security of our clients and the overall blockchain ecosystem.