Exploring Smart Contract Vulnerabilities through Ethernaut (OpenZeppelin) — Part 1

p6rkdoye0n
Park DoYeon
Published in
8 min readJun 9, 2024

The author strictly opposes black hat activities and solely advocates for white hat approaches. This article is intended for those who hope to engage in bug hunting on bug bounty platforms such as Immunefi or HackerOne from an offensive security perspective, or aspire to become smart contract auditors. Please note that any damages resulting from the exploitation of the mentioned vulnerabilities are the sole responsibility of the perpetrator, both legally and in terms of compensation.

Summary

In this article, I aim to introduce the major vulnerabilities that can occur in smart contracts, as discovered through completing the Ethernaut wargame created by OpenZeppelin. Please note that this article is not a write-up of the Ethernaut challenges but an academic exploration of security vulnerabilities in smart contracts.

Background

Security in blockchain is a critical issue. Once a transaction is confirmed and added to the network as a block, it cannot be deleted. This is why numerous smart contracts, even those that have already been audited, are continuously listed on bug bounty platforms like Immunefi and HackerOne for ongoing security improvements.

Immunefi Explore Bounties

I developed an interest in smart contract bug hunting and decided to study security vulnerabilities that can occur in smart contracts. To facilitate my learning, I worked through the challenges on Ethernaut, a wargame site created by OpenZeppelin.

Even after completing all the Ethernaut challenges, I continued to solve them repeatedly to deepen my understanding of various Solidity frameworks. Furthermore, I created a GitHub repository containing write-up code for all the challenges. Those who need references for their solutions may find it helpful to visit the repository.

Github Repository

As those who have researched security vulnerabilities in smart contracts may know, most real-world attack vectors stem from logical bugs. However, these types of vulnerabilities can manifest in vastly different ways depending on the specific smart contract.

Therefore, in this article, I aim to introduce the major attack vectors that exclude the logical bugs I encountered while solving the Ethernaut challenges.

Attack Vector

  1. Block Timestamp Manipulation
  2. Integer Overflow / Underflow
  3. Denial-of-Service (DoS)
  4. Reentrancy Attack
  5. Storage Collision
  6. Insufficient Validation

1. Block Timestamp Manipulation

Block Timestamp Manipulation is a vulnerability that occurs when the logic of a smart contract relies on the block’s timestamp.

In blockchain, each block has a timestamp indicating when it was mined. This timestamp is crucial for determining the order and execution of transactions, ensuring that the smart contract functions correctly. Block Timestamp Manipulation refers to altering the block’s timestamp to gain an advantage or exploit a vulnerability in the smart contract.

Example of Vulnerable Code

Below is a smart contract code that contains a Block Timestamp Manipulation attack vector:

The spin function within the Roulette contract operates as follows:

  • The spin function checks if it has received an amount equivalent to 1 ether.
  • It generates a pseudo-random number using the modulus operation on block.timestamp.
  • If the resulting value is zero, it sends an amount equal to twice the submitted value to the caller.

However, this design is vulnerable to Block Timestamp Manipulation. If an attacker manipulates the block.timestamp, they can always win in the spin function.

Below is an example of the exploit code leveraging the Block Timestamp Manipulation attack vector:

Attack Transaction

When executing the exploit code, despite setting the pseudo-random number arbitrarily, the value of block.timestamp can be manipulated, allowing for continuous wins.

Attack Result

Real-World Case

Lottery SmartBillions Exploit (2017): On October 4, 2017, SmartBillions hosted a hackathon to eliminate security risks in their smart contract before their ICO began. During this event, an attacker used the Block Timestamp Manipulation attack vector on the lottery contract, draining 400 ETH (worth $120,000).

Mitigation

  • Avoiding Block Timestamps: When writing smart contracts, avoid using the value of block.timestamp by default.
  • Using External Oracle: If randomness is required, it should not be generated within the smart contract. Instead, generate it externally and use an oracle to fetch the data.
  • Following the 15-Second Rule: “The Yellow Paper” suggests that each block.timestamp value must be greater than the parent block's block.timestamp. Ethereum protocols like Geth and Parity reject blocks with timestamps more than 15 seconds into the future.
