Summary
Steaking:stake
updates the users staked balance in the protocol with the msg.value
of the function call. This interaction fails to check if the user has any existing value in the contract.
def stake(_onBehalfOf: address):
"""
@notice Allows users to stake ETH for themselves or any other user within the staking period.
@param _onBehalfOf The address to stake on behalf of.
"""
assert not self._hasStakingPeriodEnded(), STEAK__STAKING_PERIOD_ENDED
assert msg.value >= MIN_STAKE_AMOUNT, STEAK__INSUFFICIENT_STAKE_AMOUNT
assert _onBehalfOf != ADDRESS_ZERO, STEAK__ADDRESS_ZERO
self.usersToStakes[_onBehalfOf] = msg.value <@
self.totalAmountStaked += msg.value
log Staked(msg.sender, msg.value, _onBehalfOf)
Vulnerability Details (POC)
Since the value of the deposit isn't added to the existing value of the array, a users deposited total can only ever be equal to the last deposited amount.
Add the console
contract to the Steaking.t.sol
test suite:
- import {Test} from "lib/forge-std/src/Test.sol";
+ import {Test, console} from "lib/forge-std/src/Test.sol";
Then add the following test :
function testDepositGriefsUser() public {
vm.deal(user1, 1 ether);
uint256 dealAmount = 1 ether;
_stake(user1, dealAmount, user1);
uint256 user1BalanceBefore = steaking.usersToStakes(user1);
console.log("user1 balance before", user1BalanceBefore);
vm.deal(user1, 1 ether);
uint256 dealAmount2 = 0.5 ether;
_stake(user1, dealAmount2, user1);
uint256 user1BalanceAfter = steaking.usersToStakes(user1);
console.log("user1 balance after", user1BalanceAfter);
assertEq(steaking.usersToStakes(user1), dealAmount2);
}
the output from the test will return
Ran 1 test for test/Steaking.t.sol:SteakingTest
[PASS] testDepositGriefsUser() (gas: 85897)
Logs:
user1 balance before 1000000000000000000
user1 balance after 500000000000000000
Confirming that the users balance in the protocol is now only 0.5 ether.
Impact
Any user who receives more than one deposit will lose the value of their previous deposits.
Tools Used
Foundry, Manual review
Recommendations
A simple way of ammending this issue is to add the msg.value
to the users balance in the deposit function.
- self.usersToStakes[_onBehalfOf] = msg.value
self.totalAmountStaked += msg.value
+ self.usersToStakes[_onBehalfOf] += msg.value
self.totalAmountStaked += msg.value