Smart Contract Security Vulnerabilities

Smart contract security is an important domain for people in the web3 space because a single hack to your launched product can lose millions of dollars. It also has lucrative opportunities if you follow the space. This list of security vulnerabilities in smart contracts is an exhaustive directory of common solidity security vulnerabilities.

There are many cases where organizations have been hacked — The DAO Hack on June 2016($89 mil in losses), Qubit Nomad Finance on Jan 2022 ($80 mil in losses), Nomad Bridge Hack on August 2022($190 mil in losses) to name a few. In this post, we will talk about all the potential vulnerabilities that your smart contracts can have so that you can write more secure code.

Note: I learned a lot about these smart contract vulnerabilities from video demonstrations by Smart Contract Programmer. Check out his playlist if you're interested.

Reentrancy

What is a Reentrancy Attack?
A reentrancy attack involves two smart contracts — the victim contract and the attacker contract. The attacker’s smart contract repeatedly calls a function on the victim’s smart contract to exploit the victim’s contract into giving away more ether. Here is an illustration of how it works:

Contract A is the victim contract; Contract B is the attacker contract

Understanding Contract A:
Contract A holds ten ether and stores the balance of contract B too. It has one function, withdraw(), which essentially checks if the contract trying to withdraw eth has a balance greater than 0. If it does, the ether mapped to the balance of contract B goes back to contract B and marks the account’s balance as 0.

Understanding Contract B:
Contract B holds one ether and has two functions — the fallback() function and the attack() function, both of which have one method: to call the withdraw function on contract A.

Attack Process:
When contract B’s attack function is triggered, contract B calls contract A’s withdraw function.

  1. The withdraw function verifies that B has more than 0 ether (1 ether in this case)
  2. Now contract A sends back the ether to contract B, and upon doing so, the fallback function in Contract B is triggered.
  3. And since the fallback function contains the withdraw method, it withdraws ether — again.
  4. Since ether has been sent back to B, contract B triggers the fallback function again.
  5. This happens until contract A runs out of ether.

Preventing Reentrancy:

  1. Update balances before making external function calls.
  2. Use safe modifiers that can lock the contract

Front Running

What is front running?

Front-running attacks are when an attacker uses their position or advanced knowledge to gain an advantage in a transaction. In the context of blockchain, front-running attacks can occur when an attacker can execute a trade before other traders by using their position as a miner or exchange to prioritize their own trades. First, check this image to understand the basics of transaction propagation on the blockchain.

A is Alice, B is Bob, and M is miner. For Alice or Bob to send a transaction, it has to go through the network of miners; every line of the miner network in the image is a transaction process. After the last miner receives the transaction, it propagates into the Tx pool or the transaction pool. For the transactions to be added to the blockchain, the miner responsible for making the next block can add them by taking them from the transaction pool.

So how does the Front Running attack work?

  1. Let’s say Alice sends a transaction, and it eventually reaches the transaction pool. If Bob(the attacker) is aware of this, Bob can send a transaction with higher gas fees meaning that it would reach the transaction pool faster.
  2. If a miner takes in both these transactions on the blockchain, Bob’s transaction would be given priority and executed before Alice’s transaction since Bob paid a higher gas fee. Because of this, Bob becomes a successful attacker.

Storage Hacks

How does Storage work in solidity?

In the picture below, we can see that there are 4 memory slots storing the 4 variables in each slot. Each memory slot can hold up to 32 bytes, and uint256 is 256 bits which is 32 bytes(256 bits / 8 bits per byte= 32 bytes). uint128 is half of that, which is 16 bytes, and the address type is 32 bytes. Now let’s rearrange the following variables. In this pic, uint256 a takes one slot, uint128 b takes one slot(we will see why soon), address _addr takes one slot, and finally, uint128 c takes one slot.

After rearranging, we can see that uint128 slots b and c can go together into one slot by just keeping them next to each other. With this, we can easily save a memory slot and hence some gas.

The catch is, even if these variables were private, the data would still be accessible, let's see how.

