RustFund

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

No check for goal reached in withdraw

Summary

A Malicious creator can withdraw funds before goal is reached.

Vulnerability Details

There is no check in withdraw for if the goal of the campaign is reached before a creator can withdraw the 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 goal is reached which is against the logic of the program.

Tools Used

Manuel code review

Recommendations

Add check for if goal is reached before withdraws

pub fn withdraw(ctx: Context<FundWithdraw>) -> Result<()> {
//add this
if ctx.accounts.fund.amount_raised >= ctx.accounts.fund.goal {
return ;//return the correct ErrorCode
}
//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 goal is not reached", async () => {
const creatorBalanceBefore = await provider.connection.getBalance(creator.publicKey);
const fund = await program.account.fund.fetch(fundPDA);
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 goal is not reached (410ms)

We can notice that the creator withdraws funds from the campaign without the 1 sol goal being reached.

Updates

Appeal created

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

No goal achievement check in `withdraw` function

Support

FAQs

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