Skip to content

Commit 1fa8c5d

Browse files
authored
Merge pull request #341 from crytic/not-so-solana-instruction
Add improper_instruction_introspection issue to not-so-smart-contracts Solana
2 parents 12ad92c + 756ddc8 commit 1fa8c5d

File tree

2 files changed

+65
-7
lines changed

2 files changed

+65
-7
lines changed

not-so-smart-contracts/solana/README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ Each _Not So Smart Contract_ includes a standard set of information:
1414

1515
## Vulnerabilities
1616

17-
| Not So Smart Contract | Description |
18-
| -------------------------------------------------- | --------------------------------------------------------- |
19-
| [Arbitrary CPI](arbitrary_cpi) | Arbitrary program account passed in upon invocation |
20-
| [Improper PDA Validation](improper_pda_validation) | PDAs are vulnerable to being spoofed via bump seeds |
21-
| [Ownership Check](ownership_check) | Broken access control due to missing ownership validation |
22-
| [Signer Check](signer_check) | Broken access control due to missing signer validation |
23-
| [Sysvar Account Check](sysvar_account_check) | Sysvar accounts are vulnerable to being spoofed |
17+
| Not So Smart Contract | Description |
18+
| ------------------------------------------------------------------------ | --------------------------------------------------------- |
19+
| [Arbitrary CPI](arbitrary_cpi) | Arbitrary program account passed in upon invocation |
20+
| [Improper PDA Validation](improper_pda_validation) | PDAs are vulnerable to being spoofed via bump seeds |
21+
| [Ownership Check](ownership_check) | Broken access control due to missing ownership validation |
22+
| [Signer Check](signer_check) | Broken access control due to missing signer validation |
23+
| [Sysvar Account Check](sysvar_account_check) | Sysvar accounts are vulnerable to being spoofed |
24+
| [Improper Instruction Introspection](improper_instruction_introspection) | Program accesses instruction using absolute index |
2425

2526
## Credits
2627

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Improper Instruction Introspection
2+
3+
Solana allows programs to inspect other instructions in the transaction using the [Instructions sysvar](https://docs.solanalabs.com/implemented-proposals/instruction_introspection). The programs requiring instruction introspection divide an operation into two or more instructions. The program have to ensure that all the instructions related to an operation are correlated. The program could access the instructions using absolute indexes or relative indexes. Using relative indexes ensures that the instructions are implicitly correlated. The programs using absolute indexes might become vulnerable to exploits if additional validations to ensure the correlation between instructions are not performed.
4+
5+
## Exploit Scenario
6+
7+
A program mints tokens based on the amount of tokens transferred to it. A program checks that `Token::transfer` instruction is called in the first instruction of the transaction. The program uses absolute index `0` to access the instruction data, program id and validates them. If the first instruction is a `Token::transfer` then program mints some tokens.
8+
9+
```rust
10+
pub fn mint(
11+
ctx: Context<Mint>,
12+
// ...
13+
) -> Result<(), ProgramError> {
14+
// [...]
15+
let transfer_ix = solana_program::sysvar::instructions::load_instruction_at_checked(
16+
0usize,
17+
ctx.instructions_account.to_account_info(),
18+
)?;
19+
20+
if transfer_ix.program_id != spl_token::id() {
21+
return Err(ProgramError::InvalidInstructionData);
22+
}
23+
// check transfer_ix transfers
24+
// mint to the user account
25+
// [...]
26+
Ok(())
27+
}
28+
```
29+
30+
The program uses absolute index to access the transfer instruction. An attacker can create transaction containing multiple calls to `mint` and single transfer instruction.
31+
32+
0. `transfer()`
33+
1. `mint(, ...)`
34+
2. `mint(, ...)`
35+
3. `mint(, ...)`
36+
4. `mint(, ...)`
37+
5. `mint(, ...)`
38+
39+
All the `mint` instructions verify the same transfer instruction. The attacker gets 4 times more than the intended tokens.
40+
41+
## Mitigation
42+
43+
Use a relative index, for example `-1`, and ensure the instruction at that offset is the `transfer` instruction.
44+
45+
```rust
46+
pub fn mint(
47+
ctx: Context<Mint>,
48+
// ...
49+
) -> Result<(), ProgramError> {
50+
// [...]
51+
let transfer_ix = solana_program::sysvar::instructions::get_instruction_relative(
52+
-1i64,
53+
ctx.instructions_account.to_account_info(),
54+
)?;
55+
// [...]
56+
}
57+
```

0 commit comments

Comments
 (0)