Cross chain bridge alarm: How does Polygon prevent the opening of Pandora's Box
Deeply analyze the Polygon network structure, interoperability and cross chain message transmission mode, and then talk about the security incidents of Shuanghua vulnerabilities.
1 Who is Polygon?
Polygon is the layer2 expansion plan of Ethereum. Its vision is to build the blockchain Internet of Ethereum. Polygon provides a general framework that allows developers to use the security of Ethereum to create customized, application focused chains, and provides an interoperable network, combining various expansion schemes, such as zk rollup, PoS, etc. Among them, Polygon PoS is the most mature and well known capacity expansion scheme on Polygon at present. It uses the side chain for transaction processing to improve transaction speed and save gas consumption. The network structure mainly includes the following three layers:
A series of contracts on the main Internet of Ethereum, mainly including Staking, Checkpoint and Reward contracts, are responsible for the pledge management functions related to PoS rights and interests, including: providing the pledge function of MATIC native token, so that anyone who pledges the token can join the system as a verifier; Verify that the transition of Polygon network is rewarded with pledge; Punish the double signature of the verifier, the downtime of the verifier and other illegal acts; Save the checkpoint.
The proof of equity verification node layer, including a group of PoS Heimdall nodes, is responsible for submitting the checkpoint of the Polygon network to the Ethereum main network, and monitoring a group of pledge contracts deployed on Ethereum. The main process is as follows: First, select a part of the active verifiers in the verifier pool as the block producers, who will be responsible for creating blocks in the Bor layer and broadcasting them; Then, according to the checkpoint submitted by Bor, verify the Merkle root hash and attach a signature; Finally, the proposer will be responsible for collecting the signatures of all verifiers at the designated checkpoint. If the number of signatures reaches more than 2/3, the checkpoint will be submitted on Ethereum.
The outbound node layer includes a group of block producers regularly selected by the verifier committee on the Heimdall layer. They are a subset of verifiers responsible for aggregating transactions on the Polygon side chain and generating blocks. This layer will periodically publish checkpoints to Heimdall layer, where the checkpoint represents a snapshot on the Bor chain, as shown in the following figure.
2 Polygon Interoperability
Checkpoint mechanism is a mechanism to synchronize Bor layer data to Ethereum. The synchronized data is a checkpoint, that is, a snapshot of Bor layer block data included in a checkpoint interval. The source code is as follows:
Proposer: The proposer is also selected by the verifier. Both the block producer and the proposer are subsets of the verifier, and their responsibilities depend on their equity ratio in the whole pool.
RootHash: Merkle Hash generated by Bor block between StartBlock and EndBlock.
The following is the pseudo code of RootHash value generated by Bor blocks numbered 1 to n:
To sum up, this value is the root hash value of the Merkel tree composed of the block number, the block timestamp time, the transaction tree root hash value tx hash, and the receipt tree root hash value receive hash in the Bor block header.
AccountRootHash: Each checkpoint needs to be sent to the Merkle Hash of the relevant account information of the verifier on Ethereum. The hash value of a single account information is calculated as follows:
The way to generate AccountRootHash from the root hash value of the account Merkle tree is the same as the RootHash value.
StateSync refers to the synchronization of Ethereum data to the Polygon Matic chain, which is mainly divided into the following steps:
First, the contract on Ethereum will trigger StateSender The syncState() function in sol synchronizes the state
The syncState() function will issue an event event, as follows:
All verifiers of Heimdall layer will receive the event, and one of them will package the transaction into heimdall block and add it to the pending status synchronization list;
The bor layer node will obtain the above list to be synchronized through API, and submit it to the bor layer contract for further business logic processing.
2.3 Polygon Bridge
Polygon Bridge implements a two-way cross chain channel between Polygon and Ethereum, enabling users to transfer tokens between two different chain platforms more easily without third-party threats and market liquidity restrictions. There are two types of Polygon Bridge: PoS and Plasma. The asset transfer between Polygon and Ethereum has the following similarities:
1) First, you need to map the token on Ethereum to Polygon, as shown in the following figure:
2) Two way Peg is also adopted, namely
a: Token assets transferred from Ethereum will be locked on Ethereum first, and the same number of mapped tokens will be cast on Polygon;
b: In order to extract token assets to Ethereum, you need to burn these mapping tokens on Polygon first, and then unlock the assets locked on Ethereum;
The following figure shows the comparison between PoS Bridge and Plasma Bridge:
It can be seen from the above figure that in terms of security, PoS Bridge depends on the security of the external verifier set, while Plasma depends on the security of the Ethereum main chain. At the same time, when users transfer cross chain assets (such as transferring tokens from Polygon to Ethereum), PoS only needs one checkpoint interval, about 20 minutes to 3 hours; Plasma needs a 7-day dispute and challenge period. At the same time, PoS supports more standard tokens, while Plasma only supports three types, including ETH, ERC20, and ERC721.
3 Cross chain messaging PoS Bridge
The PoS Bridge mainly includes two functions: Deposit and Withdrawings. Deposit refers to transferring users' assets in Ethereum to Polygon, and Withdrawings refers to extracting assets from Polygon to Ethereum.
The following is an example of how Alice uses PoS Bridge to send token assets from her Ethereum account to her Polygon account:
1. If the token assets you want to transfer are ERC20, ERC721, and ERC1155, you need to first authorize the tokens you want to transfer through the approve function. As shown below: authorize the corresponding number of tokens to the erc20Predicate contract by calling the approve method in the token contract on Ethereum.
The approve function has two parameters:
Spender: the target address where the user is authorized to spend tokens
Amount: The number of tokens that can be spent
2. After the above authorization transaction is confirmed, the user then locks the token into the erc20Predicate contract on Ethereum by calling the depositFor() method of the RootChainManager contract. Here, if the transferred asset type is ETH, the depositEtherFor() is called. The details are as follows:
The depositFor function has three parameters:
User: the user address to receive the deposit token on Polygon
RootToken: the token address on the Ethereum main chain
DepositData: the number of tokens encoded by ABI
The following is the specific code of the depositFor function in the RootChainManager contract:
According to the analysis of the source code, this function first obtains the predicate contract address corresponding to the token, and then calls its lockTokens() function to lock the token in the contract. Finally_ StateSender will call syncState() to synchronize the state. This function can only be called by the state sender set by admin.
3、StateSender. The syncState() function in sol will submit the event StateSynchronized, specifically:
The first parameter is the serial number index of the log, the second parameter is used to verify whether the caller is a registered legal contract address, and the third parameter is the data to be synchronized. The transaction is added to the Heimdall block and to the pending status synchronization list.
4. Then, after the bor node on the Polygon Matic chain obtains the StateSynchronized event in the state synchronization list through the API, the ChildChainManager contract on the chain will call the onStateReceive() function, which is used to receive the synchronization data uploaded from Ethereum, and perform the next step according to the business logic type of the same state:
Data: includes syncType of bytes32 type and syncData of bytes type. Among them, syncType represents the business type, including deposit and mapping token mapping; When syncType is mapping, syncData is the encoded rootToken address, childToken address and bytes32 type tokenType; When syncType is deposit, syncData is the encoded user address, rootToken address, and depositData of bytes type. The depositData in REC20 is the quantity, and ERC721 refers to the tokenId.
5. Because this is the Deposit business, the_ The syncDeposit() function. This function will first decode syncData in the corresponding format to obtain the corresponding rootToken, user address, and depositData. Then check whether the rootToken has a corresponding mapping token, childToken, on the polygon. If so, call the childToken's deposit() function.
6. Here we take the token contract of ERC20 as an example to introduce how to dispose the mapping token contract. This function transfers the number of tokens corresponding to mint to the user account.
This function has two parameters:
User: the address of the user who is depositing
DepositData: amount encoded with ABI
The following is an example of how Alice uses PoS Bridge to withdraw the funds deposited in her Polygon account to Ethereum account:
1. When users withdraw, they need to first burn out the corresponding number of mapped tokens on the Polygon chain by calling the withdraw () function that maps the token contract.
Withdraw only contains one parameter: the number of tokens to be burned. The withdraw () function in the corresponding token contract is as follows:
2. The above transaction will be included in the checkpoint after about 20 minutes to 3 hours, and the verified person will submit it to Ethereum.
3. Once the transaction is added to the checkpoint and submitted to Ethereum, the exit () function of the RootChainManager contract on Ethereum will be called. This function will confirm the validity of the withdraw transaction on Polygon by verifying the content of the checkpoint submitted, and trigger the corresponding Predicate contract to unlock the token deposited by the user.
Among them, Proof passed into the function proves that inputData includes the following data:
HeaderNumber: header of checkpoint block containing withdraw transaction
BlockProof: prove that the block header in the sub chain is the leaf node of the submitted merkle root
BlockNumber: the block number containing withdraw transaction on the sub chain
BlockTime: block timestamp of withdraw transaction
TxRoot: root value of block transaction tree
ReceiptRoot: root value of the block receipt tree
Receipt: receipt of withdraw transaction
ReceiptProof: Merck certificate of withdraw transaction receipt
BranchMask: the 32 bit receipt path in the receipt tree
ReceiptLogIndex: the log index read from the receipt tree
The following is the core logic of the function, which mainly includes three parts: the first part is to verify the validity of the withdraw transaction receipt, the second part is to verify whether the checkpoint contains the transaction block, and the third part is to call the exitTokens() function in the predicate contract to send the locked token to the user.
4. Take the ERC20Predicate contract as an example, that is, after decoding the number of tokens for the receiver, sender and sender from the log, the given number of tokens will be sent to the user.
According to the source code analysis of PoS Bridge's cross chain messaging process, the function calls of the whole process can only be called by the role designated by the verifier, so the cross chain security is only guaranteed by PoS (notary).
4 Cross chain messaging - Plasma Bridge
Plasma Bridge also includes two functions: Deposit and Withdrawings. The specific process is shown in the following figure:
Polygon Plasma is slightly different from the Bitcoin Plasma MVP implementation introduced in the first article of our cross chain bridge series, which mainly uses the Plasma MoreVP based on the account model. Compared with Plasma, this algorithm is mainly improved in the withdraw part.
Since the token transmission of ERC20 and ERC721 is realized through an event log similar to Bitcoin UTXO, let's first introduce this event:
Input1: sender's account balance before transfer
Input2: receiver's account balance before transfer
Output1: sender's account balance after transfer
Output2: receiver's account balance after transfer
Secondly, since the original Plasma MVP blocks are generated by a single operator or a few block producers, there are two attack scenarios on Polygon:
Operators do evil:
Previous article（《Cross chain bridge safety review: What inspiration does Nomad decentralized robbery give us?》）It is mentioned that when a user's transaction is packaged as a Plasma block by the Operator, there is a problem of the unavailability of the off chain data. Therefore, if the user exits from an older transaction during an exit transaction, the operator can use its latest transaction to challenge the user, and the challenge will succeed. At the same time, since the PoS checkpoint mechanism is used in Plasma, if the operator colludes with the verifier to do evil, it can even forge some state transitions and submit them to Ethereum.
Users do evil:
After the user initiates an exit transaction, he/she continues to spend tokens on Polygon, similar to the cross chain double flowers.
To sum up, Polygon's Plasma MoreVp algorithm uses another algorithm to calculate exit priority, that is, exit from the latest transaction. This method uses a LogTransfer event similar to UTXO. As long as the user's legitimate transactions use the correct input1 and input2, even though some malicious transactions of the Operator are packaged before the user's transaction, the user's transaction can be processed correctly because it only comes from the valid input. The relevant pseudo codes are as follows:
The following is an example of how Alice uses Plasma Bridge to send token assets from her Ethereum account to her Polygon account:
1. First, the user also needs to authorize the token assets to be transferred to the Polygon contract depositManager on the main chain (Ethereum) through the approve function.
2. After the authorization transaction is confirmed, the user calls erc20token The deposit() function triggers the depositERC20ForUser() function of the depositManager contract and stores the ERC20 token assets of the user.
3. When the Ethereum main network confirms the deposit transaction, it will then create a block containing only this transaction, and send it to the childChain contract on the Polygon network using the state synchronization mechanism. Mint the same amount of mapped currency and deposit it in the user's account on Polygon.
Note: According to the analysis of the source code of the childChain contract, Plasma only supports three types, including ETH, ERC20 and ERC721.
When users want to use Plasma bridge to extract assets from Polygon to Ethereum, they will go through the following steps:
1. The user can burn out the mapped token assets on the Polygon chain by calling the withdraw () function of mapped coins on Polygon:
You can also call the withdrawStart() interface implementation of Plasma Client on Polygon.
2. Users can call the startExitWithBurntTokens() function in ERC20Predicate contract, which first calls WithdrawManager VerifyInclusion() verifies whether the checkpoint contains the withdraw transaction and the corresponding receipt. The code is as follows:
After the verification is passed, WithdrawManager will be called AddExitToQueue() inserts them into the message queue by priority:
Finally, addExitToQueue() calls_ AddExitToQueue() casts an NFT as a refund voucher:
3. 7 day challenge period for users
4. After the challenge period is completed, you can call WithdrawManager The processExits() function sends tokens to the user.
This function is mainly divided into two steps: first, confirm whether the withdraw transaction in the message queue has passed the 7-day challenge period. If it has passed the challenge period, remove the transaction from the queue:
Next, judge whether the refund voucher NFT has been deleted during the challenge period. If not, destroy the NFT and return the corresponding asset to the user:
5 Polygon Plasma Bridge Double Flower Vulnerability
On October 5, 2021, Gerhard Wagner, the white hat, submitted a Polygon vulnerability, which may lead to a double flower attack, involving an amount of 850 million dollars. Therefore, the white hat received an official Polygon vulnerability reward of 2000000 dollars.
In the introduction of Plasma Bridge above, we know that a complete Withdraw transaction process is:
The user initiates a Withdraw transaction on Polygon, which will burn the user's token in Polygon;
After a checkpoint interval (about 30 minutes), wait for the withdraw transaction to be included in the checkpoint;
More than 2/3 of the verifiers sign their names and submit them to Ethereum. At this time, the user calls startExitWithBurntTokens() in ERC20PredicateBurnOnly contract to verify whether the checkpoint contains a burn transaction;
If the verification is passed, an NFT refund voucher will be forged and sent to the user
The user waits for a challenge period of 7 days
Call WithdrawManager ProcessExits() destroys the NFT and refunds it to the user
Note: To prevent transaction replay (double flower attack), Polygon uses NFT as the refund voucher to uniquely identify a Withdraw transaction. However, due to the ID generation flaw of NFT, attackers can construct parameters to generate multiple NFTs with different IDs by using the same valid Withdraw transaction, and then use these NFTs for refund transactions, thus realizing "dual flower attack".
How to generate NFT is described in detail below:
1. From the source code analysis above, addExitToQueue() will call_ AddExitToQueue() casting an NFT:
It can be seen from the parameter transfer analysis that exitid=priority, then the ID of NFT is generated by shifting the age priority left one bit in the Plasma Bridge.
2. According to the above source code analysis, age is WithdrawManager The return value of the verifyInclusion() function. This function will first verify the validity of the withdraw transaction. If the verification is passed, the corresponding age will be generated. In the verification logic, the value branchMaskBytes decoded from the controllable parameter data is used:
This value is also used when generating age:
3. MerklePatriciaProof that tracks calls in the transaction validation logic The verify () function is found to call_ GetNibbleArray() transcodes branchMaskBytes:
4. Continue to track the decoding function. The function discards some values when transcoding branchMaskBytes. This way of losing values will cause different values to be transcoded to obtain the same decoding value. Specifically, if the first hexadecimal bit (half byte) of the incoming hp encoded value b is 1 or 3, the second hexadecimal bit will be parsed. Otherwise, the first byte is ignored.
If an attacker constructs a branchMaskBytes parameter so that the first hexadecimal bit is not equal to 1 and 3, there are 14 * 16=224 ways to obtain the same transcoded value.
The specific attack process is as follows:
Deposit a large amount of ETH/tokens to Polygon via Polygon Plasma
Launch a Withdraw transaction on Polygon and wait for a 7-day challenge period
Modify the first byte of the branchMaskBytes parameter in the withdraw transaction (the same valid transaction can be resubmitted for up to 223 times), and initiate the Withdraw transaction repeatedly
To sum up, this vulnerability is mainly due to a problem in the ID algorithm design of the NFT that generates the refund voucher to prevent replay, which leads to different NFTs generated from the same refund transaction, resulting in a dual flower attack. It has been proved that the first byte of the encoded branch mask should always be 0x00. The fix is to check whether the first byte of the encoded branch mask is 0x00 and not treat it as an incorrect mask.
Original title: Research on Safety of Chain Bridge (III) | Polygon Warrior Polygon Safety Dialysis, How to Prevent the Opening of Pandora's Box
Written by: Chengdu Lian'an
Source: Foresight News