The withdraw()
function in the rustfund
program contains a vulnerability where the amount_raised
state variable is never reset to zero after a successful withdrawal. This leads to a situation where new contributions after a withdrawal are effectively locked in the contract, as subsequent withdrawal attempts will fail due to insufficient funds.
The key issue is that after transferring the funds to its creator, the function does not reset the amount_raised
variable. This means that if new contributions are made after a withdrawal, the amount_raised
value will continue to accumulate. When the creator attempts to withdraw again, the contract will try to transfer the entire amount_raised
value, which will be larger than the actual balance in the fund account, resulting in an InsufficientFunds
error.
Permanently locked funds: Any contributions made after a successful withdrawal will be permanently locked in the contract, as the creator cannot withdraw them.
Campaign dysfunction: The crowdfunding mechanism becomes dysfunctional after the first withdrawal, as any new funds contributed cannot be properly managed.
The following test demonstrates how funds become locked after a withdrawal due to the amount_raised not being reset:
Save the above test as tests/04.ts
in your project's test directory and run the test:
To illustrate the real-world impact of this vulnerability, consider this scenario:
A creator launches a campaign to fund a 10 SOL project.
Contributors donate a total of 10 SOL, reaching the goal.
The creator withdraws the 10 SOL (withdrawal succeeds) when goal is reached and deadline past.
The amount_raised
in the contract remains at 10 SOL, though the actual balance is now 0.
A new contributor donates 2 SOL to support the ongoing project.
The creator tries to withdraw this new contribution.
The withdrawal fails with an "InsufficientFunds" error because the contract tries to withdraw 12 SOL (the accumulated amount_raised
), but only 2 SOL is available in the account.
The 2 SOL contribution is now permanently locked in the contract, with no mechanism to withdraw it.
The withdraw()
function should be modified to reset the amount_raised
value to zero after a successful withdrawal:
This fix ensures that after each withdrawal, the amount_raised
is reset to zero, allowing new contributions to be properly accounted for and subsequently withdrawn by the creator.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.