SECTION 1: LEARNING OBJECTIVES

By the end of this lesson, you will be able to:

  • Define blockchain security and its unique challenges.

  • Explain common smart contract vulnerabilities and attack vectors.

  • Understand the security audit process and its importance.

  • Describe formal verification and its role in security.

  • Differentiate between automated and manual auditing approaches.

  • Identify security best practices for smart contract development.

  • Implement a basic vulnerability detection simulation in Python.

  • Develop a framework for evaluating smart contract security.


SECTION 2: THE SECURITY LANDSCAPE

2.1 Why Blockchain Security is Different

Blockchain introduces a new security paradigm distinct from traditional IT security. Understanding these differences is essential for building secure applications.

 
 
Aspect Traditional Security Blockchain Security
Immutability Data can be modified or deleted Data is permanent and unchangeable
Trust Model Trust in centralised authority Trust in code and consensus
Attack Surface Servers, databases, networks Smart contracts, consensus, wallets
Recovery Patches and rollbacks possible No rollbacks; upgrades require governance
Identity Centralised user management Cryptographic keys and addresses
Transparency Limited, often proprietary Fully open and auditable

2.2 Financial Impact of Security Breaches

The blockchain industry has experienced significant financial losses due to security incidents. Major hacks have affected exchanges, DeFi protocols, and bridges.

Key Statistics:

  • Cumulative losses from DeFi hacks exceed $10B (2021-2024).

  • Average DeFi hack loss: ~$25M per incident.

  • Bridge hacks account for over 30% of total losses (e.g., Ronin $625M, Wormhole $320M).

  • Smart contract bugs are the leading cause of DeFi exploits.

  • Most attacks target code vulnerabilities rather than consensus-level attacks.

2.3 Security Principles for Blockchain

  1. Least Privilege: Contracts should only have the permissions they need.

  2. Defense in Depth: Multiple layers of security, not relying on a single measure.

  3. Fail Securely: If something goes wrong, the system should default to safe state.

  4. Minimal Attack Surface: Expose only essential functions and data.

  5. Assume Breach: Design systems assuming that attackers may gain partial access.

  6. Immutable Trust: Never rely on mutable external state for critical decisions.


SECTION 3: COMMON SMART CONTRACT VULNERABILITIES

3.1 Vulnerability Categories

text
┌─────────────────────────────────────────────────────────────────────────────┐
│                    SMART CONTRACT VULNERABILITY CATEGORIES                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  1. EXECUTION VULNERABILITIES                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │ • Reentrancy – recursive call before state update                   │   │
│  │ • Unchecked external calls – relying on untrusted contract return   │   │
│  │ • Denial of Service (DoS) – blocking contract operations           │   │
│  │ • Gas limitations – out-of-gas errors, loops                       │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  2. LOGIC VULNERABILITIES                                                  │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │ • Access control – missing or insufficient checks                 │   │
│  │ • Business logic errors – incorrect validation                     │   │
│  │ • Arithmetic errors – overflow/underflow                          │   │
│  │ • Timestamp dependence – relying on block timestamps              │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  3. INTERACTION VULNERABILITIES                                            │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │ • Front-running – transaction ordering manipulation               │   │
│  │ • Oracle manipulation – feeding false price data                  │   │
│  │ • Sandwich attacks – exploiting AMM slippage                      │   │
│  │ • Flash loan attacks – using uncollateralised loans               │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  4. COMPOSITION VULNERABILITIES                                            │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │ • Dependency vulnerabilities – unsafe external contracts          │   │
│  │ • Upgradeability risks – proxy patterns misuse                    │   │
│  │ • Inheritance issues – unexpected override conflicts              │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

3.2 Deep Dive: Major Vulnerabilities

Reentrancy:
Reentrancy occurs when a contract calls an external contract that can call back into the original function before the original call completes, allowing repeated execution of sensitive logic.

Example (Simplified):

solidity
// VULNERABLE
function withdraw(uint amount) public {
    require(balances[msg.sender] >= amount);
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success);
    balances[msg.sender] -= amount;
}

Fix (Checks-Effects-Interactions):

