RustFund

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

withdraw() allows creator to cash out funds at any time

Summary

withdraw() contains no check to see if the campaign's goal has reached and its deadline has passed. This allows the creator to, unfairly to the contributors, withdraw the contributions before all criteria have been met.

Vulnerability Details

pub fn withdraw(ctx: Context<FundWithdraw>) -> Result<()> {
let amount = ctx.accounts.fund.amount_raised;
//@audit no check to see if goal has reached and deadline has passed
**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

Add the following test in tests/rustfund.ts after the "Contributes to fund" test and before the "Refunds contribution" test. This test demonstrates that the creator can immediately withdraw funds after there has been contributions.

it("Creator can withdraw at any time", async () => {
const creatorBalanceBefore = await provider.connection.getBalance(creator.publicKey);
const fundBalanceBefore = await provider.connection.getBalance(fundPDA);
console.log("creatorBalanceBefore", creatorBalanceBefore);
console.log("fundBalanceBefore", fundBalanceBefore);
// Attempt to withdraw immediately after contribution, before deadline
await program.methods
.withdraw()
.accounts({
fund: fundPDA,
creator: creator.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
const creatorBalanceAfter = await provider.connection.getBalance(creator.publicKey);
const fundBalanceAfter = await provider.connection.getBalance(fundPDA);
const fund = await program.account.fund.fetch(fundPDA);
console.log("creatorBalanceAfter", creatorBalanceAfter);
console.log("fundBalanceAfter", fundBalanceAfter);
console.log("fundAmountRaisedAfter", fund.amountRaised);
// Verify that the creator received the funds
expect(creatorBalanceAfter).to.be.greaterThan(creatorBalanceBefore);
expect(fundBalanceAfter).to.be.lessThan(fundBalanceBefore);
});
creatorBalanceBefore 499999999460946370
fundBalanceBefore 537590960
creatorBalanceAfter 499999999960941400
fundBalanceAfter 37590960
fundAmountRaisedAfter <BN: 1dcd6500>
✔ Creator can withdraw at any time (400ms)

Impact

High. Funds can be deemed as lost since goal does not need to be met. Undermines contributor's trust.

Tools Used

Manual review, anchor testing

Recommendations

Add goal and deadline checks to restrict withdrawals to successful campaigns.

Updates

Appeal created

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

No deadline check in `withdraw` function

No goal achievement check in `withdraw` function

Support

FAQs

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