RustFund

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

Spam & Resources Abuse Caused by Zero-Value Contributions

Summary

The contribute function allows zero-value contributions, enabling attackers to execute transactions that update state and consume compute units without transferring any funds. This inefficiency can be exploited to create spam transactions, bloating the Solana blockchain state, increasing operational costs, and indirectly leading to a denial-of-service (DoS) risk.

Vulnerability Details

  • Zero-Value Contributions Allowed: The function does not check whether amount > 0, allowing transactions that update state and consume compute resources without transferring any funds.

  • State Pollution: Every transaction unnecessarily writes to the Contribution account, increasing storage costs and making state reads slower over time.

  • Compute Budget Exhaustion: Attackers can flood the network with zero-value transactions, consuming valuable compute units and reducing throughput for legitimate users.

Impact

  • Spam Transactions:
    Attackers can generate a large number of zero-value contributions, increasing the blockchain storage and clogging the network.

  • Denial of Service (DoS) Risk:
    Since each transaction consumes compute units, an attacker could slow down or disrupt legitimate user interactions.

  • Unnecessary On-Chain State Growth:
    Since contributions are stored on-chain, excessive spam increases the cost of maintaining and interacting with the contract.

Tools Used

Anchor Tests: To simulate zero-value contributions and analyze execution costs.

Proof of Concept

The following test demonstrates how an attacker can repeatedly submit zero-value contributions, executing transactions that update state without transferring funds. The program fails to reject these contributions, allowing infinite spam transactions.

it("Allows zero-value contributions, causing unnecessary state updates", async () => {
// Fetch the initial fund state
let fund = await program.account.fund.fetch(fundPDA);
const initialAmountRaised = fund.amountRaised.toString();
console.log("Initial amount raised:", initialAmountRaised);
// Attempt to contribute 0 SOL
await program.methods
.contribute(new anchor.BN(0)) // Zero-value contribution
.accounts({
fund: fundPDA,
contributor: contributor.publicKey,
contribution: contributionPDA,
systemProgram: anchor.web3.SystemProgram.programId,
})
.signers([contributor])
.rpc();
// Fetch updated fund state after zero-value contribution
fund = await program.account.fund.fetch(fundPDA);
const newAmountRaised = fund.amountRaised.toString();
console.log("New amount raised:", newAmountRaised);
// Fetch the contribution record
let contribution = await program.account.contribution.fetch(contributionPDA);
console.log("Contribution amount recorded:", contribution.amount.toString());
// Expected behavior: Contribution should be rejected
// Actual behavior: Contribution is recorded, proving the vulnerability
expect(contribution.amount.toString()).to.equal("0");
expect(newAmountRaised).to.equal(initialAmountRaised);
});

The transaction executes successfully and contribution account updates incorrectly, showing a recorded contribution despite sending 0 SOL.

Recommendations

Option One: reject zero-value contributions

pub fn contribute(ctx: Context<FundContribute>, amount: u64) -> Result<()> {
let fund = &ctx.accounts.fund;
let contribution = &ctx.accounts.contribution;
if amount == 0 {
return Err(ErrorCode::ZeroContribution.into());
}
// Rest of the function logic...
}

Option Two: Set a minimum contribution threshold

pub fn contribute(ctx: Context<FundContribute>, amount: u64) -> Result<()> {
let fund = &ctx.accounts.fund;
let contribution = &ctx.accounts.contribution;
const MIN_CONTRIBUTION: u64 = 1000;
if amount < MIN_CONTRIBUTION {
return Err(ErrorCode::ContributionTooSmall.into());
}
// Rest of the function logic...
}
Updates

Appeal created

bube Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

[Invalid] Lack of minimal `amount` in `contribute` function

If user contributes 0 SOL, the `contribution.amount` will be updated with 0 value. There is no impact on the protocol. Also, the new contributers should pay for account creation, therefore there is no incentive someone to create a very huge number of accounts to contribute zero amount.

Support

FAQs

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