RustFund

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

Contribution Amount Not Updated in contribute Function

01. Relevant GitHub Links

02. Summary

The contribute function in the RustFund program does not update the contribution.amount field after a user contributes to a fund. While fund.amount_raised is incremented correctly, contribution.amount remains 0, leading to inaccurate contribution tracking and potential refund issues.

03. Vulnerability Details

Users contribute to a fund using the contribute function:

pub fn contribute(ctx: Context<FundContribute>, amount: u64) -> Result<()> {
let fund = &mut ctx.accounts.fund;
let contribution = &mut ctx.accounts.contribution;
if fund.deadline != 0 && fund.deadline < Clock::get().unwrap().unix_timestamp.try_into().unwrap() { // @note : unwrap는 Result 타입에서 반환된 값을 가져오거나 Error이면 패닉, try_into는 type을 변환하려고 시도
return Err(ErrorCode::DeadlineReached.into());
}
// Initialize or update contribution record
if contribution.contributor == Pubkey::default() {
contribution.contributor = ctx.accounts.contributor.key();
contribution.fund = fund.key();
contribution.amount = 0;
}
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)?;
fund.amount_raised += amount;
Ok(())
}

The issue lies in contribution.amount being initialized to 0 and never updated with the contributed amount, remaining 0 even after a successful contribution.

04. Impact

This vulnerability causes inaccurate tracking of individual contributions, as contribution.amount does not reflect the user’s contribution while fund.amount_raised is updated correctly. Additionally, this prevents users from refunding their contributions later. In the refund function, the amount to refund is sourced from contribution.amount, which remains 0 due to this bug. As a result, users cannot reclaim their funds even if the deadline passes and refunds are allowed, effectively locking their contributions.

05. Proof of Concept

Add the following PoC code to rustfund.ts to test the vulnerability:

it.only("contribution.amount is naver update", async () => {
// 1. Generate PDA for contribution
[contributionPDA, contributionBump] = await PublicKey.findProgramAddress(
[fundPDA.toBuffer(), provider.wallet.publicKey.toBuffer()],
program.programId
);
// 1. create fund
await program.methods
.fundCreate(fundName, description, goal)
.accounts({
fund: fundPDA,
creator: creator.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
// 2. contribute
await program.methods
.contribute(contribution)
.accounts({
fund: fundPDA,
contributor: provider.wallet.publicKey,
contribution: contributionPDA,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
// 3. check contribution
const contributionAccount = await program.account.contribution.fetch(contributionPDA);
console.log("contributionAccount.amount: ", await contributionAccount.amount);
expect(contributionAccount.amount.toString()).to.equal((0).toString());
});

Output:

rustfund
contributionAccount.amount: <BN: 0>
✔ contribution.amount is naver update (694ms)

06. Tools Used

Manual Code Review and Foundry

07. Recommended Mitigation

pub fn contribute(ctx: Context<FundContribute>, amount: u64) -> Result<()> {
let fund = &mut ctx.accounts.fund;
let contribution = &mut ctx.accounts.contribution;
if fund.deadline != 0 && fund.deadline < Clock::get().unwrap().unix_timestamp.try_into().unwrap() {
return Err(ErrorCode::DeadlineReached.into());
}
if contribution.contributor == Pubkey::default() {
contribution.contributor = ctx.accounts.contributor.key();
contribution.fund = fund.key();
contribution.amount = 0;
}
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)?;
+ contribution.amount += amount;
fund.amount_raised += amount;
Ok(())
}
Updates

Appeal created

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

Contribution amount is not updated

Support

FAQs

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