⏰ TIME LIMIT: 30 MINUTES TOTAL
Analysis: 15 min | Discussion: 10 min | Wrap-up: 5 min
Activity Overview: This is a condensed version focusing on the two most critical vulnerability types: reentrancy attacks and access control failures. Perfect for tight schedules or as an in-class checkpoint.

Group Size: 2-3 students | Materials: This handout only (all-in-one)

Learning Objectives

Activity Structure

1 Contract Analysis (15 minutes)

Read both contracts below. For each one, identify the vulnerability, explain how an attacker could exploit it, and propose a fix. Fill in the worksheet at the end.

2 Group Discussion (10 minutes)

Share findings with the class. Each group presents one vulnerability (2-3 minutes). Discuss which is more severe and why.

3 Class Wrap-Up (5 minutes)

Instructor reveals real-world hacks using these exact vulnerabilities (The DAO: $60M, Poly Network: $600M)

🔍 Quick Vulnerability Reference

Reentrancy: External call before state update → attacker can call back before balance changes
Access Control: Missing permission checks → anyone can call privileged functions

Contract 1: Bank Withdrawal System

Vulnerability: Reentrancy Attack
A simple banking contract allowing deposits and withdrawals. Users can withdraw their balance at any time.
contract Bank {
    mapping(address => uint256) public balances;

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

    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");

        // Transfer funds to user
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");

        // Update balance
        balances[msg.sender] -= amount;
    }
}
❌ The Problem:

The contract sends ETH before updating the balance. This allows an attacker to call withdraw() again in a fallback function before the balance is decreased.

✅ The Fix:

Update the balance before sending ETH (checks-effects-interactions pattern):

balances[msg.sender] -= amount;  // Update FIRST
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
🎯 Real-World Example:

The DAO Hack (2016): $60 million stolen using this exact vulnerability. Led to Ethereum hard fork.

Contract 2: Token Ownership Manager

Vulnerability: Access Control Failure
A governance token where the owner can mint tokens and pause transfers. Critical for protocol security.
contract TokenManager {
    address public owner;
    bool public paused;
    mapping(address => uint256) public balances;

    constructor() {
        owner = msg.sender;
    }

    function mint(address to, uint256 amount) public {
        require(msg.sender == owner, "Only owner can mint");
        balances[to] += amount;
    }

    function setPaused(bool _paused) public {
        require(msg.sender == owner, "Only owner");
        paused = _paused;
    }

    function setOwner(address newOwner) public {
        // Transfer ownership to new address
        owner = newOwner;  // ⚠️ MISSING ACCESS CONTROL
    }

    function transfer(address to, uint256 amount) public {
        require(!paused, "Transfers paused");
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }
}
❌ The Problem:

The setOwner() function is missing require(msg.sender == owner). Anyone can call it and become owner, gaining unlimited minting power.

✅ The Fix:

Add access control check:

function setOwner(address newOwner) public {
    require(msg.sender == owner, "Only owner");  // ADD THIS
    require(newOwner != address(0), "Invalid address");
    owner = newOwner;
}
🎯 Real-World Example:

Poly Network Hack (2021): $600 million exploited through access control bug allowing attacker to become admin.

Worksheet - Submit at End of Class

Group Members:

Contract 1: Bank Withdrawal (Reentrancy)

1. Explain the vulnerability in your own words (4 points):
2. How would an attacker exploit this? Describe step-by-step (4 points):
3. What's the fix? (2 points):

Contract 2: Token Manager (Access Control)

1. Explain the vulnerability in your own words (4 points):
2. How would an attacker exploit this? Describe step-by-step (4 points):
3. What's the fix? (2 points):

Comparative Analysis

Which vulnerability is more severe? Why? Consider impact, likelihood, and difficulty to exploit (Participation credit):

Grading Rubric (20 Points Total)

Component Points Criteria
Contract 1 Analysis 10 Vulnerability explanation (4), Attack scenario (4), Fix (2)
Contract 2 Analysis 10 Vulnerability explanation (4), Attack scenario (4), Fix (2)
TOTAL 20 100%
💡 Tips for Success:
  • Be specific: "External call before state update" beats "there's a bug"
  • Think like an attacker: What would you do if you wanted to steal funds?
  • Line-by-line analysis: Read code carefully, checking what executes in what order
  • Use the reference: The vulnerability descriptions above are your guide

For Instructors: Teaching Notes

Timing Breakdown:
Introduction & instructions 3 minutes
Student work time (groups analyze contracts) 15 minutes
Group sharing (3-4 groups × 2-3 min each) 10 minutes
Wrap-up: Real-world examples & key takeaways 2 minutes
TOTAL 30 minutes
Key Points to Emphasize:
  • Stakes are real: These vulnerabilities have cost billions in actual hacks
  • Pattern recognition: Security comes from knowing what to look for
  • Both are CRITICAL: Either vulnerability can destroy a protocol
  • Prevention is cheaper: Bug bounty ($500K) << exploit ($50M) << recovery (impossible)
Discussion Questions:
  • "Why do you think developers still make these mistakes?"
  • "If you found this bug, would you exploit it or report it? Why?"
  • "How is smart contract security different from web app security?"
  • "What does this teach us about mechanism design?"
Extension Options (if time allows):
  • Add integer overflow contract (Contract 3 from full version)
  • Have students write attacker contracts in pseudocode
  • Discuss bug bounty economics in depth
  • Assign full version as homework
⚠️ Answer Key Summary (Instructor Only)

Contract 1: Reentrancy - external call before balance update. Fix: reverse order.
Contract 2: Missing access control on setOwner(). Fix: add require(msg.sender == owner).
Severity: Both CRITICAL. Accept either as "most severe" with valid reasoning.

© Joerg Osterrieder 2025-2026. All rights reserved.