solidity
// SECURE
function withdraw(uint amount) public {
    require(balances[msg.sender] >= amount);
    balances[msg.sender] -= amount;  // Update state FIRST
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success);
}

Arithmetic Overflow/Underflow:
Before Solidity 0.8, arithmetic operations could wrap around, causing unexpected values. Use SafeMath or Solidity 0.8+ which has built-in overflow checks.

Access Control:
Functions that perform privileged actions (admin functions, minting, withdrawals) must have proper access modifiers.

Front-Running:
Transactions are visible in the mempool before inclusion. Attackers can see pending transactions and front-run them with higher gas fees. Mitigations include commit-reveal schemes, threshold mechanisms, and private mempools.

Oracle Manipulation:
DeFi protocols rely on price oracles (e.g., Chainlink). If an oracle can be manipulated, it can lead to incorrect liquidations or arbitrage. Use multiple oracles, time-weighted averages, and circuit breakers.


SECTION 4: THE AUDITING PROCESS

4.1 Security Audit Lifecycle

A comprehensive security audit follows a structured process to identify and mitigate vulnerabilities.

Phase 1: Preparation

  • Scope definition (which contracts, versions)

  • Documentation review (specifications, architecture)

  • Understanding of business logic and use cases

  • Setting up testing environment

Phase 2: Automated Analysis

  • Static analysis tools (Slither, Mythril, Securify)

  • Dynamic analysis (fuzzing, symbolic execution)

  • Coverage analysis and gas profiling

  • Identifying common vulnerability patterns

Phase 3: Manual Review

  • Code walk-through by security experts

  • Architecture and design review

  • Access control verification

  • Business logic verification

  • Special attention to complex functions

Phase 4: Testing

  • Unit tests for edge cases

  • Integration tests

  • Property-based tests

  • Fuzzing for unexpected inputs

Phase 5: Reporting

  • Classified findings (Critical, High, Medium, Low, Informational)

  • Recommendations for remediation

  • Reproducible test cases

  • Prioritisation and roadmap

Phase 6: Remediation & Verification

  • Developer fixes issues

  • Auditor verifies fixes

  • Updated report with verification status

4.2 Audit Levels

 
 
Level Description Scope
Full Audit Comprehensive review All contracts, all functions
Focused Audit Prioritised review Critical functions only
Code Review Peer review Code quality and correctness
Quick Scan Automated tools only Basic vulnerability detection

4.3 Auditor Credentials

Look for auditors with:

  • Reputation: Track record in the industry.

  • Expertise: Deep understanding of blockchain and security.

  • Methodology: Clear, transparent audit process.

  • Tools: Use of state-of-the-art tooling.

  • Reporting: Clear, actionable findings.

  • Post-Audit Support: Remediation assistance.

Top audit firms: Trail of Bits, OpenZeppelin, ConsenSys Diligence, CertiK, Hacken.


SECTION 5: TOOLS FOR SECURITY

5.1 Static Analysis Tools

 
 
Tool Description Language
Slither Python-based static analysis Solidity
Mythril Security analysis tool Solidity
Securify Formal verification Solidity
Solhint Linter for Solidity Solidity
Ethlint Style guide enforcement Solidity

5.2 Dynamic Analysis Tools

 
 
Tool Description Use Case
Foundry (forge) Fuzzing and invariant testing Solidity
Echidna Property-based fuzzing Solidity
Manticore Symbolic execution EVM bytecode
Hardhat Local testing environment Smart contract development

5.3 Formal Verification

Formal verification mathematically proves that a smart contract satisfies specific properties (invariants) under all possible inputs.

Approaches:

  • Model Checking: Exhaustively explores all states.

  • Theorem Proving: Uses mathematical logic to prove properties.

  • Symbolic Execution: Evaluates all possible execution paths.

Limitations:

  • High cost and complexity.

  • May not catch all vulnerabilities.

  • Requires specialised skills.

5.4 Bug Bounties

Bug bounty programs incentivise ethical hackers to find vulnerabilities.

Key Elements:

  • Clear scope and rules

  • Attractive reward levels (tiered by severity)

  • Responsive triage team

  • Transparent disclosure policy

  • Insurance for bounty amounts

