Summary
The contract fails to update contribution account records with the amount contributed, causing a severe accounting issue.
Vulnerability Details
In the contribute function, the contract properly transfers SOL from the contributor to the fund account, but it doesn't update the individual contribution record with the amount contributed. While the function initializes a new contribution account, it sets the amount to 0 but never updates it with the actual contribution amount
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;
Impact
This vulnerability allows users to potentially refund multiple times for the same contribution, or refund more than they contributed, creating a significant risk of fund depletion.
POC
Add to tests/rustfund.ts:
it("Shows contribution amount not recorded", async () => {
[contributionPDA, contributionBump] = await PublicKey.findProgramAddress(
[fundPDA.toBuffer(), provider.wallet.publicKey.toBuffer()],
program.programId
);
ā
await program.methods
.contribute(contribution)
.accounts({
fund: fundPDA,
contributor: provider.wallet.publicKey,
contribution: contributionPDA,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
ā
const contributionAccount = await program.account.contribution.fetch(contributionPDA);
console.log(`Contributed ${contribution.toString()} lamports`);
console.log(`Recorded contribution amount: ${contributionAccount.amount.toString()}`);
});
Output:
========================================
š BUG REPORT [HIGH]: Missing Contribution Amount Update
----------------------------------------
Description: Contribution amounts are not recorded in the contribution account, allowing multiple refunds of the same contribution
Evidence: Contributed 500000000 lamports, but recorded contribution amount is: 0
========================================
Tools Used
Recommendations
Update the contribution amount in the contribute function:
// Initialize or update contribution record
if contribution.contributor == Pubkey::default() {
contribution.contributor = ctx.accounts.contributor.key();
contribution.fund = fund.key();
contribution.amount = 0;
}
ā
// Transfer SOL from contributor to fund account
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)?;
ā
// Update the contribution amount with the new amount
+ contribution.amount += amount;
fund.amount_raised += amount;