When talking about staking in the blockchain ecosystem, one could be referring to two different concepts. It could be the kind of staking that is used to participate in a Proof-of-Stake consensus algorithm or the one that users lock their tokens in a DeFi project to earn rewards for helping increase its TVL or preserve some token’s value. This article is going to focus on the latter, but let's talk a bit more about the two so they are clearly differentiated:
Proof-of-Stake blockchains rely on users (validators) locking (staking) the native coin to participate in the consensus algorithm that is in charge of generating and adding blocks to the chain. Staking is the validators’ proof of their skin in the game. In some blockchains, such as Ethereum, a validator that submits a fraudulent block can even have its stake slashed as punishment.
Proof-of-stake (PoS) was created as an alternative to proof-of-work (PoW). Proof of stake assumes that validators will act in the best interests of the network, as they are invested in its continued value. This differs from PoW, which does not require miners to hold any of the currency whose transactions they are validating. The main advantage of Proof of Stake versus Proof of Work is its vastly reduced energy consumption. Furthermore, PoS does not require the specialized hardware that Proof of Work does.
Some decentralized finance (DeFi) projects allow users to stake coins or tokens with them to increase their TVL (total value locked) rewarding such users with more coins or tokens. The amount, kind and frequency of the rewards will depend on the specific DeFi project.
The following table summarizes the differences between the two kind of staking systems discussed:
Generally, vulnerabilities in staking projects occur when the smart contract that calculates user rewards fails to use the correct price of the underlying token or the amount of tokens the user has for such calculation. This allows attackers to get more value than expected.
The exploits typically work by having an attacker manipulate the price of the staking token or the amount the user has momentarily. At the same time, having the vulnerable smart contract calculate the user rewards, thus affecting the value or amount of reward tokens for their own benefit.
In addition to this, other typical vulnerabilities that may be present in the contract, such as reentrant methods, contract variables not updated, etc., could allow an attacker to gain more rewards.
Here are a few typical staking vulnerabilities:
On the 21st of November 2022, the CertiK’s Skynet system alerted of a flashloan that made unusually large gains. After some investigation, the team identified it as an exploit of the SportsDao token reward mechanism, where the reward amount was inversely proportional to the contract LP balance which could be “reset” by anyone through the withdrawTeam() function. The total profit was calculated to be around 13.7K BSC-USD.
The rewards were calculated based on the contract’s LP balance:
And anyone could empty the contract by calling the withdrawTeam() function to transfer all LP tokens to the team wallet.
Because the reward rate was not only decided by the liquidity provided by the user, but also inversely proportional to the LP token balance (of a designated pair) on the contract, once the contract was empty, the attacker only needed to stake some LP tokens (stakeLP() function) and then claim the rewards (getReward() function):
On the 13th of August 2022, the WarpStaking project received an attack that took advantage of a rewards’ price miscalculation. Even though the total profit was calculated to be only 0.09 BTC (around $2,293), it has been included in this article because it can be considered a great and simple example.
WarpStaking would reward users that staked their WRP tokens with them. The attacker flashloaned 2 BTCB from PancakeSwap and swapped them for WRP using the BTCB-WRP liquidity pool (increasing the value of WRP on that pool). Then, called the harvest() function to get the staking reward (in BTCB). Because the function calculates the price of WRP in BTCB using the aforementioned pool, the attacker got more rewards than they should.
In the above code, the rewardTokenPriceInToken() function is called to calculate the price of the reward token. Given that it uses a single PancakeSwap pool, the price can be easily manipulated by the attacker. The recommended mitigation is to use multiple reliable on-chain price oracle sources, such as Chainlink or Band protocol or use Time-Weighted Average Price (TWAP) technique.
The affected transaction can be observed using the Bscscan: https://bscscan.com/tx/0xa8dd1a8ecb5e7975f0e9830bbd3894cb81547338ce75b3cabcdf04dc1976a757.
It is worth mentioning that, in the past, other incidents have been observed which incorrectly use a single third-party pool to calculate the number of shares that should be minted when staking. Therefore, they would not be misusing the third-party pool to calculate the value of the rewards as explained above, but the amount of shares to get when staking in the first place. Those attacks would also lead to a malfunction in the rewarding scheme.
Equalizer allowed users to take flashloans using funds from their vaults. The contract FlashloanProvider made it possible to flashloan out assets from the vault while staking new tokens to it in the same block. This made the exchange ratio prone to manipulation. The attacker gained a total of about $112,000 worth of token by exploiting multiple vaults (~33 WETH, ~831 USDC, ~651 UNI, ~77 LINK and ~164 WBNB).
In the following code, the function getRatioForOneEToken() uses the staked token of the current contract to calculate the token’s ratio, which may not look vulnerable at first glance. However, the contract also provides a flashloan functionality, which allows the token’s ratio of the contract to be manipulated arbitrarily.
One of the exploit transactions can be observed using the Etherscan: https://etherscan.io/tx/0x9b17f61d2c7fc4463ff94c5edfea6695d131584a6e07fed5b9ed298c16c17f41/advanced
On the 26th of October 2022, the StakeUpFarm project received an attack that took advantage of the fact that the staking time parameter was not properly updated. The exploiter profited 12,328 BUSD during the hack.
StakeUpFarm did not update the startTime state variable when unstaking tokens. Therefore, it was possible to stake multiple times accruing all rewards (even from new staked tokens) as if they were staked from the beginning (first stake operation).2
In the code above, the userRewardMINT() function is called to calculate the user's reward, but the variable
claimTimeMINT[_user].startTime is not updated when the user unstakes, allowing the user to receive more rewards.
The exploit transaction can be found here: https://bscscan.com/tx/0x368a52b5b657ee7152d99ebbf595e09163d3847f47a9eb52b9c8618adbd21368
On the 13th of March 2022, the Paraluni project received an attack that took advantage of a reentrancy flaw. Around $1.7M were stolen from the project through multiple transactions.
In the code above, before the process of addLiquidityInternal() was finished, the attacker triggered MasterChef.deposit() to make a malicious token deposit LP tokens to the contract. Because the function addLiquidityInternal() checks the contract balance before and after, the contract thought the depositor provided more LP tokens. This way, the attacker had two deposit records.
The attack was a bit more complex than directly exploiting the reentrancy issue because it involved the creation of two fake tokens that would reenter upon the call of the transferFrom() function. An example of one of the exploit transactions can be found here: https://bscscan.com/tx/0x70f367b9420ac2654a5223cc311c7f9c361736a39fd4e7dff9ed1b85bab7ad54
a. Lack of access control over sensitive functions
On the 20th of February 2022, the DonationStaking suffered from a lack of access control vulnerability which resulted in a loss of 15 ETH (around 40,000 USD). There was no access control over the setStakingContract() function, so anyone could call it to modify the staking contract and staking token.
The following transaction shows how the attacker called setStakingContract() to put a crafted “L token” as the staking contract for the DonationStaking contract: https://etherscan.io/tx/0x09e026dcd64e2ebc5c59e77b3faf5543bcc3b774342ac7a980e388c3c33c84d9. This was possible due to the mentioned lack of access control over the function.
b. Lack of input validation
On the 2nd of August 2022, the Reaper Farm's ReaperVaultV2 contract was exploited, leading to a loss of more than $1.6M.
The attacker took advantage of a bug in the ReaperVaultV2 contract, where anyone could burn other users' vault shares and withdraw tokens. The main problem was that the vault share owner can be an account other than msg.sender.
In the withdraw() function above, the parameter owner can be any arbitrary address and not only the address of the caller. Also, the allowance or the relationship between the address owner and msg.sender is unchecked, which means users can withdraw other users' assets from the vault by using different owner values.
An example transaction of the attack:
c. Logical processing using input parameters
On the 23rd of April 2022, an attacker exploited the flipcoin() function, a gambling feature in the SmoltingInu token contract to win SmoltingInu tokens. The attacker dumped the winning tokens on the market causing the token price to drop 99%.
In the SmoltingInu contract, the flipCoin() function takes the user's deposit, generates the _randomNumber and determines if the user wins or not. All the steps finish in one block. The attacker wrote a contract to interact with the flipCoin() function and revert the transaction if lost.
One of the exploit transactions was https://bscscan.com/tx/0x6bfb3abbdc546959d4cb64840672920389ffb1ff4f0ccb276629ee26df3d08d6
Given that price manipulation attacks are typically performed using flashloans, their ease of use and their cheap price are making them popular amongst exploiters. If the attack fails, the only cost is the transaction fee. If the attack succeeds, the added cost is only the flashloan fee (which is minor), so the profit can be extremely high.
Some of the contracts themselves have logical vulnerabilities, such as some variables of the contract not being updated, lack of privilege control in the contract methods, and unreasonable economic models of the contract.
The web3 sector has become a new target for hackers, especially DeFi. Many projects go live without an audit and even those that do get audited could suffer new attack vectors.
Furthermore, DeFi projects rarely have AML or KYC processes, so it is relatively easy to execute an attack and launder the money through another DeFi exchange while remaining undetected.
Based on this analysis, it is clear that there is still a lot of work to be done when it comes to DeFi security. Despite all the major advancements in security, adoption, and regulatory approval, digital assets are still an emerging market. The future is being built as we read this.
The best way to protect staking projects from attacks is by reviewing the reward calculations to make sure all rewards are in line with the design and are sustainable. Furthermore, it is recommended to use a robust decentralized oracle such as Chainlink (or by aggregating many different price feeds).
If a platform decides to use an on-chain oracle, then there are a few precautionary measures available to improve security. They should use time-weighted average prices (TWAP) to calculate asset prices so that an attacker will be hard-pressed to skew the prices enough for an attack to be worthwhile.
Another way to prevent flashloan attacks on staking projects is by adding a slight delay between the steps required to interact with the platform. This delay mitigates flashloan attacks because they are required to start and finish in the same block. While this measure might limit composability and user experience, it is an effective solution.
Finally, make sure that typical vulnerabilities are not present in the contract, such as reentrant methods, lack of access controls, contract variables not updated, etc. For that, it is recommended to always follow security best practices, have your own set of tests, get your code audited by a well-known security firm and keep you updated with the latest hacks.