Platforms: Immunefi, HackerOne, Bugcrowd.


SECTION 6: IMPLEMENTATION IN PYTHON

python
# ===================================================================
# MODULE 4, LESSON 5: SECURITY AND AUDITING
# ===================================================================

import hashlib
import json
import random
import re
from typing import Dict, List, Optional, Any
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import warnings
warnings.filterwarnings('ignore')

print("="*70)
print("SECURITY AND AUDITING")
print("="*70)

# ----------------------------------------------------------------
# PART A: VULNERABILITY PATTERN DETECTION SIMULATION
# ----------------------------------------------------------------

print("\n" + "-"*60)
print("PART A: Vulnerability Pattern Detection")
print("-"*60)

class VulnerabilityScanner:
    """
    Simulated vulnerability scanner for smart contract code.
    """
    def __init__(self):
        self.vulnerability_patterns = {
            'reentrancy': {
                'patterns': [r'\.call\{.*?\}\(', r'\.delegatecall\{.*?\}\('],
                'severity': 'Critical',
                'description': 'Unchecked external call could lead to reentrancy'
            },
            'unchecked_return': {
                'patterns': [r'\.transfer\(', r'\.send\('],
                'severity': 'High',
                'description': 'Transfer/send without checking return value'
            },
            'unprotected_function': {
                'patterns': [r'function\s+\w+\(.*?\)\s+public\s+{'],
                'severity': 'High',
                'description': 'Public function without access control'
            },
            'timestamp_dependence': {
                'patterns': [r'block\.timestamp', r'block\.number'],
                'severity': 'Medium',
                'description': 'Dependence on block timestamp/number'
            },
            'unchecked_math': {
                'patterns': [r'[+\-*/]+\s*[;]'],
                'severity': 'High',
                'description': 'Potential arithmetic overflow/underflow'
            }
        }
        self.findings = []
    
    def scan_code(self, code: str) -> List[Dict]:
        """Scan smart contract code for vulnerabilities."""
        findings = []
        
        for vuln_name, vuln_data in self.vulnerability_patterns.items():
            for pattern in vuln_data['patterns']:
                matches = re.finditer(pattern, code)
                for match in matches:
                    findings.append({
                        'vulnerability': vuln_name,
                        'severity': vuln_data['severity'],
                        'description': vuln_data['description'],
                        'matched_text': match.group(0)[:50] + '...' if len(match.group(0)) > 50 else match.group(0),
                        'position': match.start()
                    })
        
        # Sort by severity
        severity_order = {'Critical': 0, 'High': 1, 'Medium': 2, 'Low': 3}
        findings.sort(key=lambda x: severity_order.get(x['severity'], 4))
        
        self.findings = findings
        return findings
    
    def get_summary(self) -> Dict:
        if not self.findings:
            return {'total': 0, 'critical': 0, 'high': 0, 'medium': 0, 'low': 0}
        
        summary = {'total': len(self.findings), 'critical': 0, 'high': 0, 'medium': 0, 'low': 0}
        for finding in self.findings:
            severity = finding['severity'].lower()
            if severity in summary:
                summary[severity] += 1
        return summary

# Sample vulnerable contract code (simplified)
sample_code = """
contract Vulnerable {
    mapping(address => uint) public balances;
    
    function withdraw(uint amount) public {
        require(balances[msg.sender] >= amount);
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
        balances[msg.sender] -= amount;
    }
    
    function setBalance(address user, uint amount) public {
        balances[user] = amount;
    }
    
    function getTimestamp() public view returns (uint) {
        return block.timestamp;
    }
}
"""

# Scan code
scanner = VulnerabilityScanner()
findings = scanner.scan_code(sample_code)

print("Vulnerability Scan Results:")
summary = scanner.get_summary()
print(f"  Total Findings: {summary['total']}")
print(f"  Critical: {summary['critical']}")
print(f"  High: {summary['high']}")
print(f"  Medium: {summary['medium']}")
print(f"  Low: {summary['low']}")

print("\nDetailed Findings:")
for finding in findings[:5]:
    print(f"  [{finding['severity']}] {finding['vulnerability']}")
    print(f"    {finding['description']}")
    print(f"    Pattern: {finding['matched_text']}")

