LogoLogo
  • WELCOME TO ALEPH ZERO
  • EXPLORE
    • About Aleph Zero
    • AlephBFT Consensus
    • The Economy of Aleph Zero
    • Where to Buy AZERO
    • Decentralized Governance on Aleph Zero
    • Ecosystem
    • Aleph Zero Foundation Treasury Management
    • Community
    • Glossary
    • Audit & Research Papers
  • USE
    • Wallets
    • Explorer
    • Ledger
    • Telegram Notifications
    • Aleph Zero Signer
      • General introduction
      • What does Signer do?
      • What are Sub-accounts and Sub-account paths?
      • Why is it critical to store your Secret Phrase in a safe place?
      • How to forget and restore accounts?
      • What are Networks?
      • What are Trusted apps?
    • Dashboard
      • Dashboard basics
      • Overview
    • Stake
      • Staking Basics
      • Staking Menu Overview
      • How to Start Staking with the Aleph Zero Dashboard
      • How to Start Staking With the Developer Wallet
      • How to start staking using Ledger hardware wallet
      • How to Change Nominations
      • How to Stop Staking
      • Staking Rewards
      • Validators
      • Commission and Foundation Nodes
      • Proxy Accounts
    • Validate
      • Validating Overview
      • Hardware requirements
      • Running an Aleph Node on Testnet
        • Downloading and running the node
        • Verifying your setup
        • Customizing your setup
        • Building and running from source [advanced]
          • Building from source
          • Set environment variables
          • Download DB snapshot
          • Running the binary
        • Appendix: Ports, addresses, validators, and archivists
      • Running an Aleph Node on Mainnet
        • Running the node
        • Building and running from source [advanced]
      • Setting your identity
      • Making the node validate
      • Securing your validator
      • Troubleshooting
      • Elections and Rewards Math
      • Testnet Validator Airdrop
      • Foundation Nomination Program
    • Using the EVM-layer
    • Governance
      • Token
      • Multisig Accounts
  • BUILD
    • Aleph Zero smart contracts basics
      • Setting up a Testnet account
      • Installing required tools
      • Creating your first contract
      • Deploying your contract to Aleph Zero Testnet
      • Extending your contract
    • Cross contract calls
      • Using references
      • Using dynamic calls
    • Migrating from Solidity
    • Writing e2e tests with ink-wrapper
    • Aleph Zero Signer integration
    • Front-end app: smart contract interaction
    • Security Course by Kudelski Security
      • ink! Developers Security Guideline
      • Lesson 1 - Getting started with ink!
      • Lesson 2 - Threat Assessment
      • Lesson 3 - Integer Overflow
      • Lesson 4 - Signed-integer
      • Lesson 5 - Role-Based Access Control
      • Lesson 6 - Address Validation
      • Lesson 7 - Smart Contract Control
    • Development on EVM-layer
  • PROTOCOL DETAILS
    • Shielder
      • Overview
      • Design against Bad Actors
      • Preliminaries - ZK-relations
      • Notes and Accounts
      • ZK-ID and Registrars
      • Anonymity Revokers
      • PoW Anonymity Revoking
      • Relayers
      • Deterministic Secret Management
      • SNARK-friendly Symmetric Encryption
      • SNARK-friendly Asymmetric Encryption
      • Cryptography
      • Token shortlist
      • User Wallet
      • Versioning
      • PoC
      • Version 0.1.0
      • Version 0.2.0
    • Common DEX
      • Common Whitepaper - Differences
      • Dutch Auctions
  • FAQ
  • Tutorials
    • Withdrawing coins from exchanges
      • How to withdraw your AZERO coins from KuCoin
      • How to withdraw your AZERO coins from MEXC Global
      • How to withdraw your AZERO coins from HTX
  • Setting up or restoring a wallet
    • How to set up or recover your AZERO account using Aleph Zero Signer
    • How to set up or recover your AZERO account using the official mainnet web wallet
    • How to set up or recover your AZERO account using Nova Wallet
    • How to set up or recover your AZERO account using SubWallet
    • How to set up or recover your AZERO account using Talisman
  • Staking
    • How to stake via a direct nomination using the Aleph Zero Dashboard
    • How to stake via a nomination pool using the Aleph Zero Dashboard
    • How to destroy a nomination pool via the Aleph Zero Dashboard
Powered by GitBook
On this page
  • Introduction
  • Prerequisites
  • Objectives and Outcomes
  • Exercise
  • Vulnerable Smart contract
  • Simulated Attack
  • Secure Solution
  • Question

Was this helpful?

  1. BUILD
  2. Security Course by Kudelski Security

Lesson 3 - Integer Overflow

PreviousLesson 2 - Threat AssessmentNextLesson 4 - Signed-integer

Last updated 1 year ago

Was this helpful?

Introduction

In this lesson you will learn how improperly handled integer overflows (or underflows) can yield vulnerabilities in your smart contract. Integer typed variables in ink! can overflow or underflow. This means that if they are assigned values outside of their bounds, they will wrap around. If this is not addressed carefully, this may allow an attacker to set values outside the bounds of what is expected by the smart contract author.

