Web3 / Smart Contract Security Intro
Reentrancy, access control bugs, integer overflow/underflow, and oracle manipulation — why immutable, financially-loaded smart contract bugs are uniquely unforgiving.
Learning Objectives
- → Explain why smart contract bugs are uniquely severe: immutability and direct financial value
- → Explain the reentrancy attack pattern and the checks-effects-interactions fix
- → Identify access control vulnerabilities in smart contracts
- → Explain oracle manipulation and front-running/MEV as DeFi-specific attack classes
- → Describe integer overflow/underflow risk in smart contract arithmetic
Why Smart Contract Bugs Are Uniquely Severe
A traditional web application bug can usually be patched and deployed within minutes. A deployed smart contract is, by design, immutable — once live on the blockchain, its code typically cannot be changed. Combined with the fact that contracts frequently hold real financial value directly (unlike a typical web app, which holds data about value stored elsewhere), a single bug can result in an instant, irreversible, and often fully public loss of funds.
Reentrancy: The Classic Smart Contract Vulnerability
Reentrancy exploits a contract that makes an external call (e.g. sending funds) before updating its own internal state:
// VULNERABLE
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
(bool success, ) = msg.sender.call{value: amount}(""); // external call FIRST
balances[msg.sender] -= amount; // state update AFTER
}
A malicious contract's fallback function can call withdraw() again during the external call, before balances[msg.sender] is decremented — repeating the withdrawal multiple times against the same original balance check. This is exactly the pattern behind the 2016 DAO hack.
The fix — checks-effects-interactions: update internal state before making any external call:
// FIXED
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount; // effects FIRST
(bool success, ) = msg.sender.call{value: amount}(""); // interaction AFTER
}
Access Control Vulnerabilities
A missing or incorrect access modifier on a sensitive function (e.g. one that mints tokens or withdraws contract funds) lets any address call it, not just the intended owner/admin. This is functionally identical to a missing authorization check on a web API endpoint — the same web security concept applied to on-chain code.
Integer Overflow/Underflow
Older Solidity versions (pre-0.8) don't automatically revert on arithmetic overflow/underflow — a uint decremented below zero silently wraps around to a massive number instead of erroring, which can be weaponized to bypass balance checks entirely. Modern Solidity (0.8+) reverts by default, but contracts using unchecked blocks or older compiler versions remain vulnerable.
Oracle Manipulation and Front-Running/MEV
- Oracle manipulation — many DeFi contracts rely on an external price oracle; if that oracle can be manipulated (e.g. via a flash loan distorting a price briefly), a contract trusting it can be tricked into believing an asset is worth far more or less than its real value
- Front-running/MEV (Maximal Extractable Value) — because pending blockchain transactions are visible before confirmation, an attacker (or the block producer itself) can observe a profitable pending transaction and insert their own transaction ahead of it to profit from the resulting price movement
Common Pitfalls
- Deploying a contract holding real value without an independent security audit, given that bugs are effectively permanent once live
- Trusting a single price oracle with no manipulation resistance (e.g. time-weighted averaging, multiple sources)
- Assuming Solidity 0.8+'s default overflow checks make all arithmetic automatically safe, ignoring
uncheckedblocks - Treating smart contract security as identical to web security without accounting for immutability and direct financial stakes
Unlike a typical web app, a live contract usually cannot simply be patched after a bug is discovered.
Why does a smart contract bug tend to be more severe than an equivalent traditional web app bug?
The vulnerable version sends funds out before recording that the balance was already spent.
In the vulnerable withdraw() pattern, what is the ordering mistake that enables reentrancy?
The external call — the riskiest part — should always be the very last thing a function does.
What does the checks-effects-interactions pattern require, in order?
💪 Exercises & Challenges
Web3 / Smart Contract Security Intro MCQ
Test your understanding of Web3 / Smart Contract Security Intro.
Rewrite a Vulnerable Withdraw Function
Given the vulnerable withdraw() function shown in the lesson content (external call before balance update), rewrite it applying the checks-effects-interactions pattern, and explain in one sentence why
Identify the Oracle Attack
A DeFi lending protocol's post-mortem reveals: an attacker took out a large flash loan, used it to briefly distort the price reported by the protocol's single price oracle, borrowed against collateral