reference: Consensys Timestamp Dependence docs (https://consensys.github.io/smart-contract-best-practices/development-recommendations/solidity-specific/timestamp-dependence/)

If the time scale of an event that changes with time can vary within about 15 seconds and still maintain integrity, then using block.timestamp can be considered safe.

2. Integer Overflow / Underflow

Integer Overflow / Underflow vulnerabilities occur when arithmetic operations attempt to create values that exceed the range that can be stored within the allocated bit-width. In Solidity, arithmetic is commonly performed using unsigned integer types (e.g., uint256, uint160, uint). If, during an arithmetic operation, the result turns into a negative value, it wraps around to a large value suitable for the unsigned integer type (Underflow). Conversely, if the value exceeds the maximum value that can be represented by the bit-width, it wraps around to zero and continues from there, resulting in a small value (Overflow).

Example of Vulnerable Code

Below is a smart contract code that contains an Integer Overflow / Underflow attack vector:

The Bank contract contains the following three functions:

  • deposit: Records the value sent to the balances mapping.
  • withdraw: Checks if the specified amount exists in the balances mapping for the user. If it does, it decreases the balance by the requested amount and transfers the specified amount to the user.
  • balanceOf: Checks the balance of the address provided as a parameter.

However, the withdraw function contains an Integer Underflow attack vector. If no deposit is made and 1 is passed as a parameter when calling the withdraw function, an Integer Underflow will occur.

Below is the exploit code that triggers the Integer Underflow attack vector:

If this code is executed, an underflow will occur, changing the mapping value to the maximum value of uint256, which is 115792089237316195423570985008687907853269984665640564039457584007913129639935 ((2²⁵⁶) — 1).

Exploit Result

Real-World Case

BeautyChain (BEC) Token Attack (2018): Attackers exploited an Overflow vulnerability in the batchTransfer function, causing abnormal fluctuations in the BEC token. The hacker drained a large amount of BEC tokens, leading to a sharp market price drop.

reference: https://medium.com/secbit-media/a-disastrous-vulnerability-found-in-smart-contracts-of-beautychain-bec-dbf24ddbc30e

Mitigation

  • Using Solidity version 0.8.0 or higher: Starting from version 0.8.0, Solidity’s compiler is designed to automatically check for integer overflow and underflow. Therefore, using code like the above in version 0.8.0 or higher will not trigger these vulnerabilities.
  • Using SafeMath Library: This was the method used in Solidity versions below 0.8.0. All arithmetic operations are performed using the SafeMath library created by OpenZeppelin or a custom library that checks for overflow and underflow.

In the real world, most smart contracts indeed use Solidity version 0.8.0 or higher, which leads many bug hunters to overlook these vulnerabilities. However, this is a mistake.

There are still several cases where integer overflow or underflow can be triggered, even in version 0.8.0.

One prominent example is the use of the unchecked function. This function was introduced for gas fee optimization and appears quite frequently in real-world applications. If an attack vector exists within this function, it can be triggered, so it should be carefully examined.

3. Denial-of-Service (DoS)

Denial-of-Service (DoS) vulnerabilities are those that can impair the functionality of a smart contract. These vulnerabilities are not limited to the Web3 ecosystem; they continue to be prevalent in websites, games, networks, and various other fields.

Differences (DoS attacks in the Web3 ecosystem) vs (general DoS attacks)

Meanwhile, DoS attacks in the Web3 ecosystem differ from the general DoS attacks we are familiar with.

reference: akamai (https://www.akamai.com/ko/glossary/what-are-syn-flood-ddos-attacks)

The DoS attacks we were previously familiar with involved overwhelming the network’s traffic to impair the service’s functionality. This could be done by sending repetitive requests or causing multiple TCP connection delays, such as with SYN flooding.

reference: ledger academy (https://www.ledger.com/academy/blockchain/what-is-proof-of-work)

However, blockchain is fundamentally designed to prevent such DoS attacks by ensuring that only verified blocks are confirmed for each request (transaction, block) through proof mechanisms. Therefore, DoS attacks in the Web3 ecosystem are based on exploiting the logic within smart contracts.

Example of Vulnerable Code

Below is the code for the famous “KingOfEther” contract, which contains a Denial-of-Service (DoS) vulnerability:

The claimThrone function in the KingOfEther contract works as follows:

  • When Ether is sent along with the function call, it compares the sent amount to the global variable balance.
  • It then sends the amount equivalent to balance to the current king.
  • The function sets the caller as the new king and the sent amount as the new balance.

However, this code has a vulnerability where if the caller’s contract does not accept Ether or enters an infinite loop in the fallback function, it can trigger a Denial-of-Service (DoS) vulnerability.

Below is the explanation of the exploit scenario:

Attack Transaction

Analyzing the attack transaction reveals that the Attacker contract does not implement a fallback function, causing the transaction to revert each time Ether is sent. This prevents the transaction from being confirmed, thereby triggering a DoS attack.

When the KingOfEther contract is affected by a triggered DoS attack, it becomes non-functional, causing significant disruption to the system’s operation.

Another method is to implement an infinite loop in the fallback function, as described below.

Attack Transaction

If the gas limit is set to 300,000,000 and the exploit code is executed, you will see a message indicating “OutOfGas,” meaning the gas has been exhausted.

Mitigation

  • Sufficient Validation within a Loop: Although we skipped explaining the Gas Exhaustion Attack, if a dynamic array is received as a parameter and iterated over in a loop, a Gas Exhaustion Attack can occur. Therefore, sufficient validation should be performed before executing the loop.
  • Gas Limit Consideration: Always be mindful of the gas limit. High gas consumption can result from loops or complex calculations. To optimize gas usage, use libraries and precomputed values whenever possible.
  • Use of Modifiers: Use modifiers to restrict access to specific functions. This prevents unauthorized users from calling functions that could potentially lead to DoS attacks.
  • Gas Price Estimation: Implement a mechanism to estimate the gas price required for function calls. This ensures that transactions are processed without unnecessary delays.

This vector is one of the most commonly encountered cases in real-world bug hunting, so it is crucial to apply secure coding practices attentively.

✍ Medium: Park DoYeon | 🕊️ Twitter: p6rkdoye0n | 📧Mail: parkttule0305

--

--