Skip to main content

Static_Analysis


Smart Contract Security Analysis with Slither


Overview

In this security research exercise, I analyzed three example Solidity contracts using Slither — a static analysis tool for smart contracts.
The goal was to:

  • Detect vulnerabilities
  • Understand the risks
  • Show how to write safer smart contracts
  • Practice presenting findings to recruiters

Contracts Under Review

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/*
Your original Counter — unchanged
*/
contract Counter {
int256 private count;
address public owner;

constructor() payable {
count = 0;
owner = msg.sender;
}

function increment() public {
count += 1;
owner = msg.sender;
}

function decrement() public {
count -= 1;
owner = msg.sender;
}

function getCount() public view returns (int256) {
return count;
}
}

/*
Simple DeFi: a minimal deposit/withdraw vault — INTENTIONALLY VULNERABLE
- Uses .call and updates state AFTER sending ETH → classic reentrancy pattern
- Public mapping shows balances
*/
contract SimpleVault {
mapping(address => uint256) public balanceOf;

function deposit() external payable {
balanceOf[msg.sender] += msg.value;
}

function withdraw(uint256 amount) external {
require(balanceOf[msg.sender] >= amount, "insufficient");
// Vulnerable: external call before state update
(bool ok, ) = payable(msg.sender).call{value: amount}("");
require(ok, "send failed");
balanceOf[msg.sender] -= amount; // state update after external call
}
}

/*
Safer DeFi version:
- Checks-Effects-Interactions pattern (state update BEFORE external call)
*/
contract SafeVault {
mapping(address => uint256) public balanceOf;

function deposit() external payable {
balanceOf[msg.sender] += msg.value;
}

function withdraw(uint256 amount) external {
require(balanceOf[msg.sender] >= amount, "insufficient");
// Effects first
balanceOf[msg.sender] -= amount;
// Interaction after effects
(bool ok, ) = payable(msg.sender).call{value: amount}("");
require(ok, "send failed");
}
}

My Command Line Process

I used Slither to run the analysis in my local environment:

root@lenova:~/practice# slither .
'forge clean' running (wd: /root/practice)
'forge config --json' running
'forge build --build-info --skip */test/** */script/** --force' running (wd: /root/practice)

INFO:Detectors:
Reentrancy in SimpleVault.withdraw(uint256) (src/Counter.sol#44-50):
External calls:
- (ok,None) = address(msg.sender).call{value: amount}() (src/Counter.sol#47)
State variables written after the call(s):
- balanceOf[msg.sender] -= amount (src/Counter.sol#49)
SimpleVault.balanceOf can be used in cross function reentrancies.
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities

INFO:Detectors:
Contract locking ether found:
Contract Counter has payable functions but no way to withdraw the ether.
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#contracts-that-lock-ether

INFO:Detectors:
Version constraint ^0.8.0 contains known severe issues.

INFO:Detectors:
Low level call in SimpleVault.withdraw(uint256)
Low level call in SafeVault.withdraw(uint256)

INFO:Slither:. analyzed (3 contracts with 93 detectors), 5 result(s) found

Findings & Analysis

1. Reentrancy Vulnerability — SimpleVault (High Risk)

  • Found in:
    (bool ok, ) = payable(msg.sender).call{value: amount}("");
    balanceOf[msg.sender] -= amount;
  • Why bad: State change happens after sending ETH, allowing malicious fallback calls to exploit the function.
  • Fix: Use Checks-Effects-Interactions pattern or ReentrancyGuard.

2. Locked Ether — Counter (Medium Risk)

  • Contract can receive ETH (payable constructor) but no function to withdraw it.
  • Effect: ETH sent can never be recovered.

3. Outdated Solidity Version (Medium Risk)

  • ^0.8.0 includes known compiler bugs.
  • Fix: Upgrade to latest stable version (e.g., ^0.8.21).

4. Low-Level Call Usage (Low Risk / Informational)

  • .call is used in both withdraw functions.
  • Risk: Can trigger arbitrary fallback logic.
  • Recommendation: Use cautiously, always handle return values.

Summary Table

Issue DetectedContractSeverityImpact
ReentrancySimpleVaultHighTheft of ETH
Locked EtherCounterMediumFunds locked
Outdated Solidity VersionAllMediumCompiler bugs
Low-Level Call UsageVaultsLowUnexpected behavior

Lessons Learned

  • Always update state before external calls.
  • Provide ETH recovery methods for payable contracts.
  • Use the latest compiler versions.
  • Treat .call with caution.