Rust Fund

AI First Flight #9
Beginner FriendlyRust
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

007_MEDIUM_contribution-amount-no-minimum

Description

The contribute function in the current implementation accepts any contribution amount > 0 (implied by u64, though 0 might also be accepted if not checked). Crucially, it lacks a Minimum Contribution Amount enforcement (e.g., amount >= MIN_AMOUNT).

Allowing extremely small contributions (e.g., 1 lamport) exposes the protocol and its users to "Dusting Attacks" and "Resource Exhaustion":

  1. State Bloat: If the protocol creates a new PDA (Program Derived Address) receipt for every contribution, an attacker can generate millions of accounts with 1 lamport each. This bloats the blockchain state and may hit per-program quotas or slow down indexers.

  2. Event Spam: It allows cheap flooding of the program's event log, making it difficult for off-chain systems to track legitimate activity.

  3. Economic Griefing: The cost of processing these transactions (compute) might outweigh the value they bring, degrading the overall system efficiency.

Risk

  • Severity: Medium

  • Likelihood: High

  • Impact: Low

Impact Details:

  1. Operational degradation: Indexers and UIs may crash or slow down attempting to load thousands of dust events.

  2. Storage Costs: If the contract pays for rent (or refunds it improperly), this could drain the operational treasury.

Proof of Concept

The following solana-program-test demonstrates how an attacker can loop through multiple "dust" transactions.

#![cfg(feature = "test-sbf")]
mod test_utils;
use solana_program_test::*;
use solana_sdk::{
signature::{Keypair, Signer},
transaction::Transaction,
pubkey::Pubkey,
instruction::Instruction,
};
#[tokio::test]
async fn test_vulnerability_dust_spam() {
// 1. Setup Environment
let program_id = Pubkey::new_unique();
let (mut banks_client, payer, recent_blockhash) = ProgramTest::new(
"crowdfunding_program",
program_id,
processor!(process_instruction),
)
.start()
.await;
// 2. Setup Campaign
let campaign_keypair = Keypair::new();
// ... Initialize campaign ...
// 3. Attack: Send 1 Lamport Contribution
let dust_amount: u64 = 1;
let contribute_ix = instruction::contribute(
&program_id,
dust_amount, // <--- 1 Lamport
&campaign_keypair.pubkey(),
&payer.pubkey(),
);
let mut transaction = Transaction::new_with_payer(
&[contribute_ix],
Some(&payer.pubkey()),
);
transaction.sign(&[&payer], recent_blockhash);
// 4. Execute
let result = banks_client.process_transaction(transaction).await;
// 5. Assert Weakness
// The transaction should typically FAIL if a minimum was enforced.
// Success indicates the vulnerability exists.
assert!(
result.is_ok(),
"VULNERABILITY: Program accepted a dust contribution of 1 lamport."
);
}

Recommended Mitigation Steps

Define a reasonable minimum constant (e.g., 0.01 SOL) and enforce it at the beginning of the contribute instruction.

Application of Fix

1. Define Constant:
Add const MIN_CONTRIBUTION: u64 = ...

2. Enforce Check:
Add require!(amount >= MIN_CONTRIBUTION, ...)

Code Diff:

// In your lib.rs or constants file
+ const MIN_CONTRIBUTION: u64 = 10_000_000; // 0.01 SOL
pub fn contribute(ctx: Context<Contribute>, amount: u64) -> Result<()> {
+ // MITIGATION: Prevent Dust Attacks
+ require!(
+ amount >= MIN_CONTRIBUTION,
+ CampaignError::ContributionTooSmall
+ );
let campaign = &mut ctx.accounts.campaign;
campaign.total_amount += amount;
Ok(())
}
// Update Error Enum
#[error_code]
pub enum CampaignError {
//...
+ #[msg("Contribution amount is below the minimum required.")]
+ ContributionTooSmall,
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!