How to access these variables?

  1. Even though these variables were private, we can use the web3js library to get the storage details of a smart contract. For example:
  2. await web3.eth.getStorageAt(contract.address, 1)
  3. This statement will get the variable stored at index 1 in a smart contract’s storage.

The delegatecall Attack

Essentially, delegatecall allows the user to call a function on another contract and specify what function you want to call on the other contract, which may change the state variables of the original contract that calls the new contract.

2 features of a delegatecall attack:

  1. During a delegatecall attack, the storage layout of the attacking contract and the victim contract are the same.
  2. The delegatecall maintains the context and can change variables of the original contract(victim contract).

The tx.origin phishing attack

What is tx.origin?

The tx.origin refers to an external account that started the first transaction irrespective of how many ever other contracts and functions have been invoked. Let’s take an example with our friends Alice and Bob. Alice makes a contract on the blockchain and deploys it. She is the one who initiated the contract, so she is the tx.origin of the contract. Let’s say there is a function called foo() on the contract. This function has an important statement that involves the tx.origin command.

Alice → made contract, tx.origin = Alice → calls function foo() and executes

Bob → is not tx.origin → calls function foo() on behalf of the tx.origin = Alice

Because Bob can call the function and do things on behalf of Alice even though he shouldn't, making him a hacker who is phishing Alice into performing her tasks.

Check out this link to understand this better with the actual smart contract code in the demonstration.

Arithmetic overflow and underflow Attacks

What are overflow and underflow errors?

In computer science, arithmetic overflow refers to the phenomenon in which the number being held in a variable is too big for the variable to handle. And underflow is the opposite, wherein the number is too small for the variable to store and handle.

Let’s look at this in the solidity context:
uint256 stands for unsigned integers that occupy space of 256 bits

uint256 can store only non-negative numbers from 0 till 2**256 -1

Since the current maximum possible number that can be stored is 2**256 -1 for a uint256, if we add 1 to it, making it 2**256, the variable overflows, causing it to reset to 0. Similarly, if the variable is set to 0 and an arithmetic function tries to modify the variable by subtracting 1, the variable underflows and becomes 2**256.

Note: The solidity compiler doesn’t care about underflow and overflow and hence will not throw any errors; however, there is a workaround.

How to prevent this attack in Solidity?

  1. Using a library called SafeMath.sol is the best preventive measure against underflows and overflows. It throws an error any time there is an overflow or underflow.

Selfdestruct

You can forcefully send ether from one contract to an address using the selfdestruct command in solidity. An attacker contract can steal all the ether and call the selfdestruct function before calling a fallback function within the victim contract.

The code would look something like this:

selfdestruct(address) // This is the address you want to send funds to

Denial of Service Attack

A denial-of-service (DoS) attack in the context of smart contracts is an attack in which the attacker deliberately executes a set of actions that cause the smart contract to become unresponsive or unavailable to other users. This can be done by triggering a bug in the contract that consumes all of its available resources, such as gas or memory, or by deliberately sending many requests to the contract that exceed its processing capabilities.

DoS attacks on smart contracts can be particularly damaging because they can disrupt the functionality of decentralized applications (dApps) that rely on those contracts for their operation. Let’s take a look at the following example:

(bool sent, ) = king.call{value: balance}("");
require(sent, "Failed to send Ether");

If an attacker wanted to exploit this contract, he would see that in the line which sends ether to some other address, there is a (“”). The (“”) means that once ether is sent, it would trigger the fallback function of the contract that the ether is sent to. Once that fallback function is triggered, the attacker contract can write malicious code to make it return back to this state and hence cause the DoS attack by denying the contract to be called right after.

How to prevent it?

  1. Make a mapping of the user's balances and use addition and subtraction to make changes to the contract.

Thanks for reading this far! If you enjoyed this blog post, do clap, share and follow for more, and you can send me crypto donations on the mainnet at this address: 0x04b24656E4B114e4eF83f40a1161d1804e684D89

--

--

I am a student in a university in India, I talk about web3 tech and blockchain because I am a web3 enthusiast!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Rahul Pujari

I am a student in a university in India, I talk about web3 tech and blockchain because I am a web3 enthusiast!