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

GPT Review not-so-smart-contracts/substrate #274

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions not-so-smart-contracts/substrate/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# (Not So) Smart Pallets

This repository contains examples of common Substrate pallet vulnerabilities. Use Not So Smart Pallets to learn about Substrate vulnerabilities, as a reference when performing security reviews, and as a benchmark for security and analysis tools.
This repository contains examples of common vulnerabilities in Substrate pallets. Use the Not So Smart Pallets to learn about Substrate vulnerabilities, as a reference for performing security reviews, and as a benchmark for security analysis tools.

## Features

Each _Not So Smart Pallet_ includes a standard set of information:

- Description of the vulnerability type
- Attack scenarios to exploit the vulnerability
- Recommendations to eliminate or mitigate the vulnerability
- Attack scenarios for exploiting the vulnerability
- Recommendations for eliminating or mitigating the vulnerability
- A mock pallet that exhibits the flaw
- References to third-party resources with more information

Expand All @@ -28,4 +28,4 @@ Each _Not So Smart Pallet_ includes a standard set of information:

These examples are developed and maintained by [Trail of Bits](https://www.trailofbits.com/).

If you have questions, problems, or just want to learn more, then join the #ethereum channel on the [Empire Hacking Slack](https://empireslacking.herokuapp.com/) or [contact us](https://www.trailofbits.com/contact/) directly.
If you have questions, need assistance, or just want to learn more, then join the #ethereum channel on the [Empire Hacking Slack](https://empireslacking.herokuapp.com/) or [contact us](https://www.trailofbits.com/contact/) directly.
10 changes: 5 additions & 5 deletions not-so-smart-contracts/substrate/arithmetic_overflow/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Arithmetic overflow
# Arithmetic Overflow

Arithmetic overflow in Substrate occurs when arithmetic operations are performed using primitive operations instead of specialized functions that check for overflow. When a Substrate node is compiled in `debug` mode, integer overflows will cause the program to panic. However, when the node is compiled in `release` mode (e.g. `cargo build --release`), Substrate will perform two's complement wrapping. A production-ready node will be compiled in `release` mode, which makes it vulnerable to arithmetic overflow.
In Substrate, arithmetic overflow occurs when arithmetic operations use primitive operations instead of specialized functions designed to check for overflow. When a Substrate node is compiled in `debug` mode, integer overflows will cause the program to panic. However, when the node is compiled in `release` mode (e.g. `cargo build --release`), Substrate performs two's complement wrapping. A production-ready node is compiled in `release` mode, which makes it susceptible to arithmetic overflow.

# Example

In the [`pallet-overflow`](https://github.com/crytic/building-secure-contracts/blob/master/not-so-smart-contracts/substrate/arithmetic_overflow/pallet-overflow.rs) pallet, notice that the `transfer` function sets `update_sender` and `update_to` using primitive arithmetic operations.
In the [`pallet-overflow`](https://github.com/crytic/building-secure-contracts/blob/master/not-so-smart-contracts/substrate/arithmetic_overflow/pallet-overflow.rs) pallet, note that the `transfer` function sets `update_sender` and `update_to` using primitive arithmetic operations.

```rust
/// Allow minting account to transfer a given balance to another account.
Expand All @@ -31,9 +31,9 @@ In the [`pallet-overflow`](https://github.com/crytic/building-secure-contracts/b
}
```

The sender of the extrinsic can exploit this vulnerability by causing `update_sender` to underflow, which artificially inflates their balance.
The sender of the extrinsic can exploit this vulnerability by causing `update_sender` to underflow, thus artificially inflating their balance.

**Note**: Aside from the stronger mitigations mentioned below, a check to make sure that `sender` has at least `amount` balance would have also prevented an underflow.
**Note**: In addition to the stronger mitigations mentioned below, checking to make sure that `sender` has at least `amount` balance would have also prevented an underflow.

# Mitigations

Expand Down
16 changes: 8 additions & 8 deletions not-so-smart-contracts/substrate/dont_panic/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Don't Panic!

Panics occur when the node enters a state that it cannot handle and stops the program / process instead of trying to proceed. Panics can occur for a large variety of reasons such as out-of-bounds array access, incorrect data validation, type conversions, and much more. A well-designed Substrate node must NEVER panic! If a node panics, it opens up the possibility for a denial-of-service (DoS) attack.
Panics occur when a node enters a state it cannot handle, which causes the program or process to stop instead of attempting to proceed. Various factors can lead to panics, such as out-of-bounds array access, improper data validation, type conversions, and many others. A well-designed Substrate node must NEVER panic! If a node panics, it becomes vulnerable to denial-of-service (DoS) attacks.

# Example

In the [`pallet-dont-panic`](https://github.com/crytic/building-secure-contracts/blob/master/not-so-smart-contracts/substrate/dont_panic/pallet-dont-panic.rs) pallet, the `find_important_value` dispatchable checks to see if `useful_amounts[0]` is greater than `1_000`. If so, it sets the `ImportantVal` `StorageValue` to the value held in `useful_amounts[0]`.
In the [`pallet-dont-panic`](https://github.com/crytic/building-secure-contracts/blob/master/not-so-smart-contracts/substrate/dont_panic/pallet-dont-panic.rs) pallet, the `find_important_value` dispatchable checks if `useful_amounts[0]` is greater than `1_000`. If it is, the `ImportantVal` `StorageValue` is set to the value contained in `useful_amounts[0]`.

```rust
/// Do some work
///
/// Parameters:
/// - `useful_amounts`: A vector of u64 values in which there is a important value.
/// - `useful_amounts`: A vector of u64 values which contains an important value.
///
/// Emits `FoundVal` event when successful.
#[pallet::weight(10_000)]
Expand All @@ -28,14 +28,14 @@ In the [`pallet-dont-panic`](https://github.com/crytic/building-secure-contracts
}
```

However, notice that there is no check before the array indexing to see whether the length of `useful_amounts` is greater than zero. Thus, if `useful_amounts` is empty, the indexing will cause an array out-of-bounds error which will make the node panic. Since the `find_important_value` function is callable by anyone, an attacker can set `useful_amounts` to an empty array and spam the network with malicious transactions to launch a DoS attack.
However, note that there is no check before the array indexing to verify whether the length of `useful_amounts` is greater than zero. Consequently, if `useful_amounts` is empty, the indexing will trigger an array out-of-bounds error, causing the node to panic. Since the `find_important_value` function can be called by anyone, an attacker could set `useful_amounts` to an empty array and spam the network with malicious transactions, launching a DoS attack.

# Mitigations

- Write non-throwing Rust code (e.g. prefer returning [`Result`](https://paritytech.github.io/substrate/master/frame_support/dispatch/result/enum.Result.html) types, use [`ensure!`](https://paritytech.github.io/substrate/master/frame_support/macro.ensure.html), etc.).
- Proper data validation of all input parameters is crucial to ensure that an unexpected panic does not occur.
- A thorough suite of unit tests should be implemented.
- Fuzz testing (e.g. using [`test-fuzz`](https://github.com/trailofbits/test-fuzz)) can aid in exploring more of the input space.
- Write non-throwing Rust code (e.g., prefer returning [`Result`](https://paritytech.github.io/substrate/master/frame_support/dispatch/result/enum.Result.html) types, use [`ensure!`](https://paritytech.github.io/substrate/master/frame_support/macro.ensure.html), etc.).
- Thoroughly validate all input parameters to avoid unexpected panics.
- Implement a comprehensive suite of unit tests.
- Perform fuzz testing (e.g., using [`test-fuzz`](https://github.com/trailofbits/test-fuzz)) to explore a wider range of inputs.

# References

Expand Down
18 changes: 9 additions & 9 deletions not-so-smart-contracts/substrate/origins/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Origins

The origin of a call tells a dispatchable function where the call has come from. Origins are a way to implement access controls in the system.
In the context of dispatchable functions, the origin of a call indicates where the call has come from. Origins serve as a means to implement access controls in a system.

There are three types of origins that can used in the runtime:
There are three types of origins that can be used in the runtime:

```rust
pub enum RawOrigin<AccountId> {
Expand All @@ -12,13 +12,13 @@ pub enum RawOrigin<AccountId> {
}
```

Outside of the out-of-box origins, custom origins can also be created that are catered to a specific runtime. The primary use case for custom origins is to configure privileged access to dispatch calls in the runtime, outside of `RawOrigin::Root`.
Aside from the default origins, custom origins tailored for specific runtimes can also be created. The primary purpose of custom origins is to configure privileged access for dispatch calls in the runtime, outside of `RawOrigin::Root`.

Using privileged origins, like `RawOrigin::Root` or custom origins, can lead to access control violations if not used correctly. It is a common error to use `ensure_signed` in place of `ensure_root` which would allow any user to bypass the access control placed by using `ensure_root`.
Using privileged origins, such as `RawOrigin::Root` or custom origins, can result in access control violations if not employed properly. A common mistake is using `ensure_signed` instead of `ensure_root`, which allows any user to bypass the access control imposed by `ensure_root`.

# Example

In the [`pallet-bad-origin`](https://github.com/crytic/building-secure-contracts/blob/master/not-so-smart-contracts/substrate/origins/pallet-bad-origin.rs) pallet, there is a `set_important_val` function that should be only callable by the `ForceOrigin` _custom_ origin type. This custom origin allows the pallet to specify that only a specific account can call `set_important_val`.
The [`pallet-bad-origin`](https://github.com/crytic/building-secure-contracts/blob/master/not-so-smart-contracts/substrate/origins/pallet-bad-origin.rs) pallet features a `set_important_val` function that should be callable only by the `ForceOrigin` _custom_ origin type. This custom origin enables the pallet to specify that only a specific account can call `set_important_val`.

```rust
#[pallet::call]
Expand All @@ -42,7 +42,7 @@ impl<T:Config> Pallet<T> {
}
```

However, the `set_important_val` is using `ensure_signed`; this allows any account to set `ImportantVal`. To allow only the `ForceOrigin` to call `set_important_val` the following change can be made:
However, since `set_important_val` is using `ensure_signed`, any account can set `ImportantVal`. To restrict `set_important_val` to `ForceOrigin` calls only, the following change can be made:

```rust
T::ForceOrigin::ensure_origin(origin.clone())?;
Expand All @@ -51,9 +51,9 @@ let sender = ensure_signed(origin)?;

# Mitigations

- Ensure that the correct access controls are placed on privileged functions.
- Develop user documentation on all risks associated with the system, including those associated with privileged users.
- A thorough suite of unit tests that validates access controls is crucial.
- Ensure that appropriate access controls are applied to privileged functions.
- Create user documentation that covers all risks associated with the system, including those related to privileged users.
- Develop a comprehensive suite of unit tests to verify access controls.

# References

Expand Down
66 changes: 33 additions & 33 deletions not-so-smart-contracts/substrate/randomness/README.md
Original file line number Diff line number Diff line change
@@ -1,59 +1,59 @@
# Bad Randomness

To use randomness in a Substrate pallet, all you need to do is require a source of randomness in the `Config` trait of a pallet. This source of Randomness must implement the [`Randomness`](https://paritytech.github.io/substrate/master/frame_support/traits/trait.Randomness.html) trait. The trait provides two methods for obtaining randomness.
To utilize randomness in a Substrate pallet, simply require a source of randomness in the `Config` trait of a pallet. This source of randomness must implement the [`Randomness`](https://paritytech.github.io/substrate/master/frame_support/traits/trait.Randomness.html) trait. The trait provides two methods for obtaining randomness:

1. `random_seed`: This function takes no arguments and returns back a random value. Calling this value multiple times in a block will result in the same value.
2. `random`: Takes in a byte-array (a.k.a "context-identifier") and returns a value that is as independent as possible from other contexts.
1. `random_seed`: This function takes no arguments and returns a random value. Calling this function multiple times in a block results in the same value.
2. `random`: Takes in a byte array (a.k.a "context-identifier") and returns a value that is as independent as possible from other contexts.

Substrate provides the [Randomness Collective Flip Pallet](https://docs.rs/pallet-randomness-collective-flip/latest/pallet_randomness_collective_flip/) and a Verifiable Random Function implementation in the [BABE pallet](https://paritytech.github.io/substrate/master/pallet_babe/index.html). Developers can also choose to build their own source of randomness.
Substrate offers the [Randomness Collective Flip Pallet](https://docs.rs/pallet-randomness-collective-flip/latest/pallet_randomness_collective_flip/) and a Verifiable Random Function implementation in the [BABE pallet](https://paritytech.github.io/substrate/master/pallet_babe/index.html). Developers can also opt to build their own source of randomness.

A bad source of randomness can lead to a variety of exploits such as the theft of funds or undefined system behavior.
A poor source of randomness can lead to various exploits, such as theft of funds or unpredictable system behavior.

# Example

The [`pallet-bad-lottery`](https://github.com/crytic/building-secure-contracts/blob/master/not-so-smart-contracts/substrate/randomness/pallet-bad-lottery.rs) pallet is a simplified "lottery" system that requires one to guess the next random number. If they guess correctly, they are the winner of the lottery.
The [`pallet-bad-lottery`](https://github.com/crytic/building-secure-contracts/blob/master/not-so-smart-contracts/substrate/randomness/pallet-bad-lottery.rs) pallet is a simplified "lottery" system that requires one to guess the next random number. If a user guesses correctly, they are declared the winner of the lottery.

```rust
#[pallet::call]
impl<T:Config> Pallet<T> {
/// Guess the random value
/// If you guess correctly, you become the winner
#[pallet::weight(10_000)]
pub fn guess(
origin: OriginFor<T>,
guess: T::Hash
) -> DispatchResultWithPostInfo {
let sender = ensure_signed(origin)?;
// Random value.
let nonce = Self::get_and_increment_nonce();
let (random_value, _) = T::MyRandomness::random(&nonce);
// Check if guess is correct
ensure!(guess == random_value, <Error<T>>::IncorrectGuess);
<Winner<T>>::put(&sender);
/// Guess the random value
/// If you guess correctly, you become the winner
#[pallet::weight(10_000)]
pub fn guess(
origin: OriginFor<T>,
guess: T::Hash
) -> DispatchResultWithPostInfo {
let sender = ensure_signed(origin)?;
// Random value
let nonce = Self::get_and_increment_nonce();
let (random_value, _) = T::MyRandomness::random(&nonce);
// Check if guess is correct
ensure!(guess == random_value, <Error<T>>::IncorrectGuess);
<Winner<T>>::put(&sender);

Self::deposit_event(Event::NewWinner(sender));
Self::deposit_event(Event::NewWinner(sender));

Ok(().into())
}
Ok(().into())
}
}

impl<T:Config> Pallet<T> {
/// Increment the nonce each time guess() is called
pub fn get_and_increment_nonce() -> Vec<u8> {
let nonce = Nonce::<T>::get();
Nonce::<T>::put(nonce.wrapping_add(1));
nonce.encode()
}
/// Increment the nonce each time guess() is called
pub fn get_and_increment_nonce() -> Vec<u8> {
let nonce = Nonce::<T>::get();
Nonce::<T>::put(nonce.wrapping_add(1));
nonce.encode()
}
}
```

Note that the quality of randomness provided to the `pallet-bad-lottery` pallet is related to the randomness source. If the randomness source is the "Randomness Collective Flip Pallet", this lottery system is insecure. This is because the collective flip pallet implements "low-influence randomness". This makes it vulnerable to a collusion attack where a small minority of participants can give the same random number contribution making it highly likely to have the seed be this random number (click [here](https://ethresear.ch/t/rng-exploitability-analysis-assuming-pure-randao-based-main-chain/1825/7) to learn more). Additionally, as mentioned in the Substrate documentation, "low-influence randomness can be useful when defending against relatively weak adversaries. Using this pallet as a randomness source is advisable primarily in low-security situations like testing."
Note that the quality of randomness provided to the `pallet-bad-lottery` pallet depends on the randomness source. If the randomness source is the "Randomness Collective Flip Pallet", this lottery system is insecure. This is because the collective flip pallet implements "low-influence randomness", making it vulnerable to a collusion attack where a small minority of participants can provide the same random number contribution, making it highly likely that the seed will be this random number. (To learn more, click [here](https://ethresear.ch/t/rng-exploitability-analysis-assuming-pure-randao-based-main-chain/1825/7)). Additionally, as mentioned in the Substrate documentation, "low-influence randomness can be useful when defending against relatively weak adversaries. Using this pallet as a randomness source is advisable primarily in low-security situations like testing."

# Mitigations

- Use the randomness implementation provided by the [BABE pallet](https://paritytech.github.io/substrate/master/pallet_babe/index.html). This pallet provides "production-grade randomness, and is used in Polkadot. **Selecting this randomness source dictates that your blockchain use Babe consensus.**"
- Defer from creating a custom source of randomness unless specifically necessary for the runtime being developed.
- Do not use `random_seed` as the method of choice for randomness unless specifically necessary for the runtime being developed.
- Use the randomness implementation provided by the [BABE pallet](https://paritytech.github.io/substrate/master/pallet_babe/index.html), which offers "production-grade randomness" and is used in Polkadot. **Selecting this randomness source mandates that your blockchain utilize Babe consensus.**
- Refrain from creating a custom source of randomness unless specifically necessary for the runtime being developed.
- Avoid using `random_seed` as the method of choice for randomness unless specifically necessary for the runtime being developed.

# References

Expand Down
Loading