Skip to content

List of Security Vulnerabilities

endorphin edited this page Apr 15, 2019 · 7 revisions

Table of Contents

  1. Integer Arithmetic
  2. Floating Point Arithmetic
  3. Reentrancy
  4. Access Control
    1. Default Visibility
    2. Authentication With tx.origin
    3. Signature Verification
    4. Unprotected Functions
  5. Code Injection via delegatecall
  6. Signature Replay Attacks
  7. Unchecked External Calls
    1. Insufficient Gas Attacks
  8. DOS
    1. Unexpected Revert
    2. Block Gas Limit
    3. External Calls without Gas Stipends
    4. Offline Owner
  9. Entropy Illusion
  10. Privacy Illusion
  11. Miner Attacks
    1. Transaction Ordering
    2. Timestamp Manipulation
  12. Unexpected Ether
  13. External Contract Referencing
  14. Uninitialized Storage Pointers
  15. Writes to Arbitrary Storage Locations
  16. Incorrect Interface
  17. Arbitrary Jumps with Function Variables
  18. Variable Shadowing
  19. Assert Violation
  20. Dirty Higher Order Bits
  21. Complex Modifiers
  22. Outdated Compiler
  23. Use of Deprecated Solidity Functions
  24. Experimental Language Features
  25. Frontend (Off Chain) Attacks
    1. Short Address Attack
  26. Historic Attacks
    1. Constructor Names
    2. Call Depth Attack
    3. Constantinople Reentrancy
    4. Solidity Abi Encoder v2 Bug
  27. References

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.

Integer Arithmetic

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.

Floating Point Arithmetic

Fixed point numbers are not yet fully supported by solidity. User implementations may contain errors.

Reentrancy

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.

Access Control

There are a number of common mistakes relating to access control.

Default Visibility

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.

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.

Signature Verification

If a smart contract system implements its own signature verification scheme, it may contain vulnerabilities.

Unprotected Functions

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.

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.

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 call and send return false. Programmers may mistakenly expect call and send to revert, and fail to check for their 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.

DOS

This is a broad category of attacks where an attacker may render a contract inoperable, temporarily or permanently.

Unexpected Revert

An attacker may be able to exploit the fact that transfer (alternatively 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.

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:

Offline Owner

Some systems may become inoperable if the owner or some other authority goes offline / loses their private key. This should be avoided.

Entropy Illusion

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.

Privacy Illusion

Developers may forget that everything on chain is public, and store private data in the open.

Miner Attacks

Transaction Ordering

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.

Timestamp Manipulation

Many contracts use block timestamps for various purposes. Keep in mind that miners can slightly adjust them to their advantage.

Unexpected Ether

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.

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 storage or 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.

Incorrect Interface

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.

Variable Shadowing

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.

Assert Violation

Properly functioning code should never violate an assert statement. This can occur if developers mistakenly use assert instead of require.

Dirty Higher Order Bits

If 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.

Complex Modifiers

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.

Outdated Compiler

Never use a compiler version that is significantly out of date. Implementations should specify an exact compiler version with pragma.

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.

Historic Attacks

Constructor Names

Fixed in solidity v0.4.22

Call Depth Attack

Fixed in EIP 150

Constantinople Reentrancy

Solidity Abi Encoder v2 Bug

Fixed in solidity v0.5.7

References

You can’t perform that action at this time.