huichain/smart-contract-security-lab

GitHub: huichain/smart-contract-security-lab

Stars: 0 | Forks: 0

# Smart Contract Security Lab A Foundry-based lab to learn smart contract security by reproducing real vulnerabilities, writing PoCs, and proposing fixes. This repo is built **day by day**, not all at once. Each day adds one small, working piece: a vulnerable contract, an attacker, a fix, or a report. ## Status - ✅ **Reentrancy** module complete — vulnerable contract, attacker, fix, 3 passing tests, audit-style writeup - ✅ **Access Control** module complete — vulnerable + fixed contracts, 5 passing tests, audit-style writeup - 🟡 **Signature Replay** module in progress — vulnerable airdrop, fixed implementation, and 3 passing tests; writeup next - ⚪ Oracle Manipulation, Upgradeable Proxy — planned ## Reentrancy — Vulnerable Vault, Exploit PoC, Fix, and Writeup - [x] `src/reentrancy/VulnerableVault.sol` A minimal ETH vault that sends ETH to the user **before** updating the user's balance, which makes it vulnerable to reentrancy. - [x] `src/reentrancy/ReentrancyAttacker.sol` Attacker contract that re-enters `withdraw` from `receive()` to drain the vault. - [x] `src/reentrancy/FixedVault.sol` Hardened vault using checks-effects-interactions and OpenZeppelin's `ReentrancyGuard` (defense in depth). - [x] `test/reentrancy/ReentrancyPoC.t.sol` Foundry PoC test suite: - `testExploit_DrainsVault` — the attack drains a 10 ETH vault with 1 ETH of attacker capital - `testFix_BlocksReentrancy` — the same attacker against `FixedVault` reverts and victim funds remain safe - `testFix_AllowsHonestWithdraw` — sanity check that the fix does not break legitimate users - [x] [`reports/01-reentrancy.md`](reports/01-reentrancy.md) Audit-style writeup: severity, summary, root cause, PoC, recommendation, fixed implementation, and learnings. ## Access Control — Vulnerable Treasury, Exploit PoC, Fix - [x] `src/access-control/VulnerableTreasury.sol` A protocol treasury with an `owner` field, but `withdraw` and `setOwner` have **no access checks** — two independent bugs. - [x] `src/access-control/FixedTreasury.sol` Hardened treasury using OpenZeppelin `Ownable` and `onlyOwner` on sensitive functions (no separate attacker contract needed; EOA can exploit the vulnerable version). - [x] `test/access-control/AccessControlPoC.t.sol` Foundry PoC test suite: - `testExploit_AnyoneCanDrainTreasury` — any account can call `withdraw` and drain all ETH - `testExploit_AnyoneCanBecomeOwner` — any account can call `setOwner` and seize ownership - `testFix_BlocksUnauthorizedWithdraw` — attacker `withdraw` reverts with `OwnableUnauthorizedAccount` - `testFix_BlocksUnauthorizedSetOwner` — attacker `setOwner` reverts; owner unchanged - `testFix_AllowsOwnerFunctions` — legitimate owner can still withdraw and transfer ownership - [x] [`reports/02-access-control.md`](reports/02-access-control.md) Audit-style writeup: two findings (`withdraw`, `setOwner`), severity, PoC, recommendation, fixed implementation, and learnings. ## Signature Replay — Vulnerable Airdrop, Replay PoC, Fix - [x] `src/signature-replay/VulnerableAirdrop.sol` A deliberately vulnerable ETH airdrop that accepts an off-chain signature from a trusted signer, but the signed message only binds `account` and `amount`. - [x] `test/signature-replay/SignatureReplayPoC.t.sol` Foundry PoC test suite: - `testExploit_SameSignatureClaimsTwice` — reuses the exact same signature twice and proves the claimant receives the airdrop twice - `testFix_BlocksSignatureReplay` — proves the fixed contract consumes the user's nonce and rejects the replayed signature - `testFix_RejectsExpiredSignature` — proves expired signatures cannot be used - [x] `src/signature-replay/FixedAirdrop.sol` Fixed implementation: binds signatures to nonce, deadline, chain id, and `address(this)`. - [ ] `reports/03-signature-replay.md` Planned audit-style writeup covering replay impact, root cause, PoC, and mitigation. ## Project Structure smart-contract-security-lab/ ├─ foundry.toml # Foundry config + remappings ├─ foundry.lock # Locked dependency versions ├─ .gitmodules # Git submodules (forge-std, openzeppelin-contracts) ├─ .gitignore ├─ remappings.txt # IDE-friendly remappings (mirrors foundry.toml) ├─ README.md ├─ lib/ │ ├─ forge-std/ # Foundry standard testing library (submodule) │ └─ openzeppelin-contracts/ # OpenZeppelin Solidity library (submodule) ├─ src/ │ ├─ reentrancy/ │ │ ├─ VulnerableVault.sol │ │ ├─ ReentrancyAttacker.sol │ │ └─ FixedVault.sol │ ├─ access-control/ │ │ ├─ VulnerableTreasury.sol │ │ └─ FixedTreasury.sol │ └─ signature-replay/ │ ├─ VulnerableAirdrop.sol │ └─ FixedAirdrop.sol ├─ test/ │ ├─ reentrancy/ │ │ └─ ReentrancyPoC.t.sol │ ├─ access-control/ │ │ └─ AccessControlPoC.t.sol │ └─ signature-replay/ │ └─ SignatureReplayPoC.t.sol └─ reports/ ├─ 01-reentrancy.md └─ 02-access-control.md ## Dependencies - [Foundry](https://book.getfoundry.sh) - [forge-std](https://github.com/foundry-rs/forge-std) `v1.16.1` — Foundry standard testing library - [OpenZeppelin Contracts](https://github.com/OpenZeppelin/openzeppelin-contracts) `v5.6.1` — battle-tested Solidity components used in fixed versions (Ownable, ReentrancyGuard, ECDSA, etc.) All dependencies are installed as git submodules under `lib/` and locked in `foundry.lock`. ## Getting Started ### 1. Install Foundry Quick reference: # macOS / Linux / WSL curl -L https://foundry.paradigm.xyz | bash foundryup # Windows (PowerShell) powershell -c "irm https://foundry.paradigm.xyz/install.ps1 | iex" foundryup ### 2. Clone with submodules git clone --recurse-submodules https://github.com/huichain/smart-contract-security-lab.git cd smart-contract-security-lab If you already cloned without submodules: git submodule update --init --recursive ### 3. Build forge build ### 4. Test The lab currently ships with **11 passing tests** across two complete modules plus the in-progress Signature Replay module. **Reentrancy** (`test/reentrancy/`): - `testExploit_DrainsVault` — proves the attacker drains a 10 ETH vault with 1 ETH of capital. - `testFix_BlocksReentrancy` — proves the same exploit reverts against `FixedVault`. - `testFix_AllowsHonestWithdraw` — sanity check that legitimate users still work. **Access Control** (`test/access-control/`): - `testExploit_AnyoneCanDrainTreasury` — proves anyone can drain the treasury via `withdraw`. - `testExploit_AnyoneCanBecomeOwner` — proves anyone can seize ownership via `setOwner`. - `testFix_BlocksUnauthorizedWithdraw` — proves `FixedTreasury` blocks unauthorized withdrawals. - `testFix_BlocksUnauthorizedSetOwner` — proves unauthorized ownership transfer reverts. - `testFix_AllowsOwnerFunctions` — sanity check that the owner can still operate the treasury. **Signature Replay** (`test/signature-replay/`): - `testExploit_SameSignatureClaimsTwice` — proves the same signature can be replayed to claim twice. - `testFix_BlocksSignatureReplay` — proves nonce consumption blocks replaying the same signature. - `testFix_RejectsExpiredSignature` — proves signatures cannot be used after their deadline. Run all tests: forge test Run a single module: forge test --match-path test/access-control/AccessControlPoC.t.sol -vv ## Roadmap (high-level) | Vulnerability | Status | | --- | --- | | Reentrancy | ✅ Done — vulnerable + attacker + fix + tests + writeup | | Access Control | ✅ Done — vulnerable + fix + tests + writeup | | Signature Replay | 🟡 In progress — vulnerable + fixed airdrop + tests; writeup next | | Oracle Manipulation | ⚪ Planned | | Upgradeable Proxy | ⚪ Planned | ## About the Author Software engineer with C++ / C# background, transitioning into smart contract security and Web3 tooling. - GitHub: [huichain](https://github.com/huichain) - X: [@vividhui](https://x.com/vividhui)