Prerequisites

To be able to understand and complete this lesson, we believe that the minimum requirement should be the completion of . If you desire to be even more prepared please have a look at the official website of .

Objectives and Outcomes

At the end of this lesson, you will be identify integer overflow/underflow vulnerabilities in smart contract code. You will also understand a few ways to prevent these attacks beyond explicit checks. These include: appropriate compiler configuration, safe math, and wrapping methods.

Exercise

Vulnerable Smart contract

Bob develops a smart contract for as a simple token bank. The bank can be initialized with a specific quantity of tokens. Alice can then withdraw tokens into her personal account. His smart contract code can be found below.

#![cfg_attr(not(feature = "std"), no_std)]


#[ink::contract]
mod bank {
    use ink::storage::Mapping;
    
    #[ink(storage)]
    #[derive(Default)]
    pub struct Bank {
        bank: u8,
        balances: Mapping<AccountId, u8>,
    }

    impl Bank {
        /// Constructor that initializes the supply of the token bank
        #[ink(constructor)]
        pub fn new(supply: u8) -> Self {
            let balances = Mapping::default();
            Self{
                balances,
                bank: supply,
            }
        
        }

        /// report the totaly supply of token
        #[ink(message)]
        pub fn bank(&self) -> u8 {
            self.bank
        }
  
        /// Simply returns the current balance of token in balances
        #[ink(message)]
        pub fn balance_of(&self, account: AccountId) -> u8 {
            match self.balances.get(&account) {
                Some(value) => value,
                None => 0,
            }
        }

        #[ink(message)]
        pub fn withdraw(&mut self, amount: u8) {
            let sender = self.env().caller();
            let sender_balance = self.balance_of(sender);
            if sender_balance + amount < sender_balance {
                return;
            }
            self.balances.insert(sender, &(sender_balance + amount));
            self.bank -= amount;
        }

    }
}

==- Hint First, identify lines where mathematical operations occur. Then check for a possible overflow or underflow. ==- Answer The underflow vulnerability is on line 50.

self.bank -= amount;

==-

Simulated Attack

Did you succeed? Congratulations! If not, see the exploit below.

==- Reveal Exploit This test demonstrates an exploit that allows Alice to withdraw more tokens than Bob has made available.

#[cfg(test)]
mod tests {
    use crate::bank::Bank;
    use ink::env::{test, DefaultEnvironment};

    #[ink::test]
    fn tokens_for_free() {
        let accounts = test::default_accounts::<DefaultEnvironment>();
        test::set_caller::<DefaultEnvironment>(accounts.alice);
        let mut bank = Bank::new(10);

        // Initially, the bank has 10 tokens and Alice has 0 tokens
        assert_eq!(bank.balance_of(accounts.alice), 0);
        assert_eq!(bank.bank(), 10);

        // Alice attempts to withdraw 111 tokens, which is more than the bank has
        bank.withdraw(111);

        // Now, the bank has 155 tokens and Alice has 111 tokens due to underflow
        assert_eq!(bank.balance_of(accounts.alice), 111);
        assert_eq!(bank.bank(), 155);
    }
}

==-

Secure Solution

Luckily, there exist a secure way to implement the smart contract! Before revealing the solution, should take some time and try to secure the smart contract. Any success? Let’s reveal two possible solutions. ==- Solution 1 How can we fix the code so that we catch the underflow? We can use safe math that catches underflows.

        #[ink(message)]
        pub fn withdraw(&mut self, amount: u8) {
            let sender = self.env().caller();
            let sender_balance = self.balance_of(sender);
            if sender_balance + amount < sender_balance {
                return;
            }

            let check = self.bank.checked_sub(amount);
            assert_eq!(check, None, "Underflow");

            self.balances.insert(sender, &(sender_balance + amount));
            self.bank -= amount;
        }

Keep in mind, this costs additional computations throughout the code. ==-

If you want, you can verify that the solution is secure by replaying the attack described above.

Question

Unfortunately, Bob did not follow the and this has for consequences that his contract is vulnerable. Do you see where the security vulnerability occurs?

Can you think about a way to exploit this vulnerability? You may download the contract here, deploy it on Aleph Zero , and try to attack it.

==- Solution 2 Set the compiler flags debug-assertion and overflow-checks to true. These can be set in for profile.release. This will ensure there will be a panic during runtime for overflows and underflows.

If you are up to the challenge, see if you know the answer to this question: ==- Reveal Question True or false: Exceeding the maximum value for an integer type will always panic. ==- Answers False. By default, debug builds will panic on overflow while release builds will not. This can be modified in Cargo.profile or by providing appropriate compiler flags. Even with overflow checking on, panics can be averted with saturating_, wrapping_, checked_, and overflowing_ methods. See in the the rust docs. ==-

Lesson 1 - Getting Started
ink!
security guidelines
testnet
Cargo.toml
integer overflow