Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Add sealevel-exploits along with descriptions to docs #2041

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1577045
adding sea-level-exploits to docs
nheingit Jul 7, 2022
9fad5b2
adding sea-level-exploits to docs
nheingit Jul 7, 2022
34045b7
added owner-check exploit content
nheingit Jul 7, 2022
409b5f5
added type-cosplay exploit to docs
nheingit Jul 7, 2022
87c0ac7
added initalization exploit to docs and fixed some spacing issues on …
nheingit Jul 7, 2022
b52bae2
added missing "secure" section that was on TODO
nheingit Jul 7, 2022
df1115a
added arbitrary cpi overview
nheingit Jul 10, 2022
695f779
added bump-seed and duplicate-account exploits
nheingit Jul 12, 2022
a067463
adding pda-sharing-exploit
nheingit Jul 12, 2022
16e1108
Merge branch 'master' of https://github.com/coral-xyz/anchor into add…
nheingit Jul 12, 2022
971545e
no descriptions, but added all code examples of closing accounts
nheingit Jul 12, 2022
963b151
Merge remote-tracking branch 'upstream/master'
nheingit Jul 20, 2022
101cb4b
adding sea-level-exploits to docs
nheingit Jul 7, 2022
aee614d
added owner-check exploit content
nheingit Jul 7, 2022
ff0f223
added type-cosplay exploit to docs
nheingit Jul 7, 2022
4ee50fd
added initalization exploit to docs and fixed some spacing issues on …
nheingit Jul 7, 2022
3693400
added missing "secure" section that was on TODO
nheingit Jul 7, 2022
b153525
added arbitrary cpi overview
nheingit Jul 10, 2022
c1e31a8
added bump-seed and duplicate-account exploits
nheingit Jul 12, 2022
b3d4735
adding pda-sharing-exploit
nheingit Jul 12, 2022
d3ab2a4
no descriptions, but added all code examples of closing accounts
nheingit Jul 12, 2022
cd39ccb
Merge branch 'add-exploits-to-docs' of https://github.com/nheingit/an…
nheingit Jul 20, 2022
20cc71d
accidently changed package.lock
nheingit Jul 20, 2022
c364de0
Update docs/src/pages/docs/pda-sharing-exploit.md
nheingit Aug 4, 2022
f346b5f
Apply suggestions from code review
nheingit Aug 4, 2022
cb46474
Update docs/src/pages/docs/arbitrary-cpi-exploit.md
nheingit Aug 4, 2022
0101d58
Update docs/src/pages/docs/bump-seed-canonicalization-exploit.md
nheingit Aug 27, 2022
71f8db2
Merge branch 'add-exploits-to-docs' of github.com:nheingit/anchor int…
Dec 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 46 additions & 1 deletion docs/src/pages/_app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,52 @@ const navigation = [
},
{
title: 'Common Security Exploits',
links: [{ title: 'Sealevel Attacks', href: '/docs/security-exploits' }],
links: [
{
title: 'Sealevel Attacks',
href: '/docs/security-exploits'
},
{
title: 'Signer Authorization',
href: '/docs/signer-authorization-exploit'
},
{
title: 'Account Data Matching',
href: '/docs/account-data-matching-exploit'
},
{
title: 'Owner Checks',
href: '/docs/owner-checks-exploit'
},
{
title: 'Type Cosplay',
href: '/docs/type-cosplay-exploit'
},
{
title: 'Initialization',
href: '/docs/initialization-exploit'
},
{
title: 'Arbitrary CPI',
href: '/docs/arbitrary-cpi-exploit'
},
{
title: 'Duplicate Mutable Accounts',
href: '/docs/duplicate-mutable-accounts-exploit'
},
{
title: 'Bump Seed Canonicalization',
href: '/docs/bump-seed-canonicalization-exploit'
},
{
title: 'PDA Sharing',
href: '/docs/pda-sharing-exploit'
},
{
title: 'Closing Accounts',
href: '/docs/closing-accounts-exploit'
},
]
},
]

Expand Down
108 changes: 108 additions & 0 deletions docs/src/pages/docs/account-data-matching-exploit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
title: Account Data Matching exploit
description: Anchor - Account Data Matching exploit
---

## Overview
The account data matching vulnerability is when your program does not properly validate the structure of the data you are acting upon.
When you do not properly validate your program's data shape, a bad actor can pass in arbitrary data that executes in malicious and unexpected ways.
See the example below.
### Insecure

```rust
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program_pack::Pack;
use spl_token::state::Account as SplTokenAccount;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod account_data_matching_insecure {
use super::*;

pub fn log_message(ctx: Context<LogMessage>) -> ProgramResult {
// token.data could be any `AccountInfo`
let token = SplTokenAccount::unpack(&ctx.accounts.token.data.borrow())?;
msg!("Your account balance is: {}", token.amount);
Ok(())
}
}

#[derive(Accounts)]
pub struct LogMessage<'info> {
token: AccountInfo<'info>,
authority: Signer<'info>,
}
```
This code is insecure because we do not validate the the value our `LogMessage` token takes in is *actually* a token account.
All we do is check that it is of the type `AccountInfo`; which could be any number of different things.
To patch this you need to check that the `LogMessage` has an `authority` key that matches the `token.owner`'s.

### Secure

