BOOK THIS SPACE FOR AD
ARTICLE ADSummary
Whitehat Csanuragjain submitted a critical vulnerability in Tidal Finance to Immunefi on July 31, 2021. The vulnerability was a logic error that permitted a malicious user to claim more rewards from staking than they were entitled to. Approximately 300 million Tidal tokens were locked in the staking contract at the time. Since the trigger condition would require multiple requirements to meet during a claim and payout process, it is unclear how much could have been stolen with additional claims. Tidal Finance quickly patched the bug and paid out a $25,000 USDT bounty to the whitehat.
Vulnerability Analysis
Tidal Finance is a decentralized discretionary mutual cover protocol that offers the DeFi community the ability to hedge against the failure of any DeFi protocol or asset. The vulnerability, discovered by whitehat Csanuragjain, was present in the staking contract located on Polygon and consisted of a logic error that could have been taken advantage of by a malicious hacker to steal an unearned share of rewards generated by staking activity.
The payout of rewards for staking operates as a four-part sequential process (startPayout, setPayout, doPayout, finishPayout), but the most important part of that process is the doPayout function.
The steps to execute the attack are as follows:
The admin initiates a payout for Asset Index 0The four payout functions are called in sequential order — the payout is marked as finishedOnce the Asset Index 0 is finished, a malicious user enters the pool and deposits some arbitrary amountAfter some amount of time, say a year, the malicious user calls doPayout with the Asset Index 0 and an attacker wallet address as an argumentDespite the fact that the malicious user has not made any payment, the user’s reward is calculated in the user.rewardAmount variable, but the user.rewardDebt is still 0, meaning the system cannot tell that the reward has already been allocatedIn other words, because the asset index had a finalized payout, the user.rewardAmount variable increased without a corresponding increase in the user.rewardDebt. Because the value of any unset variable in Solidity is 0, the malicious attacker now has more rewards than he should be entitled to. The normal staking process generates a reward figure stored in user.rewardAmount and the final payout should result from a subtraction of user.rewardAmount - user.rewardDebt, but that subtraction process does not properly occur because user.rewardDebt is mistakenly at 0.
This exploit cannot simply be run repeatedly, as the bug depends on how much rewards accumulate from staking and what percentage of the pool is owned at the time an attacker decides to execute an attack.
Vulnerability Fix
The bug fix to prevent a malicious attacker from receiving awards that they never staked in was already located in the code itself but not activated at the right point in time in the logic flow.
The Tidal team duplicated the line user.rewardDebt = user.amount.mul(poolInfo.accRewardPerShare).div(UNIT_PER_SHARE); and moved it up to be included in the if statement if (payoutInfo[assetIndex_][payoutId].finished), so that the reward debt amount is updated after payout, in order to ensure that the value of the reward debt amount does not remain 0. Now that the reward debt amount is properly updated after payout, a malicious user cannot take rewards generated from staking that they are not entitled to.
Acknowledgements
We’d like to thank the Tidal Finance team for their rapid and effective response to the bug report. Tidal Finance paid out a bounty of $25,000 to the whitehat. To report additional vulnerabilities, please see Tidal’s bug bounty program with Immunefi. If you’re interested in protecting your project with a bug bounty like Tidal, visit the Immunefi services page and fill out the form.