Closed c4-bot-8 closed 5 months ago
JustDravee marked the issue as duplicate of #489
JustDravee marked the issue as sufficient quality report
koolexcrypto marked the issue as unsatisfactory: Invalid
koolexcrypto marked the issue as unsatisfactory: Invalid
koolexcrypto marked the issue as unsatisfactory: Invalid
koolexcrypto marked the issue as nullified
koolexcrypto marked the issue as not nullified
koolexcrypto marked the issue as duplicate of #1001
koolexcrypto marked the issue as satisfactory
Lines of code
https://github.com/code-423n4/2024-04-dyad/blob/4a987e536576139793a1c04690336d06c93fca90/src/core/VaultManagerV2.sol#L143
Vulnerability details
In VaultManagerV2::withdraw, there is a protection from flash loans by checking if there is an already deposit by the dNFT id on the same block:
The
idToBlockOfLastDeposit
is updated when a deposit is made:We can see that
deposit
has the modifierisValidDNft
because someone can deposit on behalf of another dNFT. However, a malicious actor can abuse this modifier by front-running withdrawal requests to deposit on behalf of the DNft making the withdraw, soidToBlockOfLastDeposit
will be set for that DNft, and thus, the flash loan protection mechanism will prevent the withdrawal request.The
deposit
does not enforce any minimum value, so a malicious actor depositing1 wei
value is enough to perform the DoS.Consider the following example that is demonstrated by a runnable PoC (numbers are kept small for simplicity):
1
dyad.redeemDyad
1 wei
wETH on behalf of Alice, passing her DNft ID to thedeposit
function. Now,idToBlockOfLastDeposit[1]
which corresponds to Alice is set to the current block numberredeemDyad
) will revert due to the flash loan protectionImpact
Proof of Concept
The following test demonstrates the described example above. Copy and paste the following contracts to
/test
:Base2Test.col
:import "forge-std/Test.sol"; import "forge-std/console.sol"; import {DeployV2, Contracts} from "../script/deploy/Deploy.V2.s.sol"; import {Parameters} from "../src/params/Parameters.sol"; import {DNft} from "../src/core/DNft.sol"; import {Dyad} from "../src/core/Dyad.sol"; import {Licenser} from "../src/core/Licenser.sol"; import {VaultManager} from "../src/core/VaultManager.sol"; import {Vault} from "../src/core/Vault.sol"; import {Payments} from "../src/periphery/Payments.sol"; import {OracleMock} from "./OracleMock.sol"; import {ERC20Mock} from "./ERC20Mock.sol"; import {IAggregatorV3} from "../src/interfaces/IAggregatorV3.sol"; import {ERC20} from "@solmate/src/tokens/ERC20.sol"; import {VaultManagerV2} from "../src/core/VaultManagerV2.sol"; import {VaultWstEth} from "../src/core/Vault.wsteth.sol"; import {KerosineManager} from "../src/core/KerosineManager.sol"; import {UnboundedKerosineVault} from "../src/core/Vault.kerosine.unbounded.sol"; import {BoundedKerosineVault} from "../src/core/Vault.kerosine.bounded.sol"; import {Kerosine} from "../src/staking/Kerosine.sol"; import {KerosineDenominator} from "../src/staking/KerosineDenominator.sol"; contract Base2Test is Test, Parameters {
function setUp() public { vm.createSelectFork("https://eth.llamarpc.com", 19691300); Contracts memory contracts = new DeployV2().run();
} }
[PASS] test_malicious_actor_can_DoS_redeem_or_withdraw() (gas: 294705)