stake.link

stake.link
DeFiHardhatBridge
27,500 USDC
View results
Submission Details
Severity: high
Invalid

Malicious user can brick 'SDLPoolSecondary.sol'

Summary

Due to a lack of checks in SDPoolSecondary.withdraw(), a malicious user can populate queuedLockUpdates by repeatedly calling the withdraw() function, resulting in a Out of Gas error. Which means reward distribution on the L2 chain will not be possible.

Vulnerability Details

A malicious user can repeatedly call SDPoolSecondary.withdraw() to populate the queuedLockUpdates array:

function withdraw(uint256 _lockId, uint256 _amount)
external
onlyLockOwner(_lockId, msg.sender)
updateRewards(msg.sender) {
//.. ommitted code
queuedLockUpdates[_lockId].push(LockUpdate(updateBatchIndex, lock));
//.. ommitted code
}

Due to the array being populated, every time the Keeper, which is an automated entity, sends an update, this update will revert due to being Out of Gas, effectively bricking communication from L2 -> L1.

This attack does not cost a lot due to the execution costs on Arbitrum/Optimism being extremely low. This attack is especially very easy to execute on Optimism due to their low gas block limit.

I wrote this PoC:

// Put this test in any test function in `sdl-pool-secondary.test.ts`
it.only('DoS', async () => {
let ts1 = await mintLock()
await time.increase(200 * DAY)
await sdlPool.initiateUnlock(1)
let ts2 = (await ethers.provider.getBlock(await ethers.provider.getBlockNumber())).timestamp
await time.increase(200 * DAY)
let startingBalance = await sdlToken.balanceOf(accounts[0])
for(let i = 0; i < 4999; i++){
await sdlPool.withdraw(1, 0)
}
await updateLocks()
}).timeout(10000000)

As the test finished(takes a few minutes), the traces show(I used hardhat-tracer to follow the traces) :

[EXCEPTION] EvmError(reason: "out of gas")
[REVERT] UnknownError(0x)

Impact

Communication between L2 -> L1 is bricked, which means updates will not be possible, which means core functionality of this project is broken. Stake.link won't be able to function as it's supposed to. Furthermore, changes in assets of users on the L2 will not be reflected on the L1, potentially causing loss of funds.

Tools Used

Hardhat

Recommendations

Allow a user to make one withdraw per update period or don't allow the user to specify the amount to withdraw, let the user withdraw the maximum amount that the user can withdraw at that moment.

Updates

Lead Judging Commences

0kage Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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