TempleGold

TempleDAO
Foundry
25,000 USDC
View results
Submission Details
Severity: high
Invalid

The Unauthorized access in `TempleGoldStaking::migrateWithdraw` function allows the migrator(attacker) to withdraw funds and claim Rewards from any staker.

Summary:

The TempleGoldStaking contract has a high-critical vulnerability in the migrateWithdraw function. The function allows an attacker to withdraw funds and claim Rewards of other stakers by exploiting the fact that the msg.sender can be any address, including an attacker. This can occur even if the migrator address is correctly set because there is no check to ensure that only the rightful staker can withdraw their own funds.

  • staker = address stakerA (also a migrator)

  • attacker = address msg.sender (also a migrator)

Impact:

An attacker who is set as the migrator can call migrateWithdraw and withdraw funds and calim Rewards from any staker's account by passing the staker's address and the index of their stake.

Proof of Concept (explanation) :

  • if attacker is migrator (msg.sender)

  • attacker calls migrateWithdrawfunction by passing other staker addresses as parameters and index

  • then call_withdraFor in the function

  • attcker withdraw funds from other stakers account

  • its also Claims the Rewards where Claim = true as boolean parameter in_withdrawForfunction

  • because their no any checks like msg.sender==staker present in function

  • hence it exploits it.

function from Contract:

function migrateWithdraw(address staker, uint256 index) external override onlyMigrator returns (uint256) {
if (staker == address(0)) { revert CommonEventsAndErrors.InvalidAddress(); }
StakeInfo storage _stakeInfo = _stakeInfos[staker][index];
uint256 stakerBalance = _stakeInfo.amount;
_withdrawFor(_stakeInfo, staker, msg.sender, index, _stakeInfo.amount, true, staker);
return stakerBalance;
}

Test Code

contract StakingContractTest is Test {
StakingContract stakingContract;
address staker1 = address(0x123);
address staker2 = address(0x456);
address migrator = address(0x789);
address attacker = address(0xABC);
function setUp() public {
vm.deal(staker1, 10 ether);
vm.deal(staker2, 10 ether);
vm.deal(migrator, 1 ether);
vm.deal(attacker, 1 ether);
stakingContract = new StakingContract(migrator);
// Staker1 stakes 5 ether
vm.prank(staker1);
stakingContract.stake{value: 5 ether}(0);
// Staker2 stakes 5 ether
vm.prank(staker2);
stakingContract.stake{value: 5 ether}(0);
}
function testExploit() public {
// Attacker (who is also the migrator) withdraws staker1's funds
vm.prank(attacker);
stakingContract.migrateWithdraw(staker1, 0);
// Check balances
assertEq(address(attacker).balance, 6 ether);
assertEq(address(staker1).balance, 5 ether);
}
}

Tools Used:

Manual audit, Foundry

Recommendations:

Modify the migrateWithdraw function to ensure that only the rightful staker can withdraw their funds. Add a check to ensure that the msg.sender is the staker:

function migrateWithdraw(address staker, uint256 index) external override onlyMigrator returns (uint256) {
+ require(msg.sender == staker, "Only staker can withdraw their own funds");
...
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

0xdhanraj30 Submitter
11 months ago
inallhonesty Lead Judge
11 months ago
inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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