SlowMist: An Analysis of the Attack on Popsicle Credentials (Released in 2021)

SlowMist
8 min readMay 9, 2023

--

On August 4th, 2021, according to SlowMist Zone, Popsicle Finance’s Sorbetto Fragola product, a cross-chain yield aggregator, was attacked by hackers. SlowMist’s Security Team promptly intervened in the analysis and shared the results as follows.

Attack Background

In this attack, the attacker completed the attack on Sorbetto Fragola by creating three attack contracts. The following are the specific addresses involved in this attack:

Attacker:

H1: 0x3A9D90eD069021057d9d11E78F142F2C4267934A

H2: 0xf9E3D08196F76f5078882d98941b71C0884BEa52

Attack contracts:

C1: 0xdFb6faB7f4bc9512d5620e679E90D1C91C4EAdE6

C2: 0x576cf5f8ba98e1643a2c93103881d8356c3550cf

C3: 0xd282f740bb0ff5d9e0a861df024fcbd3c0bd0dc8

Sorbetto Fragola:

0xc4ff55a4329f84f9Bf0F5619998aB570481EBB48

Attacked Target

According to the official introduction, Sorbetto Fragola is mainly used to help users manage their Uniswap V3 positions, so as to avoid users exceeding their chosen price range for Uniswap V3 liquidity provision. Users can deposit two types of tokens corresponding to the liquidity they provide in Sorbetto Fragola, and will receive Popsicle LP (PLP) credentials from Sorbetto Fragola. Users can use these credentials to obtain rewards and withdraw their liquidity funds, and these credentials can also be freely transferred to other users.

Core of the Attack

The core of this attack is that Sorbetto Fragola calculates the rewards users can receive based on the number of PLP credentials they hold, and these credentials can be freely transferred to other users without any reward calculation or transfer operation during the credential transfer process. This means that as long as a user holds a PLP credential, they can immediately receive rewards, resulting in the same PLP credential generating revenue for multiple holders at the same time. Next, we will analyze the details of the attack.

Attack Details

The attacker first created attack contracts C1, C2, and C3 through address H1, and then used address H2 to call attack contract C1 to start the attack, with the following transaction:

0xcd7dae143a4c0223349c16237ce4cd7696b1638d116a72755231ede872ab70fc.

By analyzing this transaction, we can see that the attacker first borrowed 30,000,000 USDT, 13,000 WETH, 1,400 WBTC, 30,000,000 USDC, 3,000,000 DAI, and 200,000 UNI through flash loans from AAVE to prepare for providing liquidity and obtaining PLP credentials in Sorbetto Fragola.

Later, the attacker called the deposit function of the Sorbetto Fragola contract to deposit the two tokens that provide liquidity (using WETH and USDT deposited by the attacker as an example). The checkDeviation and updateVault modifiers are used to check the price and update the reward, respectively. The price check is mainly for detecting situations where the price has been manipulated by large fluctuations, which will not be discussed here. The reward update is closely related to this attack, so we will focus on it.

We can see that it calls the _updateFeesReward function for the specific update operation. Let’s follow this function:

From the above figures, we can easily see that it first uses the positionLiquidity function to obtain the amount of liquidity held by the contract within the tickLower and tickUpper range. Then, it uses the _earnFees function to collect liquidity provider rewards from the Uniswap V3 Pool. After that, it calculates the liquidity provider rewards that each PLP token can receive through the _tokenPerShare function. Finally, it uses the _fee0Earned and _fee1Earned functions to calculate how much rewards a user can get with their PLP tokens, and records them using the user.token0Rewards and user.token1Rewards variables, as shown in the figure.

But at this point, the attacker had just made a deposit and had not yet obtained any PLP tokens, so their user.token0Rewards and user.token1Rewards variables were naturally recorded as 0.

By now, you may have realized the problem. Since the rewards recorded in the user.token0Rewards and user.token1Rewards variables are calculated based on the user’s holding of PLP credentials, and PLP credentials can be transferred, can’t we simply hold PLP tokens and trigger the recording of rewards in these variables to obtain rewards? The answer is yes. Let’s continue examining the deposit function:

After the reward update, the liquidity provided by the user within the target price range is calculated using the liquidityForAmounts function, and the Uniswap V3 Pool mint function is called to inject liquidity. Then, the _calcShare function is used to calculate the number of PLP credentials that Sorbetto Fragola needs to mint for the user.

