Back to all stories
Reports
Incident Analysis
Paraluni Exploit
7/9/2022
Paraluni Exploit

TL;DR

Paraluni's MasterChef contract was attacked on March 13, 2022, at 12:04:30 AM +UTC. The attacker exploited a reentrancy vulnerability within the project’s smart contract. Around $1.7M was stolen from the project through multiple transactions. The Paraluni hack was made possible by a reentrancy vulnerability within the contract’s depositByAddLiquidity function.

Event Summary

On March 13 2022, Paraluni, the Metaverse Financial project, was the victim of a “classic” re-entrancy attack.

The depositByAddLiquidity function calls an internal depositByAddLiquidityInternal function that transfers the attacker’s deposit into the appropriate pool. However the pool ID value (_pid) used to look up the appropriate pool was not valdiated internally.

The attacker took advantage of this by directing this to an attacker-controlled contract, whose malicious transferFrom function is called. This function then exploits the reentrancy vulnerability to call the Masterchef deposit function before the internal state is updated. Between the initial and malicious deposits, the attacker is credited with excess tokens and able to extract more value from the contract than they deposited. The Paraluni team reached out to the hacker on-chain after the incident as seen in some of the conversations screen shotted below:

Paraluni team: 1*4UD-IW0O5B1XO9BHA9oZJw

Response from the hacker: 1*0fyhdYOGa5gwUehSCQD Og

Paraluni announced that they would overlook this exploit and consider it as a white-hat hack and offered the attacker a reward for finding this exploit if he promised to return the stolen funds. However, their attempt failed as there was no response.

Attacker address: 0x94bc1..

Attack transaction: 0x70f36..

Attack contract: 0x4770b..

Masterchef contract: 0xa386f..

Attack Technical Analysis

Preparation Stage:

The attacker first created ERC20 token contracts called UBT and UGT with a revised transferFrom function. Within the UBT token contract, there are two malicious implementations:

  • Within the transferFrom() function, the attacker implement an invocation to MasterChef’s deposit() function to deposit LP tokens.

  • A “withdrawAsset()” function that will call Masterchef’s “withdraw()” to withdraw LP tokens deposited.

Exploit Stage:

The attacker first borrowed about 157,000 USDT and 157,000 BUSD from PancakeSwap via a flash loan.

1*JwRNGZMBu86Pirk MTFmIw

The attacker then transferred flashloaned tokens to ParaPair and received 155,935 LP tokens in return 1*6x2lfKzbXfaSsTwr QC6wA

The exploiter called “depositByAddLiquidity()” function to 0x55d39.. deposit LP tokens to the pool:

The input parameters “_pid” is 18 and “_tokens” is [UGT,UBT], which are created in the preparation stage.

HpcIR3rsNQ1AmkzZ25vowlw7DeJkWjHy Hkr1qC3q75R 3M8tztNiUXz5db03Bcv z0MIR0L1P9qL PbKo8c06naQ SDxcVrICujyCo1qPXleU0FjU7SVnCLRRrxN75S9K7OErTFAOVqZk7jiganqA

As depositByAddLiquidity() will invoke the “UBT.transferFrom()”, MasterChef.deposit() was invoked to deposit 155,935 LP tokens to the contract. c3DLBWl8VkOpB4l62QSbsL23NrZdOxDoEmw69k12kVwwd4fdcLZbwyNOv98Yclpe6gr6ZOI9jzM166Z9Y3TmRa5pNGqfDR-dtzjlMAxZUWeaE 6Vi86l1Ip2-RR82WjI7VZJ1oqINmGb9P1-IQnlbg

In this case, 155,935 LP tokens are deposited twice and the attacker got two separated “userInfo” records (one is from the UBT, the other is from the attacker’s address)

The attacker withdrew twice: a. One is via the “UBT.withdrawAsset()”. b. The other is from the attacker’s invocation to “Masterchef.withdraw()”.

Finally, the attacker removed the liquidity, returned the flashloans, and received 310,000 in USDT and 310,000 in BUSD. 1*gFM673G 2k-z7wR4SLD7jA

Contract Vulnerability Analysis

In the function MasterChef.depositByAddLiquidity(), the passed _tokens does not match with the tokens in the pool with _pid, which should be validated.

w39vDbSQa-7Ycyf1tWsC0dD5LF1jD4YxbBnzQqsqYHMdWJdl-aRg-IziXdE9eu1PVWueQldWDJ8Tcil-mfdurqDvktDaTcf1i9RnfTdU8OIrugkrTjrAxytIKc 5 O5HrCtap0hYhqxMKdRvrmtBlQ

8KHWNnUJRgfsi0QMYJaTAwUF-LR0JWuA6slHgal4ZJYWHx6lF9pjsjaOr-yIG2UoaLq-sZm1ce7y8VbYZwPnncjdPhWiQHueccpvpdxBdbmt6L7Y3ymJHKDeXavCR53jdwQ4w3D0eGhNxVzqThJX1w

The function depositByAddLiquidity() calls depositByAddLiquidityInternal() which triggers the function depositByAddLiquidityInternal() and addLiquidityInternal(). TiIoG98UUbH Sh-1x6o pYivfQI9WBGIlIF -jNhov ABvLINhWgr4qHNmG O5eUXkzWQHf921Q9h4xvw6i9Uz KuQY4cdnOc0nzRiGGLdoNrgSXUsa4J8BM c9BC3dxZZkBeai-tV1LgIJA9zNjfA

The addLiduidityInternal() will call the malicious token contract UBT’s transferFrom() to invoke the MasterChef.deposit(). ySIMqFKgjBSLJ1Sj-XaqEZaQUrycdaziU7KU6g853Gx06WDf1JA8n7qZLT1hLYMzCdKEgDWt9YAA8GvGn hNY1ThdS0gEJcVslG4PDj8ZoT-5eji2ckpVCVgfc6T5Nb4jpIU6eKXBoV7TnilvzIBHQ

Profit and assets tracing

Following the incident, the exploiter exchanged BNB for ETH. The attacker then cross-chained ETH from BSC to Ethereum and finally deposited 660 ETH into Tornado in 12 instalments. In total ~$1.7M worth of asset was stolen.

image-20220709-084054

Conclusion:

The hack was made possible mainly due to the logic flaws in the contract code and lack of reentrancy prevention in the key functions for fund operations. The two vulnerabilities at stake here were: failing to validate user input (_pid) and a reentrancy vulnerability. Both of these are well-known types of vulnerabilities and could have been detected and prevented by a smart contract audit.