# ----------------------------------------------------------------
# PART B: AUDIT SEVERITY CLASSIFICATION
# ----------------------------------------------------------------

print("\n" + "-"*60)
print("PART B: Audit Severity Classification")
print("-"*60)

severity_data = {
    'Severity': ['Critical', 'High', 'Medium', 'Low', 'Informational'],
    'Description': [
        'Allows asset theft or total compromise',
        'Significant risk of loss or misuse',
        'Limited impact or under specific conditions',
        'Minor issues, no direct loss',
        'Best practices, recommendations'
    ],
    'Action Required': [
        'Immediate fix',
        'Priority fix',
        'Fix next release',
        'Consider fixing',
        'Consider implementing'
    ],
    'Examples': [
        'Reentrancy, access control bypass',
        'Oracle manipulation, logic errors',
        'Gas issues, timestamp dependence',
        'Unused variables, style issues',
        'Code clarity, documentation'
    ]
}

severity_df = pd.DataFrame(severity_data)
print(severity_df.to_string(index=False))

# ----------------------------------------------------------------
# PART C: SECURITY TOOLS COMPARISON
# ----------------------------------------------------------------

print("\n" + "-"*60)
print("PART C: Security Tools Comparison")
print("-"*60)

tools_data = {
    'Tool': ['Slither', 'Mythril', 'Foundry', 'Securify', 'Echidna'],
    'Type': ['Static', 'Static/Dynamic', 'Dynamic', 'Formal Verification', 'Fuzzing'],
    'Speed': ['Fast', 'Medium', 'Fast', 'Slow', 'Medium'],
    'False Positives': ['Medium', 'High', 'Low', 'Low', 'Medium'],
    'Ease of Use': ['High', 'Medium', 'High', 'Low', 'Medium']
}

tools_df = pd.DataFrame(tools_data)
print(tools_df.to_string(index=False))

# ----------------------------------------------------------------
# PART D: AUDIT PROCESS VISUALISATION
# -----------------------------------------------------------------

print("\n" + "-"*60)
print("PART D: Audit Process Visualisation")
print("-"*60)

# Simulate audit phases and time allocation
phases = ['Preparation', 'Automated Analysis', 'Manual Review', 'Testing', 'Reporting', 'Remediation']
time_allocation = [10, 15, 30, 20, 15, 10]  # percentage

fig, ax = plt.subplots(figsize=(10, 5))
ax.bar(phases, time_allocation, color='blue', alpha=0.7)
ax.set_ylabel('Time Allocation (%)')
ax.set_title('Security Audit Phase Distribution')
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('audit_phases.png', dpi=300, bbox_inches='tight')
plt.show()
print("Audit phases chart saved as 'audit_phases.png'")

# ----------------------------------------------------------------
# PART E: SUMMARY AND RECOMMENDATIONS
# -----------------------------------------------------------------

print("\n" + "="*70)
print("PART E: Summary and Recommendations")
print("="*70)

print("""
Security and Auditing – Key Takeaways:

1. Blockchain security is unique due to immutability, public access, and financial incentives.
2. Common vulnerabilities: reentrancy, arithmetic errors, access control, front-running, oracle manipulation.
3. The audit lifecycle: Preparation → Automated Analysis → Manual Review → Testing → Reporting → Remediation.
4. Tools: static analysis (Slither), dynamic analysis (Foundry), formal verification (Securify).
5. Audit severity levels: Critical, High, Medium, Low, Informational.
6. Bug bounties incentivise ethical hacking and help uncover hidden vulnerabilities.
7. Secure development principles: least privilege, defense in depth, fail securely, minimal attack surface.

Security Best Practices:
  - Use latest Solidity version with built-in overflow checks.
  - Implement Checks-Effects-Interactions pattern.
  - Use OpenZeppelin libraries for standard contracts.
  - Conduct external audits for production contracts.
  - Run continuous fuzzing and invariant testing.
  - Establish bug bounty programs.
  - Regularly update dependencies.
  - Monitor contract activity for anomalies.
""")