As we expected, after the attacker obtained the PLP credentials, they transferred them to other addresses and called the Sorbetto Fragola contract’s collectFees function to record the rewards.

Through the PLP transfer records shown in the above figure, we can see that after the attack contract C1 obtained the PLP credential, it transferred it to the attack contract C2, and then called the collectFees function. Later, the attack contract C2 transferred the PLP credential to the attack contract C3 and called collectFees again. Finally, the attack contract C3 transferred the PLP credential back to the attack contract C1. Let’s analyze the collectFees function:

As we can easily see from the above figure, this function also has an updateVault modifier, and as we analyzed earlier, the updateVault modifier is used for reward updates. Therefore, when the attack contract C2 calls the collectFees function while holding the PLP credential, it triggers the updateVault modifier to calculate the reward that should be distributed based on the number of PLP credentials held, and record it in the user’s token0Rewards and token1Rewards variables. It should be noted that at this point, the tokenPerSharePaid variable cached for PLP credential holders is 0, which directly leads to users being able to receive rewards for holding PLP credentials.

We can also observe the state changes on the chain:

After that, the attack contract C2 followed the same process to obtain reward records.

Finally, the PLP credentials were transferred back to attack contract C1, and the Sorbetto Fragola contract’s withdraw function was called to burn the PLP credentials and withdraw the previously deposited WETH and USDT liquidity. The attack contract C2 and C3 also called the collectFees function to receive the rewards they wanted. This way, in the same block, the attacker not only retrieved the deposited liquidity but also obtained multiple liquidity providing rewards.

Then the attacker started using other tokens to replicate the same strategy to collect rewards, as shown in the following figure:

Attack Process

1. Attacker created multiple attack contracts and borrowed a large amount of tokens from AAVE using flash loans.

2. Attacker deposited the borrowed tokens into Sorbetto Fragola contract to obtain PLP credentials.

3. Attacker exploited the reward calculation flaw in Sorbetto Fragola contract by transferring the obtained PLP credentials among the created attack contracts and calling the collectFees function of Sorbetto Fragola contract to record rewards for each contract.

4. Attacker burnt the PLP credentials to withdraw the liquidity funds deposited in Sorbetto Fragola contract and called the collectFees function through each attack contract to obtain the recorded rewards.

5. The above steps were repeated to attack various liquidity pools to harvest rewards.

6. Finally, the attacker repaid the flash loans and walked away with profits.

MistTrack Analysis

The SlowMist AML team analyzed and calculated that this attack resulted in a loss of approximately 4.98 million USDT, 2.56K WETH, 96 WBTC, 5.39 million USDC, 159.93K DAI, and 10.49K UNI, totaling nearly 21 million US dollars.

Analysis of Fund Flow

Through its anti-money laundering tracking system MistTrack, the SlowMist AML team found that the attacker first withdrew initial funds from Tornado.Cash and then deployed three attack contracts from the H1 address:

After profiting from the attack, the attacker exchanged the obtained tokens for ETH through Uniswap V3 and transferred them back to Tornado.Cash.

Currently, the attacker’s account balance is only 0.08 ETH, and the remaining funds have been transferred through Tornado.Cash.

Summary

The core of this vulnerability lies in the fact that the same PLP credential can bring benefits to multiple holders at the same time due to the reward update record defect. In response to such vulnerabilities, the SlowMist Security Team recommends that the reward settlement issue should be handled before credential transfer, and the reward cache before and after the transfer should be recorded to avoid similar problems.

Reference attack transaction:

https://etherscan.io/tx/0xcd7dae143a4c0223349c16237ce4cd7696b1638d116a72755231ede872ab70fc

About SlowMist

SlowMist is a blockchain security firm established in January 2018. The firm was started by a team with over ten years of network security experience to become a global force. Our goal is to make the blockchain ecosystem as secure as possible for everyone. We are now a renowned international blockchain security firm that has worked on various well-known projects such as Huobi, OKX, Binance, imToken, Crypto.com, Amber Group, Klaytn, EOS, 1inch, PancakeSwap, TUSD, Alpaca Finance, MultiChain, O3Swap, etc.

Website:
https://www.slowmist.com
Twitter:
https://twitter.com/SlowMist_Team
Github:
https://github.com/slowmist/

--

--

SlowMist
SlowMist

Written by SlowMist

SlowMist is a Blockchain security firm established in 2018, providing services such as security audits, security consultants, red teaming, and more.

No responses yet