RustFund

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

Incorrect Logic in withdraw() Allows Multiple Withdrawals by Fund Creator

Summary

The withdraw() function transfers the full fund amount to the fund creator but does not reset the amount_raised variable after withdrawal. This allows the fund creator to repeatedly withdraw funds, leading to inconsistencies in accounting. As there is no deadline check in this function, it will also allow creator to call this function multiple times draing the funds from the contributors before the goals and deadline is reached

Vulnerability Details

The function retrieves the fund’s balance and transfers it to the creator. However, after the transfer, fund.amount_raised is not reset to 0, meaning the same amount can be withdrawn multiple times.

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

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)?;
//@audit - No update to amount_raised after tokens are transfered
Ok(())
}
}

Impact

Fund accounting becomes inconsistent, as amount_raised does not reflect actual withdrawals

Fund creator can repeatedly call withdraw() function. As there is no deadline check here, it will also allow creator to call this function multiple times.

Tools Used

Manual review

Recommendations

Reset fund.amount_raised to 0 after the withdrawal to prevent multiple withdrawals

pub fn withdraw(ctx: Context<FundWithdraw>) -> Result<()> {
let amount = ctx.accounts.fund.amount_raised;
//@audit - Update amount_raised to zero like this
+ ctx.accounts.fund.amount_raised = 0;
**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

Appeal created

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

`amount_raised` is not reset to 0 in `withdraw` function

Support

FAQs

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