RustFund

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

Withdrawal Logic Flaw in RustFund Contract

Summary

The withdraw function in the RustFund contract allows creators to withdraw all raised funds without verifying campaign success (e.g., amount_raised >= goal or deadline passed). This undermines the intended trustless crowdfunding model by permitting premature or unauthorized withdrawals.

Vulnerability Details

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(())
}

Missing Success Check: No conditions enforce amount_raised >= goal or deadline <= current_time.

  • Immediate Access: Creators can call this function anytime after contributions, even before the campaign concludes.

Impact

Funds Misallocation: Creators can withdraw funds before meeting the goal, violating contributor trust.

  • Security Risk: Undermines the "secure withdrawals" feature, allowing creators to abscond with funds.

  • Intent Violation: Contradicts "withdraw funds once their campaign succeeds

Tools Used

Manual Review

Recommendations

pub fn withdraw(ctx: Context<FundWithdraw>) -> Result<()> {
let fund = &ctx.accounts.fund;
let current_time = Clock::get()?.unix_timestamp.try_into().unwrap();
if fund.amount_raised < fund.goal {
return Err(ErrorCode::GoalNotMet.into());
}
if fund.deadline > current_time {
return Err(ErrorCode::DeadlineNotReached.into());
}
let amount = fund.amount_raised;
**ctx.accounts.fund.to_account_info().try_borrow_mut_lamports()? =
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(())
}
Add new error codes:
rust
#[error_code]
pub enum ErrorCode {
// ... existing errors ...
#[msg("Campaign goal not met")]
GoalNotMet,
}
Updates

Appeal created

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

No deadline check in `withdraw` function

No goal achievement check in `withdraw` function

Support

FAQs

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