```rust
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program_pack::Pack;
use spl_token::state::Account as SplTokenAccount;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod account_data_matching_secure {
use super::*;

pub fn log_message(ctx: Context<LogMessage>) -> ProgramResult {
let token = SplTokenAccount::unpack(&ctx.accounts.token.data.borrow())?;
// check happens here
if ctx.accounts.authority.key != &token.owner {
return Err(ProgramError::InvalidAccountData);
}
msg!("Your acocunt balance is: {}", token.amount);
Ok(())
}
}

#[derive(Accounts)]
pub struct LogMessage<'info> {
token: AccountInfo<'info>,
authority: Signer<'info>,
}
```
This is now secured since we validated that the data matches our expected values.
But this makes our programs a bit noisy.
Anchor provides us the ability to do this validation inside our `LogMessage` struct, which cleans up our program's code.

### Recommended
```rust
use anchor_lang::prelude::*;
use anchor_spl::token::TokenAccount;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod account_data_matching_recommended {
use super::*;

pub fn log_message(ctx: Context<LogMessage>) -> ProgramResult {
msg!("Your acocunt balance is: {}", ctx.accounts.token.amount);
Ok(())
}
}

#[derive(Accounts)]
pub struct LogMessage<'info> {
// These two lines are where the magic happens
#[account(constraint = authority.key == &token.owner)]
token: Account<'info, TokenAccount>,
authority: Signer<'info>,
}
```
Here you can see that in the program logic we are no longer doing any validation.
This makes it much easier to see what is going on in the program, without all of the noise that data validation causes.
Validation has moved from the program to `LogMessage`.


We have imported the `TokenAccount` which we use in place of the generic `AccountInfo`.
This will check that the account that gets passed into `token` has valid data.
We also have the ability to add arbitrary validations through the raw constraint expression.
In this instance we check that the `authority.key == &token.owner`.
143 changes: 143 additions & 0 deletions docs/src/pages/docs/arbitrary-cpi-exploit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
title: Arbitrary CPI exploit
description: Anchor - In this Chapter of Common Security Exploits of Sealevel, or Sealevel Attacks, we do a deep dive on how malicious actors can invoke arbitrary programs using CPI to breach your security.
---

## Overview
The arbitrary CPI vulnerability is when your program does not properly validate the program that it is invoking.
When you do not properly check the program's address, you could potentially call *any* program, which could have severe consequences.
See the example below.
### Insecure

```rust
use anchor_lang::prelude::*;
use anchor_lang::solana_program;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod arbitrary_cpi_insecure {
use super::*;

pub fn cpi(ctx: Context<Cpi>, amount: u64) -> ProgramResult {
solana_program::program::invoke(
&spl_token::instruction::transfer(
ctx.accounts.token_program.key,
ctx.accounts.source.key,
ctx.accounts.destination.key,
ctx.accounts.authority.key,
&[],
amount,
)?,
&[
ctx.accounts.source.clone(),
ctx.accounts.destination.clone(),
ctx.accounts.authority.clone(),
],
)
}
}

#[derive(Accounts)]
pub struct Cpi<'info> {
source: AccountInfo<'info>,
destination: AccountInfo<'info>,
authority: AccountInfo<'info>,
token_program: AccountInfo<'info>,
}
```
This code is insecure because we are invoking a program that the user passed in.
Any data that is entered by a user needs to be validated.
To patch this you need to check that that the *expected* program's address is the same as the one the user passed in.

### Secure

```rust
use anchor_lang::prelude::*;
use anchor_lang::solana_program;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod arbitrary_cpi_secure {
use super::*;

pub fn cpi_secure(ctx: Context<Cpi>, amount: u64) -> ProgramResult {
// Make sure the program's address matches our user's passed in program.
if &spl_token::ID != ctx.accounts.token_program.key {
return Err(ProgramError::IncorrectProgramId);
}
solana_program::program::invoke(
&spl_token::instruction::transfer(
ctx.accounts.token_program.key,
ctx.accounts.source.key,
ctx.accounts.destination.key,
ctx.accounts.authority.key,
&[],
amount,
)?,
&[
ctx.accounts.source.clone(),
ctx.accounts.destination.clone(),
ctx.accounts.authority.clone(),
],
)
}
}

#[derive(Accounts)]
pub struct Cpi<'info> {
source: AccountInfo<'info>,
destination: AccountInfo<'info>,
authority: AccountInfo<'info>,
token_program: AccountInfo<'info>,
}
```
This is now secured since we validated that the data matches our expected values.
However, using Anchor we can clean this up a bit.

### Recommended
```rust
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Token, TokenAccount};

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod arbitrary_cpi_recommended {
use super::*;

pub fn cpi(ctx: Context<Cpi>, amount: u64) -> ProgramResult {
token::transfer(ctx.accounts.transfer_ctx(), amount)
}
}

#[derive(Accounts)]
pub struct Cpi<'info> {
source: Account<'info, TokenAccount>,
destination: Account<'info, TokenAccount>,
authority: Signer<'info>,
token_program: Program<'info, Token>,
}

impl<'info> Cpi<'info> {
pub fn transfer_ctx(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
let program = self.token_program.to_account_info();
let accounts = token::Transfer {
from: self.source.to_account_info(),
to: self.destination.to_account_info(),
authority: self.authority.to_account_info(),
};
CpiContext::new(program, accounts)
}
}
```
Here you can see that in the program logic we are no longer doing any validation.
This makes it much easier to see what is going on in the program, without all of the noise that data validation causes.
In this instance, we use Anchor's wrapper around the SPL token program to check everything.

Another thing to note is that we've further secured our program by implementing the `transfer_ctx` function for our `Cpi` account struct.
This gives us full confidence that we know the data that we are be working with.

Not only do we make use of the `Program` struct for the `token_program`, but we also make use of the `Account` struct to make sure that the `source` and `destination` are actually token accounts.
While Anchor has an already existing wrapper for the SPL token program, you can see how to implement this yourself for other non-Anchor programs that you want to interact with [here](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/account/struct.Account.html#using-account-with-non-anchor-programs).