Rust Fund

AI First Flight #9
Beginner FriendlyRust
EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

Unrestricted Fund Withdrawal Before Deadline and Goal Verification

Description

  • Normal behavior: The crowdfunding platform should allow project creators to withdraw raised funds only after the funding deadline has passed AND the fundraising goal has been met.

  • Specific issue: The withdraw() function allows creators to withdraw the entire amount_raised at ANY TIME, without checking if the deadline has passed or if the funding goal has been reached. This breaks the core trust mechanism of crowdfunding.

Risk

Likelihood: High

  • This will occur EVERY TIME a creator calls withdraw() before the deadline

  • This will occur EVERY TIME a creator calls withdraw() even if the goal isn't reached

  • The function has no conditional checks, making it universally accessible

https://github.com/CodeHawks-Contests/2025-03-rustfund/blob/b5dd7b0ec01471667ae3a02520701aae405ac857/programs/rustfund/src/lib.rs#L90-106

Impact: High

  • Contributors lose their funds to fraudulent campaigns that withdraw immediately

  • Protocol becomes insolvent as funds can be stolen at any time

Proof of Concept

keep everything in `./tests/rustfund.rs` up on to `Contribute to fund` test, then add the below:
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));
});
goal 1000000000
fundBalance 537590960
creatorBalanceBefore 499999999460946370
creatorBalanceAfter 499999999960941400
fundBalanceAfter 37590960
✔ Creator withdraws funds when deadline is not reached (398ms)

Recommended Mitigation

Add check for if campaign as reached deadline before a creator can withdraw
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(())
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 5 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!