Bitter Hemp Tiger
High
In VVVVCTokenDistributor::claim
, it allows users to claim their rewards accumulated during the vesting period, an user would need a valid signature to proceed. However, in claim
and signature validation function, it only checks wether the signature is from expected signer, but does not check msg.sender
, this open an opportunity for anyone to use existing valid signature for their own benefit.
Here, in the claim
function, basic sanity checks are done for the param
argument and signature:
function claim(ClaimParams memory _params) public {
if (claimIsPaused) {
revert ClaimIsPaused();
}
if (_params.projectTokenProxyWallets.length != _params.tokenAmountsToClaim.length) {
revert ArrayLengthMismatch();
}
if (_params.nonce <= nonces[_params.kycAddress]) {
revert InvalidNonce();
}
if (!_isSignatureValid(_params)) {
revert InvalidSignature();
}
// update nonce
nonces[_params.kycAddress] = _params.nonce;
// define token to transfer
IERC20 projectToken = IERC20(_params.projectTokenAddress);
// transfer tokens from each wallet to the caller
for (uint256 i = 0; i < _params.projectTokenProxyWallets.length; i++) {
projectToken.safeTransferFrom(
_params.projectTokenProxyWallets[i],
msg.sender,
_params.tokenAmountsToClaim[i]
);
}
emit VCClaim(
_params.kycAddress,
_params.projectTokenAddress,
_params.projectTokenProxyWallets,
_params.tokenAmountsToClaim,
_params.nonce
);
}
And also in _isSignatureValid
:
function _isSignatureValid(ClaimParams memory _params) private view returns (bool) {
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(
abi.encode(
CLAIM_TYPEHASH,
_params.kycAddress,
_params.projectTokenAddress,
_params.projectTokenProxyWallets,
_params.tokenAmountsToClaim,
_params.nonce,
_params.deadline
)
)
)
);
address recoveredAddress = ECDSA.recover(digest, _params.signature);
bool isSigner = recoveredAddress == signer;
bool isExpired = block.timestamp > _params.deadline;
return isSigner && !isExpired;
}
The above function checks if signature is from expected signature, and wether signature has expired. During the entire flow of claiming process, msg.sender
is not validated anywhere, but at the end, rewards are transferred to msg.sender
. This would allow anyone, including addresses are not from KYC list and expected claim to use such signature to get reward for their own benefit.
In the vesting contract, invest
also does not check msg.sender
, but as msg.sender
would need to pay for vested amount for kycAddress
, such attack would gain no value.
Alice has some rewards ready to be claimed, and off-chain the protocol signs Alice a signature for her claim.
No response
Attacker observes her transaction or gets her signature in some other ways, frontruns her transaction using her signature, and get Alice's reward.
Rewards can be stolen, causing loss of funds due to frontrunning.
No response
Restrict reward recipient, considering adding recipient
field in signature as well, and validate if msg.sender == params.recipient
.