List of Security Vulnerabilities
Table of Contents
- Integer Arithmetic
- Floating Point Arithmetic
- Access Control
- Code Injection via delegatecall
- Signature Replay Attacks
- Unchecked External Calls
- Entropy Illusion
- Privacy Illusion
- Miner Attacks
- Unexpected Ether
- External Contract Referencing
- Uninitialized Storage Pointers
- Writes to Arbitrary Storage Locations
- Incorrect Interface
- Arbitrary Jumps with Function Variables
- Variable Shadowing
- Assert Violation
- Dirty Higher Order Bits
- Complex Modifiers
- Outdated Compiler
- Use of Deprecated Solidity Functions
- Experimental Language Features
- Frontend (Off Chain) Attacks
- Historic Attacks
This page contains a comprehensive list of common smart contract security vulnerabilities, compiled from various sources. We use it as our reference list for security audits. In this page we only include basic information. Follow the links given in each section for more information.
Neither the EVM nor Solidity provide builtin error reporting for arithmetic overflow/underflow. Consequently, applications need to check for these cases themselves. Furthermore, one cannot make the (seemingly reasonable) assumption that
x != -x, because of this case.
- Consensys Best Practices: Integer Overflow and Underflow
- DASP: Arithmetic Issues
- Sigmaprime: Arithmetic Over/Under Flows
- Solidity Documentation: Two's Complement / Underflows / Overflows
- SWC Registry: Integer Overflow and Underflow
- Trail of Bits: Integer Overflow
Floating Point Arithmetic
Fixed point numbers are not yet fully supported by solidity. User implementations may contain errors.
When a contract calls an external function, that external function may itself call the calling function. This can have unexpected effects. To prevent this sort of attack, a contract can implement a lock in storage that prevents re-entrant calls.
- Consensys Best Practices: Reentrancy
- DASP: Reentrancy
- Sigmaprime: Reentrancy
- Solidity Documentation: Reentrancy
- SWC Registry: Reentrancy
- Trail of Bits: Reentrancy
There are a number of common mistakes relating to access control.
The default visibility for Solidity functions is
public. Developers may forget to specify the visibility for a function that is intended to be private, leading to possible vulnerabilities.
- SWC Registry: State Variable Default Visibility
- SWC Registry: Function Default Visibility
- Sigmaprime: Default Visibilities
Authentication With tx.origin
Solidity has a global variable,
tx.origin, which returns the address of the account that originally sent the call. Using this variable for authentication leaves the contract vulnerable to a phishing-like attack.
- Sigmaprime: tx.origin Authentication
- SWC Registry: Authentication Through tx.origin
- Solidity Documentation: tx.origin
If a smart contract system implements its own signature verification scheme, it may contain vulnerabilities.
Contracts may implement other (more complex) forms of access control themselves. Errors in this code can lead to functions that should be private being accessible by an attacker. In particular, one should always check that any
selfdestruct calls and ether withdrawals can only be made by those who are intented to be able to.
- SWC Registry: Unprotected SELFDESTRUCT
- SWC Registry: Unprotected Ether Withdrawal
- Trail of Bits: Unprotected Functions
Code Injection via delegatecall
Solidity allows calling external contracts via the
DELEGATECALL opcode, which executes the code of an external contract in the persistent context of the present contract. Certain contracts perform
DELEGATECALL calls using user-provided call data, which can effectively give full control to an attacker.
Signature Replay Attacks
If a smart contract system performs any sort of signature verification, it may be vulnerable to signature replay attacks. (Keep in mind that any signature sent to a contract via calldata will be publicly available.) Keeping track of processed signatures in storage is a simple way to prevent such attacks. Furthermore, in some cases, signatures may be malleable, i.e. an attacker may be able to modify them (so that they may be replayed) without destroying their validity.
- SWC Registry: Missing Protection Against Signature Replay Attacks
- SWC Registry: Signature Malleability
Unchecked External Calls
In Solidity, there are multiple ways to call an external contract and send ether. The function
transfer reverts if the transfer fails. However, the functions
send return false. Programmers may mistakenly expect
send to revert, and fail to check for their return value.
- DASP: Unchecked Low Level Calls
- Sigmaprime: Unchecked CALL Return Values
- Trail of Bits: Unchecked External Calls
- SWC Registry: Unchecked Call Return Value
Insufficient Gas Attacks
If a function makes a call to an external subroutine with
call, an attacker may be able to cause the function to only partially execute by sending a (precise) insufficient amount of gas.
This is a broad category of attacks where an attacker may render a contract inoperable, temporarily or permanently.
An attacker may be able to exploit the fact that
require(addr.send(amount))) reverts on failure to prevent a function from ever completing execution.
Block Gas Limit
In cases where the users of a system can manipulate how much computation (gas) is necessary for the execution of some function, it may be possible to DOS the system by causing the required gas to exceed the block gas limit. This is often the case in systems that loop over an array or mapping that can be enlarged by users at little cost.
- Consensys Best Practices: DOS with Block Gas Limit
- SWC Registry: DOS with Block Gas Limit
- Solidity Documentation: Gas Limit and Loops
External Calls without Gas Stipends
In some cases, developers may want to make a transfer and continue execution regardless of the result. One way to achieve this is with
call.value(v)(), however this may allow the recipient to consume all the gas of the calling function, preventing execution from continuing. See example 1 here:
Some systems may become inoperable if the owner or some other authority goes offline / loses their private key. This should be avoided.
The EVM does not have support for uncertainty/random number generation. Contracts may try to simulate uncertainty in a way that is in fact predictable and exploitable.
- DASP: Bad Randomness
- Sigmaprime: Entropy Illusion
- Trail of Bits: Bad Randomness
- SWC Registry: Weak Sources of Randomness from Chain Attributes
- Solidity Documentation: Private Information and Randomness
Developers may forget that everything on chain is public, and store private data in the open.
Miners can see transactions for a short time before they get included in a block, and exploit this information. They can also alter the order of transactions within a block. Users also have some influence over this process by setting gas prices. This can often pose a security risk, especially in systems where users are bidding or otherwise competing for something.
- DASP: Front-Running
- Sigmaprime: Race Conditions / Front-Running
- Consensys Best Practices: Front-Running
- Trail of Bits: Race Conditions
- SWC Registry: Transaction Order Dependence
Many contracts use block timestamps for various purposes. Keep in mind that miners can slightly adjust them to their advantage.
- DASP: Time Manipulation
- Sigmaprime: Block Timestamp Manipulation
- Consensys Best Practices: Timestamp Dependence
- SWC Registry: Timestamp Dependence
Certain contracts behave erroneously when their account contains ether. It is always possible to forcibly send ether to a contract (without triggering its fallback function), using
selfdestruct, or by mining to the account.
- Sigmaprime: Unexpected Ether
- Consensys Best Practices: Forcibly Sending Ether
- Trail of Bits: Forced Ether Reception
- Solidity Documentation: Sending and Receiving Ether
External Contract Referencing
When a contract delegates some of its functionality to an external contract whose address is either inaccessible or subject to change, a benign implementation may be swapped out for a malicious one.
Uninitialized Storage Pointers
Local variables in solidity functions default to
memory depending on their type. Uninitialized local storage variables can point to unexpected storage locations in the contract, leading to intentional or unintentional vulnerabilities.
Writes to Arbitrary Storage Locations
In general, writes to arbitrary storage locations should be avoided. This can occur, for example, if a storage array is written to at an index specified by a user.
Typos or mistakes in the type signature of a function can lead to the fallback function being called instead.
Arbitrary Jumps with Function Variables
If a contract uses function variables, an attacker may be able to manipulate a function variable to point to an unexpected location.
If multiple variables with the same name are declared in different scopes, there may be unintended effects. This is easy to miss in the case where a contract inherits from a contract implemented in a separate file.
- Trail of Bits: Variable Shadowing
- SWC Registry: Incorrect Inheritance Order
- SWC Registry: Shadowing State Variables
Properly functioning code should never violate an
assert statement. This can occur if developers mistakenly use
assert instead of
- SWC Registry: Assert Violation
- SWC Registry: Requirement Violation
- Solidity Documentation: Error Handling
Dirty Higher Order Bits
keccak256(msg.data) or some similar hash functionality is used (for example to log past calls), be aware that functions of types that don't occupy 32 bytes may be called with identical arguments but different hashes.
Use modifiers only for input validation with
require. Modifiers should not contain any substantive logic, because that logic will be executed before any input validation done at the start of function bodies.
Never use a compiler version that is significantly out of date. Implementations should specify an exact compiler version with
Use of Deprecated Solidity Functions
Avoid the use of deprecated solidity functions.
Experimental Language Features
Avoid experimental language features, as these are not properly tested and have contained vulnerabilities in the past.
Frontend (Off Chain) Attacks
These are possible vulnerabilities in frontends to ethereum contracts, not vulnerabilities in the contracts themselves. (Possibly out of scope.)
Short Address Attack
Frontends should validate any input used to make transactions on chain.
Fixed in solidity
- Sigmaprime: Constructors with Care
- Trail of Bits: Wrong Constructor Name
- SWC Registry: Incorrect Constructor Name
Call Depth Attack
Fixed in EIP 150
Solidity Abi Encoder v2 Bug
Fixed in solidity