RustFund

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

Missing Fund Account Validation in Contribute Function

Summary

The 'contribute' function fails to properly validate the user-provided fund account, allowing malicious actors to divert contributions to arbitrary accounts

Vulnerability Details

In the contribute function context(FundContribute), the protocol accepts a fund account as input without verifying. Unlike other functioins in the protocol (Such as withdraw), there is no PDA validation constraint to ensure the fund account was created by the protocol.

#[derive(Accounts)]
pub struct FundContribute<'info> {
#[account(mut)]
pub fund: Account<'info, Fund>, //@audit --> Fund Account is not verifyed.
#[account(mut)]
pub contributor: Signer<'info>,
#[account(
init_if_needed,
payer = contributor,
space = 8 + Contribution::INIT_SPACE,
seeds = [fund.key().as_ref(), contributor.key().as_ref()],
bump
)]
pub contribution: Account<'info, Contribution>,
pub system_program: Program<'info, System>,
}
let cpi_context = CpiContext::new(
ctx.accounts.system_program.to_account_info(),
system_program::Transfer {
from: ctx.accounts.contributor.to_account_info(),
to: fund.to_account_info(),
},
);
system_program::transfer(cpi_context, amount)?;

Attack Scenario

An Attacker could exploit this vulnerability as follows:

  1. A legitimate user (User A) creates a fund through the fund_create function with proper PDA derivation.

  2. A malicious actor (Attacker B) creates a fake fund account or uses an existing account they control.

  3. When the User calls the contribute function by using the fake fund account. The protocol transfer the funds from the user account to the fake funds account(which is controlled by the User) and the fund.amount_raised will be increaed although the funds were not received to the Original Fund Account.

Impact

High

Tools Used

Manual Review

Recommendations

#[derive(Accounts)]
pub struct FundContribute<'info> {
#[account(
mut,
seeds = [fund.name.as_bytes(), creator.key().as_ref()],
bump
)] //Add this line
pub fund: Account<'info, Fund>,
#[account(mut)]
pub contributor: Signer<'info>,
#[account(
init_if_needed,
payer = contributor,
space = 8 + Contribution::INIT_SPACE,
seeds = [fund.key().as_ref(), contributor.key().as_ref()],
bump
)]
pub contribution: Account<'info, Contribution>,
pub system_program: Program<'info, System>,
}
Updates

Lead Judging Commences

bube Lead Judge
5 months ago

Appeal created

bube Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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