On March 13, 2022, the SlowMist intelligence zone received news that the metaverse project Paraluni was the victim of a $1.7 million hack. Our security team conducted an analysis of the incident and tracked the stolen funds using our anti-money laundering software MistTrack. These are our findings:
Relevant information
Paraluni is a Metaverse (DeFi) project on the BSC chain where users can provide liquidity to earn yield.
The following are the relevant addresses involved in this incident.
Attacker address:
https://bscscan.com/address/0x94bc1d555e63eea23fe7fdbf937ef3f9ac5fcf8f
Attack transaction:
https://bscscan.com/tx/0x70f367b9420ac2654a5223cc311c7f9c361736a39fd4e7dff9ed1b85bab7ad54
Attack contract:
https://bscscan.com/address/0x4770b5cb9d51ecb7ad5b14f0d4f2cee8e5563645
UBT (Ukraine bad token) token contract:
https://bscscan.com/address/0xca2ca459ec6e4f58ad88aeb7285d2e41747b9134
UGT (Russia good token) token contract:
https://bscscan.com/address/0xbc5db89ce5ab8035a71c6cd1cd0f0721ad28b508
Masterchef contract:
https://bscscan.com/address/0xa386f30853a7eb7e6a25ec8389337a5c6973421d#code
Cause of Incident
The depositByAddLiquidity function within the MasterChef contract does not check whether the LP token constructed by the incoming token array parameter _tokens corresponds to the specified _pid parameter and is consistent with the LP token (USDT-BUSD LP) in the pool.
There is also no reentrancy restriction when adding liquidity calculations, leading to the use of malicious contracts to conduct reentrancy attacks.
Detailed Analysis
1. The attacker first created ERC20 token contracts called UBT and UGT with a revised transferFrom function.
2. Then they borrowed about 157,000 USDT and 157,000 BUSD from PancakeSwap via a flash loan.
3. The attacker then addedLiquidity to the ParaRouter contract and minted 155,000 Paraluni LP tokens on the UBT contract.
4. In the MasterChef contract, call the depositByAddLiquidity function with the incoming _pid argument set to 18. Provide 1 UBT token and 1 UGT token to the pool as liquidity. The depositByAddLiquidityInternal function is called inside the function below.
In the depositByAddLiquidityInternal function, the LP token is created by calling the addLiquidity function in the paraRouter contract with the incoming _tokens token. We can see that the function does not check whether the pool token (USDT-BUSD LP) corresponding to _pid matches the LP token constructed using the incoming _tokens.
5. The depositByAddingLiquidityInternal function calls the addLiquidityInternal function internally. The addLiquidityInternal function first obtains the LP token balance in the contract address and records it as vars.oldBalance. Then, it calls the addLiquidity function of the paraRouter contract and obtains the LP token in the contract address again. This time the balance is recorded as vars.newBalance. Finally, it uses the vars.newBalance-vars.oldBalance formula to calculate the number of deposits required and calls the _deposit function to deposit LP tokens.
The addLiquidity function of the ParaRouter contract will call the transferFrom function in the UBT token contract created by the attacker. This is a malicious contract that will call the deposit function to transfer the LP token to the UBT contract. Because the value of vars.oldBalance was not updated when the contract _deposit was deployed, the UBT token contract and the attack contract will both account for 155,000 LP tokens, causing a re-entrancy attack.
6. To extract LP tokens into the malicious contract, the attacker calls the withdrawAsset function in the UBT contract and uses the attack contract to execute the withdraw function in the Masterchef contract.
7. The attacker removed all liquidity and received 310,000 in USDT and 310,000 in BUSD.
MistTrack Analysis
According to MistTrack, the initial 0.99785 BNB from the hacker’s address came from Tornado.Cash.
Following the incident, the exploiter exchanged BNB for ETH. They then cross-chained ETH from BSC to Ethereum and finally deposited 660 ETH into Tornado in 12 installments.
The Paraluni team reached out to the hacker on-chain after the incident. These were some of their conversation listed below:
Paraluni team:
Response from the hacker:
The team also offered to overlook this incident as white-hat behavior, as well as reward the hacker for finding this exploit if they promised to return the stolen funds. However, no response has been made from the hacker.
Summary
The main cause of this incident was a vulnerability in the depositByAddLiquidity function within the Masterchef contract. The contract did not check the LP tokens constructed by the pool tokens corresponding to the specified _pid and the tokens in the incoming_tokens array. Since there is no restriction on reentrancy locks when the property is added, this leads to the use of LP tokens being registered.
The SlowMist security team recommends that when performing sensitive operations such as deposits, the pool token corresponding to the specified_pid and the incoming_token should be checked for consistency, as well as functions that involve changes in the number of LP tokens, such as adding liquidity. By creating restrictions on reentrant locks, it can help prevent attacks like this in the future.