RustFund

First Flight #36
Beginner FriendlyRust
100 EXP
View results
Submission Details
Severity: high
Valid

Campaign Creator Can Withdraw Funds from an Unsuccessful Campaign (No Funding Goal Check)

Summary

The withdraw function lacks a check for campaign success. The campaign creator is able to call withdraw and retrieve contributed funds even if the funding goal has not been reached. In other words, there is no validation that the campaign met its goal before allowing the creator to take the money. This oversight enables a malicious or impatient creator to steal contributions from a campaign that should have failed (where contributors expected refunds).

Vulnerability Details

Normally, a crowdfunding contract should only allow withdrawal of funds after the campaign is successful (i.e., it raised at least the goal amount by the deadline). In RustFund’s withdraw handler, there is no requirement or condition verifying that fund.amount_raised >= fund.goal. The code likely simply transfers whatever has been raised to the creator, regardless of the goal.

pub fn withdraw(ctx: Context<FundWithdraw>) -> Result<()> {
let amount = ctx.accounts.fund.amount_raised;
**ctx.accounts.fund.to_account_info().try_borrow_mut_lamports()? =
ctx.accounts.fund.to_account_info().lamports()
.checked_sub(amount)
.ok_or(ProgramError::InsufficientFunds)?;
**ctx.accounts.creator.to_account_info().try_borrow_mut_lamports()? =
ctx.accounts.creator.to_account_info().lamports()
.checked_add(amount)
.ok_or(ErrorCode::CalculationOverflow)?;
Ok(())
}

In practice, this means if a campaign only raised, say, 50% of its goal by the deadline, the creator can still withdraw that 50% from the fund account. There is an authorization check that likely ensures only the campaign’s creator can call withdraw (e.g., a has_one = creator constraint or similar), so arbitrary users can’t withdraw. But the creator themselves is not restricted by campaign success.

Impact

This is a critical logic flaw. It violates the fundamental guarantee to contributors: if the campaign fails, they should get their money back. Instead, the creator can take all contributed funds from a failed campaign. The impact is effectively a theft of contributor funds under the guise of an unsuccessful campaign. Contributors have no on-chain recourse if this happens, because the contract itself permits it. A malicious campaign creator could exploit this by setting a high goal to attract contributions, then even if they don’t meet the goal, simply withdraw whatever was contributed. Even an honest but desperate creator might be tempted to withdraw funds despite failing, undermining the platform’s integrity. Contributors wouldn’t be able to claim refunds while the creator empties the fund, resulting in a complete loss for contributors. This issue would be classified as High severity because it enables direct and unauthorized loss of user funds.

Tools Used

  • Comparison of specification requirements with code analysis.

  • Manual analysis of logical branches in the withdraw function.

Recommendations

  • Add a check, for example: if fund.amount_raise < fund.goal or if the deadline has not yet arrived, withdrawal of funds should be prohibited.

  • Define an explicit campaign success condition and update the function logic according to this condition.

Updates

Appeal created

bube Lead Judge 8 months ago
Submission Judgement Published
Validated
Assigned finding tags:

No goal achievement check in `withdraw` function

Support

FAQs

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