sherlock-audit / 2024-06-boost-aa-wallet-judging

3 stars 1 forks source link

SovaSlava - User could validate one action twice, using malleable hash #465

Open sherlock-admin4 opened 2 months ago

sherlock-admin4 commented 2 months ago

SovaSlava

Medium

User could validate one action twice, using malleable hash

Summary

SignerValidator.sol validator use SignatureCheckerLib from solady package. This library dont check, that hash is malleable. Therefore, user could create second (different) signature(if he has one) and validata again his executed action. And get 2 rewards for 1 action.

Root Cause

In 'SignerValidator.sol:6' uses library from solady package, which dont have protection from malleable attack. If you open code of this library, you can read aboit it in comment - https://github.com/Vectorized/solady/blob/362b2efd20f38aea7252b391e5e016633ff79641/src/utils/SignatureCheckerLib.sol#L23 https://github.com/sherlock-audit/2024-06-boost-aa-wallet/blob/main/boost-protocol/packages/evm/contracts/validators/SignerValidator.sol#L6

SignerValidator contract calculate storage slot, usgin hash (as we know it could be different for the same values due to malleable attack) for storing bits (is incentive has validated or not).


 function setOrThrow(IncentiveMap storage bitmap, bytes32 hash, uint256 incentive) internal {
        bytes4 invalidSelector = BoostError.IncentiveToBig.selector;
        bytes4 claimedSelector = BoostError.IncentiveClaimed.selector;
        /// @solidity memory-safe-assembly
        assembly {
            if gt(incentive, 7) {
                // if the incentive is larger the 7 (the highest bit index)
                // we revert
                mstore(0, invalidSelector)
                mstore(4, incentive)
                revert(0x00, 0x24)
            }
            mstore(0x20, bitmap.slot)
            mstore(0x00, hash)
            let storageSlot := keccak256(0x00, 0x40)
            // toggle the value that was stored inline on stack with xor
            let updatedStorageValue := xor(sload(storageSlot), shl(incentive, 1))
            // isolate the toggled bit and see if it's been unset back to zero
            let alreadySet := xor(1, shr(incentive, updatedStorageValue))

So, if user execute one action, get signature and call claimIncentive() twice with different hashes, he get 2 rewards.

Internal pre-conditions

Boost uses SignerValidator.sol as validator

External pre-conditions

No response

Attack Path

Creator create boost with validator SignerValidator.sol User execute 1 action User get signature User call call claimIncentive() twice with different hashes

Impact

User get 2 rewards for 1 action

PoC

No response

Mitigation

Use library from openzeppelin