RustFund

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

No check for if campaign reached deadline before withdraw

Summary

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.

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.

Tools Used

Manuel code review

Recommendations

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

POC

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

this outputs:

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.

Updates

Appeal created

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

No deadline check in `withdraw` function

bshyuunn Auditor
8 months ago
bube Lead Judge
8 months ago
bube Lead Judge 8 months ago
Submission Judgement Published
Validated
Assigned finding tags:

No deadline check in `withdraw` function

Support

FAQs

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