Rust Fund

AI First Flight #9
Beginner FriendlyRust
EXP
View results
Submission Details
Severity: high
Valid

Withdraw() Function Lacks Deadline and Goal Achievement Checks

Summary
The withdraw function currently allows the campaign creator to claim the raised funds without verifying that the campaign's deadline has passed and that the funding goal has been met. This can lead to premature withdrawals, undermining the crowdfunding mechanism.

Vulnerability Details
In the withdraw function, funds are transferred to the creator based solely on the amount_raised variable. There are no checks to ensure that:

The campaign deadline has been reached, ensuring that the fundraising period has ended.

The funding goal has been achieved, which is a precondition for a successful campaign according to project requirements.

Without these verifications, the creator could potentially withdraw funds before the campaign is concluded or even if the campaign fails to meet its goal, thereby violating the trust model of the platform.

Impact
Premature Fund Withdrawal: The creator may withdraw funds before the campaign's end, denying contributors their right to a refund if the goal is not met.

Campaign Integrity Compromise: Contributors may lose confidence in the platform if funds are accessible before campaign conditions are fully satisfied.

Financial Discrepancy: Incorrect handling of funds could lead to disputes and mismanagement, affecting both the campaign's success and overall platform reliability.

Tools Used
Manual code review

Static analysis of business logic

Recommendations
Add Deadline Check: Modify the withdraw function to ensure that funds can only be withdrawn after the campaign deadline has passed. For example:

let current_timestamp: u64 = Clock::get()?.unix_timestamp.try_into().unwrap();
if current_timestamp < fund.deadline {
return Err(ErrorCode::DeadlineNotReached.into());
}
Add Goal Achievement Check: Include a condition to verify that the funding goal was met before allowing withdrawals:

if fund.amount_raised < fund.goal {
return Err(ErrorCode::GoalNotReached.into());
}
Update Error Codes: Add appropriate error variants such as DeadlineNotReached and GoalNotReached in the ErrorCode enum.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Validated
Assigned finding tags:

[H-01] No check for if campaign reached deadline before withdraw

## Description A Malicious creator can withdraw funds before the campaign's deadline. ## Vulnerability Details There is no check in withdraw if the campaign ended before the creator can withdraw funds. ```Rust 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 A Malicious creator can withdraw all the campaign funds before deadline which is against the intended logic of the program. ## Recommendations Add check for if campaign as reached deadline before a creator can withdraw ```Rust pub fn withdraw(ctx: Context<FundWithdraw>) -> Result<()> { //add this if ctx.accounts.fund.deadline != 0 && ctx.accounts.fund.deadline > Clock::get().unwrap().unix_timestamp.try_into().unwrap() { return Err(ErrorCode::DeadlineNotReached.into()); } //stops here 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(()) } ``` ## POC keep everything in `./tests/rustfund.rs` up on to `Contribute to fund` test, then add the below: ```TypeScript it("Creator withdraws funds when deadline is not reached", async () => { const creatorBalanceBefore = await provider.connection.getBalance(creator.publicKey); const fund = await program.account.fund.fetch(fundPDA); await new Promise(resolve => setTimeout(resolve, 150)); //default 15000 console.log("goal", fund.goal.toNumber()); console.log("fundBalance", await provider.connection.getBalance(fundPDA)); console.log("creatorBalanceBefore", await provider.connection.getBalance(creator.publicKey)); await program.methods .withdraw() .accounts({ fund: fundPDA, creator: creator.publicKey, systemProgram: anchor.web3.SystemProgram.programId, }) .rpc(); const creatorBalanceAfter = await provider.connection.getBalance(creator.publicKey); console.log("creatorBalanceAfter", creatorBalanceAfter); console.log("fundBalanceAfter", await provider.connection.getBalance(fundPDA)); }); ``` this outputs: ```Python goal 1000000000 fundBalance 537590960 creatorBalanceBefore 499999999460946370 creatorBalanceAfter 499999999960941400 fundBalanceAfter 37590960 ✔ Creator withdraws funds when deadline is not reached (398ms) ``` We can notice that the creator withdraws funds from the campaign before the deadline.

Support

FAQs

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

Give us feedback!