Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Reentrancy Auditing with Slither and Manual Review

Hello! Welcome to our next lesson in the "Advanced Solidity & Secure Development" module.

Introduction

In our last session, we focused on a defensive programming strategy, learning how to apply the Checks-Effects-Interactions (CEI) pattern to build contracts that are inherently resistant to reentrancy attacks. We explored why this pattern is crucial for maintaining state consistency.

Today, we're shifting our perspective from that of a builder to that of an auditor. It's not enough to know how to write secure code; you must also be able to analyze existing code and identify vulnerabilities. This lesson will equip you with the skills to do just that.

Learning Outcome: By the end of this lesson, you will be able to audit contracts for reentrancy vulnerabilities using static analysis (Slither) and manual review.

We will cover a two-pronged approach to auditing:

  1. Automated Analysis: Using Slither, a powerful static analysis tool, to quickly find common vulnerability patterns.
  2. Manual Review: Applying a systematic methodology to uncover more complex and nuanced reentrancy bugs that automated tools might miss.

1. Automated Auditing with Slither

The first line of defense in any audit is often automated tooling. Static analysis tools scan source code for known anti-patterns without actually executing it. For Solidity, the industry-standard tool is Slither, developed by the security firm Trail of Bits.

Given your background, you can think of static analysis as a sophisticated linter that understands the semantics of the code. It parses the code into an Abstract Syntax Tree (AST) and a Control Flow Graph (CFG) to analyze execution paths and data dependencies, allowing it to detect patterns that suggest vulnerabilities.

Getting Started with Slither

This video provides an excellent introduction to Slither, demonstrates how to use it, and importantly, discusses its limitations.

How To Find Smart Contract Vulnerabilities Automatically: Slither Complete Tutorial

Watch this tutorial from JohnnyTime to understand what Slither is, how it works, and see it in action finding a reentrancy vulnerability.

Please watch the following segments: What is Slither and How it Works (01:08 - 02:38): Get a high-level overview of the tool. Slither's Capabilities and Limitations (02:38 - 05:26): This part is crucial for understanding why Slither is not a replacement for a manual audit. Detecting Reentrancy with Slither (06:57 - 10:48): Observe how Slither identifies a classic reentrancy bug. Pay attention to how its output directly points out the violation of the CEI pattern: a state variable write occurring after an external call. When Slither Fails (10:48 - 12:25): This demonstrates a scenario where Slither misses a more complex, logical bug, reinforcing the need for manual review. Conclusion (12:25 - 14:06): This summary puts the tool's role in the proper context.

As you saw, running slither . on a project is a simple way to get an initial report. For a reentrancy vulnerability, Slither's output is very specific:

  • It identifies the function containing the vulnerability (e.g., EtherBank.withdraw).
  • It points to the external call (the "Interaction").
  • It highlights the state variables that are written to after the call (the misplaced "Effect").

This makes it an incredibly effective tool for catching straightforward violations of the CEI pattern.

Slither's Reentrancy Detectors

Slither has several specific detectors for different flavors of reentrancy, which are categorized by severity. Understanding these helps you interpret its reports more effectively.

Slither Detector Documentation

This is the official documentation for Slither's detectors. Skimming through the different reentrancy checks will give you a deeper appreciation for the tool's granularity.

Use Ctrl+F or your browser's find function to locate the five sections on 'Reentrancy vulnerabilities'. Read the 'Description' for each of these detectors: reentrancy-eth (High severity) reentrancy-no-eth (Medium severity) reentrancy-benign (Low severity) reentrancy-events (Low severity) reentrancy-unlimited-gas (Informational) Focus on how they differ, for example, by whether Ether is involved or if the impact is limited to event ordering.

The key takeaway is that Slither is an indispensable first pass. It automates the tedious work of finding common mistakes, allowing you to focus your manual effort on more complex issues.

2. Manual Reentrancy Auditing: A Systematic Approach

As the video on Slither demonstrated, automated tools have their limits. They struggle with developer intent, multi-contract interactions, and novel attack vectors. This is where manual review becomes critical. A skilled auditor combines a deep understanding of security principles with a systematic process to uncover hidden risks.

In the previous lesson, you watched parts of Owen Thurm's "The Ultimate Guide To Reentrancy". We will now focus on the final section, where he outlines his personal methodology for hunting reentrancy bugs.

The Ultimate Guide To Reentrancy

This segment provides a clear, actionable framework for manually auditing a codebase for any type of reentrancy vulnerability.

