Summary
The contract allows External Owned Accounts (EOAs) to be registered as charities, bypassing the intended requirement for charities to be smart contracts with proper logic and controls.
Vulnerability Details
function registerCharity(address charity) public {
registeredCharities[charity] = true;
}
function donate(address charity) public payable {
require(registry.isVerified(charity), "Charity not verified");
(bool sent,) = charity.call{value: msg.value}("");
}
Test proving vulnerability:
function testDonateToEOA() public {
address payable eoa = payable(makeAddr("eoa"));
vm.startPrank(admin);
registryContract.registerCharity(address(eoa));
registryContract.verifyCharity(address(eoa));
vm.stopPrank();
charityContract.updateRegistry(address(registryContract));
vm.deal(address(this), 1 ether);
charityContract.donate{value: 1 ether}(address(eoa));
assertEq(eoa.balance, 1 ether);
}
Impact
MEDIUM - Because:
Funds can be sent to personal wallets
Bypasses charity contract verification
No guarantee of charitable use
Loss of funds control
Tools Used
Recommendations
function registerCharity(address charity) public {
require(charity.code.length > 0, "Must be contract");
require(ICharity(charity).isCharity(), "Must implement charity interface");
registeredCharities[charity] = true;
}