RustFund

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

Improper Access Control in Refund and Withdraw Functions

01. Relevant GitHub Links

02. Summary

The contract’s refund and withdraw functions do not enforce the conditions outlined in the documentation. Refunds are allowed if the deadline is unset or passed, without checking the goal, and creators can withdraw funds at any time, even if the goal isn’t met. Additionally, the amount_raised value isn’t updated during refunds, which can lock funds and cause withdraw failures.

03. Vulnerability Details

The documentation states that refunds should only occur after the deadline if the goal isn’t met, and withdrawals should only be allowed after the campaign succeeds (goal is met).

Refund Mechanism: Contributors can get refunds if deadlines are reached and goals aren't met

Secure Withdrawals: Creators can withdraw funds once their campaign succeeds

However:

  • In the refund function, there’s no check for the goal. Refunds proceed if the deadline is unset or passed.

  • In the withdraw function, there’s no goal check either, allowing creators to withdraw funds anytime.

  • Another issue: refund doesn’t subtract the refunded amount from fund.amount_raised. Since withdraw uses amount_raised to determine the withdrawal amount, this can lead to failures if the actual balance is lower, locking the funds.

04. Impact

  • Contributors can be refunded even when they shouldn’t be, undermining the campaign’s integrity.

  • Creators can withdraw funds prematurely, potentially stealing contributions.

  • Funds can become locked due to the mismatch between amount_raised and the actual balance, preventing legitimate withdrawals.

05. Tools Used

Manual Code Review and Foundry

06. Recommended Mitigation

Add goal checks to both functions and update amount_raised during refunds:

  • For refund: Ensure it only works if the deadline is set, passed, and the goal isn’t met.

  • For withdraw: Allow withdrawals only if the goal is met.

  • Subtract refunded amounts from fund.amount_raised.

pub fn refund(ctx: Context<FundRefund>) -> Result<()> {
let amount = ctx.accounts.contribution.amount;
- if ctx.accounts.fund.deadline != 0 && ctx.accounts.fund.deadline > Clock::get().unwrap().unix_timestamp.try_into().unwrap() {
+ if ctx.accounts.fund.deadline != 0 && ctx.accounts.fund.deadline > Clock::get().unwrap().unix_timestamp.try_into().unwrap() || !ctx.accounts.fund.dealine_set {
return Err(ErrorCode::DeadlineNotReached.into());
}
+ if ctx.accounts.fund.goal <= ctx.accounts.fund.amount_raised {
+ return Err(ErrorCode::GoalReached.into());
+ }
**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.contributor.to_account_info().try_borrow_mut_lamports()? =
ctx.accounts.contributor.to_account_info().lamports()
.checked_add(amount)
.ok_or(ErrorCode::CalculationOverflow)?;
// Reset contribution amount after refund
ctx.accounts.contribution.amount = 0;
Ok(())
}
#[error_code]
pub enum ErrorCode {
#[msg("Deadline already set")]
DeadlineAlreadySet,
#[msg("Deadline reached")]
DeadlineReached,
#[msg("Deadline not reached")]
DeadlineNotReached,
#[msg("Unauthorized access")]
UnauthorizedAccess, // @question : 사용 안되는데?
#[msg("Calculation overflow occurred")]
CalculationOverflow,
+ #[msg("Goal Reached")]
+ GoalReached,
}
pub fn withdraw(ctx: Context<FundWithdraw>) -> Result<()> {
let amount = ctx.accounts.fund.amount_raised;
+ if ctx.accounts.fund.goal > ctx.accounts.fund.amount_raised {
+ return Err(ErrorCode::GoalNotReached.into());
+ }
**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(())
}
#[error_code]
pub enum ErrorCode {
#[msg("Deadline already set")]
DeadlineAlreadySet,
#[msg("Deadline reached")]
DeadlineReached,
#[msg("Deadline not reached")]
DeadlineNotReached,
#[msg("Unauthorized access")]
UnauthorizedAccess, // @question : 사용 안되는데?
#[msg("Calculation overflow occurred")]
CalculationOverflow,
+ #[msg("Goal Not Reached")]
+ GoalNotReached,
}
Updates

Appeal created

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

There is no check for goal achievement in `refund` function

No goal achievement check in `withdraw` function

`amount_raised` not updated in `refund` function

Support

FAQs

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