Watch the section 'Methodology for Detecting Reentrancy Vulnerabilities' from 25:09 to 29:00. Pay close attention to the three-step process he describes.

The Manual Audit Process

Let's formalize the methodology from the video into a step-by-step process. When auditing a contract for reentrancy, you should:

Step 1: Enumerate All External Calls
First, identify every single location in the code where execution is handed over to an external address. This includes:

  • Low-level calls: addr.call(...), addr.delegatecall(...), addr.staticcall(...)
  • Ether transfers: addr.transfer(...), addr.send(...)
  • Token transfers that might have hooks:
    • safeTransferFrom in ERC-721 tokens (calls onERC721Received)
    • Any transfer function in ERC-777 tokens (calls tokensToSend and tokensReceived hooks)
  • Calls to any external contract interface: IExternalContract(addr).someFunction()

Step 2: Analyze State Around Each Call
For each external call identified in Step 1, analyze the code around it. Specifically, look for violations of the CEI pattern:

  • Outdated State: Are there any state variables that are read before this external call but are only updated after it? This is the core of the vulnerability. The period between the external call and the state update is your window of opportunity.
  • State to Analyze: This includes balances, flags, counters, timestamps, addresses, or any other variable stored on-chain.

Step 3: Map Re-Entry Points & Assess Impact
Now, put on your attacker hat.

  • Identify Re-Entry Points: List all public and external functions in the contract (and potentially in other contracts within the same protocol) that an attacker could call.
  • Connect the Dots: For each risky external call from Step 2, consider what would happen if the attacker used their control to call back into one of the re-entry points from this step. Ask yourself: "Knowing that state variable X is outdated, can I call function Y to gain an advantage?"

Applying the Methodology

Let's consider how this process helps find the complex reentrancy types we discussed in the last lesson:

  • Cross-Function Reentrancy:

    1. External Call: You spot the .call{value: amount}("") in withdraw().
    2. Outdated State: You notice balances[msg.sender] is updated after the call.
    3. Re-Entry & Impact: You scan for other functions. You see a transfer() function that reads balances[msg.sender]. You immediately realize an attacker can re-enter via transfer() to move funds they technically no longer have, because the balance update in withdraw() hasn't happened yet.
  • Cross-Contract Reentrancy:

    1. External Call: In the SwapExecutor contract, you find the swapper.swap() call.
    2. Outdated State: You see that delete pendingSwaps[key] happens after the swap call. The state of the swap is outdated.
    3. Re-Entry & Impact: You look at the SwapManager contract and find a public function cancelSwap(). You connect the dots: an attacker, during the swap() execution, can re-enter the system by calling cancelSwap() on the other contract to get their initial funds back, effectively receiving both the swap output and their original input.
  • Read-Only Reentrancy:

    1. External Call: You find an external call in a withdraw function.
    2. Outdated State: A balance or allowance variable is updated after the call.
    3. Re-Entry & Impact: You notice a public view function like getHealthFactor() that relies on that balance. You realize that even though the attacker can't change state in the original contract, they can trigger an action in a third-party lending protocol that reads the stale, incorrect health factor, potentially allowing them to take out an undercollateralized loan.

This systematic process transforms auditing from a guessing game into a structured investigation, allowing you to find not just the obvious bugs, but the deep, architectural flaws as well.

Conclusion

Today you've learned to approach smart contracts with the critical eye of an auditor. You now have a powerful, two-step process for identifying reentrancy vulnerabilities.

Key Takeaways:

  • Auditing for reentrancy requires a combination of automated tools and rigorous manual review.
  • Slither is your first-pass tool to automatically detect common violations of the Checks-Effects-Interactions pattern.
  • Slither is not a substitute for human expertise; it can miss complex, multi-contract, or purely logical vulnerabilities.
  • A systematic manual audit process is essential: 1) Enumerate external calls, 2) Identify outdated state around those calls, and 3) Map potential re-entry points to assess the impact.
  • This manual methodology is what enables auditors to find sophisticated exploits like cross-function and cross-contract reentrancy.

Next Steps:

We have now covered how to prevent reentrancy (CEI pattern) and how to find it (auditing). In our next lesson, we will move to another fundamental pillar of smart contract security: access control. We will learn how to implement and audit common patterns like Ownable and AccessControl to ensure that only authorized users can perform sensitive actions.

Can't find a good explanation? Sign up and we'll make it for you

Sign up