Skip to content

Instantly share code, notes, and snippets.

@zigtur
Last active November 25, 2024 15:22
Show Gist options
  • Save zigtur/a20b7839d0351f34d4dcc2f0aa4eb75a to your computer and use it in GitHub Desktop.
Save zigtur/a20b7839d0351f34d4dcc2f0aa4eb75a to your computer and use it in GitHub Desktop.
Solana - Security for Builders
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
msg,
program::invoke,
program_error::ProgramError,
pubkey::Pubkey,
rent::Rent,
system_instruction::create_account,
sysvar::Sysvar,
};
entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey, // Current program ID being invoked
accounts: &[AccountInfo], // The list of accounts
instruction_data: &[u8], // The instruction to execute
) -> ProgramResult {
let instruction = Instructions::try_from_slice(instruction_data)?;
match instruction {
Instructions::Initialize { init_timestamp } => {
process_initialize(program_id, accounts, init_timestamp)
}
Instructions::CreateUser { user, amount } => {
process_create_user(program_id, accounts, user, amount)
}
}
}
pub fn process_initialize(
program_id: &Pubkey,
accounts: &[AccountInfo],
init_timestamp: u64,
) -> ProgramResult {
let accounts_iter = &mut accounts.iter();
let new_account = next_account_info(accounts_iter)?;
let signer = next_account_info(accounts_iter)?;
let system_program = next_account_info(accounts_iter)?;
if !signer.is_signer {
msg!("Signer account is not a valid signer.");
return Err(ProgramError::MissingRequiredSignature);
}
// No strict check for data or ownership to maintain the vulnerability
if new_account.owner != program_id && !new_account.data_is_empty() {
msg!("Target account already initialized or owned by another program.");
return Err(ProgramError::AccountAlreadyInitialized);
}
let account_data = Admin {
admin: *signer.key,
init_timestamp,
};
let size = account_data.try_to_vec()?.len();
let lamports = Rent::get()?.minimum_balance(size);
invoke(
&create_account(
signer.key,
new_account.key,
lamports,
size as u64,
program_id,
),
&[signer.clone(), new_account.clone(), system_program.clone()],
)?;
account_data.serialize(&mut *new_account.data.borrow_mut())?;
msg!("Initialized admin account with timestamp {}");
Ok(())
}
pub fn process_create_user(
program_id: &Pubkey,
accounts: &[AccountInfo],
user: Pubkey,
amount: u64,
) -> ProgramResult {
let accounts_iter = &mut accounts.iter();
let admin_account = next_account_info(accounts_iter)?;
if admin_account.owner != program_id {
msg!("Target admin account is owned by another program.");
return Err(ProgramError::InvalidAccountData);
}
let user_account = next_account_info(accounts_iter)?;
let signer = next_account_info(accounts_iter)?;
let system_program = next_account_info(accounts_iter)?;
let admin_data = Admin::try_from_slice(&admin_account.data.borrow())?;
if signer.key != &admin_data.admin {
msg!("Signer is not the admin of this account.");
return Err(ProgramError::InvalidAccountData);
}
if !signer.is_signer {
msg!("Signer account is not a valid signer.");
return Err(ProgramError::MissingRequiredSignature);
}
let user_account_data = User { user, amount };
let size = user_account_data.try_to_vec()?.len();
let lamports = Rent::get()?.minimum_balance(size);
invoke(
&create_account(
signer.key,
user_account.key,
lamports,
size as u64,
program_id,
),
&[signer.clone(), user_account.clone(), system_program.clone()],
)?;
user_account_data.serialize(&mut *user_account.data.borrow_mut())?;
msg!("Created user account with {} tokens.", amount);
Ok(())
}
#[derive(BorshSerialize, BorshDeserialize)]
pub enum Instructions {
Initialize { init_timestamp: u64 },
CreateUser { user: Pubkey, amount: u64 },
}
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct Admin {
pub admin: Pubkey,
pub init_timestamp: u64,
}
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct User {
pub user: Pubkey,
pub amount: u64,
}
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
msg,
program::invoke,
program_error::ProgramError,
pubkey::Pubkey,
rent::Rent,
system_instruction::create_account,
sysvar::Sysvar,
};
entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey, // Current program ID being invoked
accounts: &[AccountInfo], // The list of accounts
instruction_data: &[u8], // The instruction to execute
) -> ProgramResult {
let instruction = Instructions::try_from_slice(instruction_data)?;
match instruction {
Instructions::Initialize {} => process_initialize(program_id, accounts),
Instructions::CreateUser { user } => process_create_user(program_id, accounts, user),
}
}
pub fn process_initialize(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
let accounts_iter = &mut accounts.iter();
let new_account = next_account_info(accounts_iter)?;
let signer = next_account_info(accounts_iter)?;
let system_program = next_account_info(accounts_iter)?;
if !signer.is_signer {
msg!("Signer account is not a valid signer.");
return Err(ProgramError::MissingRequiredSignature);
}
if new_account.owner != program_id && !new_account.data_is_empty() {
msg!("Target account already initialized or owned by another program.");
return Err(ProgramError::AccountAlreadyInitialized);
}
let account_data = Admin { admin: *signer.key };
let size = account_data.try_to_vec()?.len();
let lamports = Rent::get()?.minimum_balance(size);
invoke(
&create_account(
signer.key,
new_account.key,
lamports,
size as u64,
program_id,
),
&[signer.clone(), new_account.clone(), system_program.clone()],
)?;
account_data.serialize(&mut *new_account.data.borrow_mut())?;
msg!("Initialized account");
Ok(())
}
pub fn process_create_user(
program_id: &Pubkey,
accounts: &[AccountInfo],
user: Pubkey,
) -> ProgramResult {
let accounts_iter = &mut accounts.iter();
let admin_account = next_account_info(accounts_iter)?;
let user_account = next_account_info(accounts_iter)?;
let signer = next_account_info(accounts_iter)?;
let system_program = next_account_info(accounts_iter)?;
let admin_data = Admin::try_from_slice(&admin_account.data.borrow())?;
if signer.key != &admin_data.admin {
msg!("Signer is not the admin of this account.");
return Err(ProgramError::InvalidAccountData);
}
if !signer.is_signer {
msg!("Signer account is not a valid signer.");
return Err(ProgramError::MissingRequiredSignature);
}
let user_account_data = User { user: user };
let size = user_account_data.try_to_vec()?.len();
let lamports = Rent::get()?.minimum_balance(size);
invoke(
&create_account(
signer.key,
user_account.key,
lamports,
size as u64,
program_id,
),
&[signer.clone(), user_account.clone(), system_program.clone()],
)?;
user_account_data.serialize(&mut *user_account.data.borrow_mut())?;
msg!("New user account");
Ok(())
}
#[derive(BorshSerialize, BorshDeserialize)]
pub enum Instructions {
Initialize {},
CreateUser { user: Pubkey },
}
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct Admin {
pub admin: Pubkey,
}
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct User {
pub user: Pubkey,
}
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
msg,
program::invoke,
program_error::ProgramError,
pubkey::Pubkey,
rent::Rent,
system_instruction::create_account,
sysvar::Sysvar,
};
entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey, // Current program ID being invoked
accounts: &[AccountInfo], // The list of accounts
instruction_data: &[u8], // The instruction to execute
) -> ProgramResult {
let instruction = Instructions::try_from_slice(instruction_data)?;
match instruction {
Instructions::Initialize { data } => process_initialize(program_id, accounts, data),
Instructions::UpdateData { data } => process_update_data(program_id, accounts, data),
}
}
pub fn process_initialize(
program_id: &Pubkey,
accounts: &[AccountInfo],
data: u64,
) -> ProgramResult {
let accounts_iter = &mut accounts.iter();
let new_account = next_account_info(accounts_iter)?;
let signer = next_account_info(accounts_iter)?;
let system_program = next_account_info(accounts_iter)?;
if !signer.is_signer {
msg!("Signer account is not a valid signer.");
return Err(ProgramError::MissingRequiredSignature);
}
if new_account.owner != program_id && !new_account.data_is_empty() {
msg!("Target account already initialized or owned by another program.");
return Err(ProgramError::AccountAlreadyInitialized);
}
let account_data = DataAccount {
admin: *signer.key,
data,
};
let size = account_data.try_to_vec()?.len();
let lamports = Rent::get()?.minimum_balance(size);
invoke(
&create_account(
signer.key,
new_account.key,
lamports,
size as u64,
program_id,
),
&[signer.clone(), new_account.clone(), system_program.clone()],
)?;
account_data.serialize(&mut *new_account.data.borrow_mut())?;
msg!("Initialized account with data: {:?}", data);
Ok(())
}
pub fn process_update_data(
program_id: &Pubkey,
accounts: &[AccountInfo],
data: u64,
) -> ProgramResult {
let accounts_iter = &mut accounts.iter();
let data_account = next_account_info(accounts_iter)?;
let signer = next_account_info(accounts_iter)?;
if data_account.owner != program_id {
msg!("Account is not owned by this program.");
return Err(ProgramError::IncorrectProgramId);
}
let mut account_data = DataAccount::try_from_slice(&data_account.data.borrow())?;
if signer.key != &account_data.admin {
msg!("Signer is not the admin of this account.");
return Err(ProgramError::InvalidAccountData);
}
account_data.data = data;
account_data.serialize(&mut *data_account.data.borrow_mut())?;
msg!("Updated account data to: {:?}", data);
Ok(())
}
#[derive(BorshSerialize, BorshDeserialize)]
pub enum Instructions {
Initialize { data: u64 },
UpdateData { data: u64 },
}
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct DataAccount {
pub admin: Pubkey,
pub data: u64,
}
# Native Solana security issues
This gist shows 3 vulnerable code examples in Native Rust Solana programs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment