Summary
A critical vulnerability exists in the current implementation that allows fund withdrawals even when the fundraising goal has not been achieved, fundamentally breaking the core mechanism of crowdfunding.
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(())
}
Impact
Allows creators to withdraw funds regardless of goal achievement
Breaks the docs **Refund Mechanism:** Contributors can get refunds if deadlines are reached and goals aren't met
Breaks contributor trust
Platform reputation damage
Contributors does not get refunded.
Proof Of concept
Scenario 1: Premature Fund Extraction
Scenario 2: Repeated Incomplete Campaigns
Create multiple campaigns
Intentionally fail to meet goals
Continuously extract partial funds
Exploit platform's lack of validation
Recommendation
pub fn withdraw(ctx: Context<FundWithdraw>) -> Result<()> {
let fund = &ctx.accounts.fund;
let current_time = Clock::get()?.unix_timestamp;
if fund.amount_raised < fund.goal {
return Err(ErrorCode::GoalNotMet.into());
}
let amount = 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(())
}
#[error_code]
pub enum ErrorCode {
GoalNotMet,
}