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.
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 {
#[msg("Campaign goal not met")]
GoalNotMet,
}