sherlock-audit / 2024-09-orderly-network-solana-contract-judging

0 stars 0 forks source link

Uneven Opaque Cottonmouth - Attacker will bridge fake tokens to Orderly for USDC #77

Open sherlock-admin3 opened 4 days ago

sherlock-admin3 commented 4 days ago

Uneven Opaque Cottonmouth

High

Attacker will bridge fake tokens to Orderly for USDC

Summary

Missing check in the deposit accounts struct will enable bridging of fake tokens to Orderly that will be redeemable for USDC.

Root Cause

In Deposit accounts struct the deposit_token is never checked to be the same token as depoit_params.token_hash (see here).

Internal pre-conditions

None

External pre-conditions

None

Attack Path

  1. The attacker calls deposit with the correct deposit parameters but provides a Mint account different from the USDC Mint while providing the correct user token addresses for the user and vault associated with this mint.
  2. The bridge sends the bridge message, which contains the USDC (or any token the platform aims to allow in the future)

Impact

Fake USDC will be accounted to the attacker on the Orderly chain, meaning they will be able to withdraw these tokens on Solana. This can be as much as all tokens inside the USDC vault account.

PoC

No response

Mitigation

Consider expanding the allowed token account verification to also verify whether the mint in the supplied allowed token matches the supplied deposit token.

#[derive(Accounts)]
#[instruction(deposit_params: DepositParams, oapp_params: OAppSendParams)]
pub struct Deposit<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    #[account(
        mut,
        associated_token::mint = deposit_token,
        associated_token::authority = user
    )]
    pub user_token_account: Box<Account<'info, TokenAccount>>,

    #[account(
        mut,
        seeds = [VAULT_AUTHORITY_SEED],
        bump = vault_authority.bump,
    )]
    pub vault_authority: Box<Account<'info, VaultAuthority>>,

    #[account(
        init_if_needed,
        payer = user,
        associated_token::mint = deposit_token,
        associated_token::authority = vault_authority
    )]
    pub vault_token_account: Box<Account<'info, TokenAccount>>,

    #[account()]
    pub deposit_token: Box<Account<'info, Mint>>,

    #[account(
        seeds = [
            PEER_SEED,
            &oapp_config.key().to_bytes(),
            &vault_authority.dst_eid.to_be_bytes()
        ],
        bump = peer.bump
    )]
    pub peer: Box<Account<'info, Peer>>,

    #[account(
        seeds = [
            ENFORCED_OPTIONS_SEED,
            &oapp_config.key().to_bytes(),
            &vault_authority.dst_eid.to_be_bytes()
        ],
        bump = enforced_options.bump
    )]
    pub enforced_options: Box<Account<'info, EnforcedOptions>>,

    #[account(
        seeds = [OAPP_SEED],
        bump = oapp_config.bump
    )]
    pub oapp_config: Box<Account<'info, OAppConfig>>,

    #[account(
        seeds = [BROKER_SEED, deposit_params.broker_hash.as_ref()],
        bump = allowed_broker.bump,
        constraint = allowed_broker.allowed == true @ VaultError::BrokerNotAllowed
    )]
    pub allowed_broker: Box<Account<'info, AllowedBroker>>,

    #[account(
        seeds = [TOKEN_SEED, deposit_params.token_hash.as_ref()],
        bump = allowed_token.bump,
-       constraint = allowed_token.allowed == true @ VaultError::TokenNotAllowed
+       constraint = allowed_token.allowed == true && allowed_token.mint_account == deposit_token.key() @ VaultError::TokenNotAllowed
    )]
    pub allowed_token: Box<Account<'info, AllowedToken>>,

    pub token_program: Program<'info, Token>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    pub system_program: Program<'info, System>,
}