Tasks
The assessment objectives and respective statuses are summarized in the following table:
Objective | Status |
| Objective met - suicide vulnerabilies identified pertaining to improper access control |
| Objective met - ran slither on all contracts, identified over 14 vulnerabilities. |
| Objective met - Reentry exploit code produced and deployed to Ganache environment. |
| Objective not met - Did not perform dynamic testing using MithX due to commercial license required. |
Summarized Findings
The following SmartContracts were subjected to static analysis using slither for common vulnerabilities:
Private-Sale.sol,
Halborn-Pool.sol,
MyTokens.sol
The vulnerabilities identified with business impact are:
Reentry (sev: high) - 3 instances found,
State-variable-shadowing (high),
Suicidal (sev: high) - 2 instances found,
Unchecked-transfer (sev: high),
Contract locking (sev: Med)
Note, all vulnerabilities with the impact of High & Medium are highlighted in yellow in the evidence section below.
There were 2 instances found of improper access control or rather known as a “suicide” vulnerability in MyToken.sol &
HalbornPool.sol respectively, it meant that a malicious attacker could self-destruct the contract. These vulnerabilities are
categorized as SWC-106 at https://swcregistry.io/docs/SWC-106
The following vulnerabilities with informational severity are:
Low level calls
Different Pragma Directives were used
Conformance to Solidity naming conventions
Dead Code
Unused State Variable
State Variable that Could be declared constant
Different pragma in used
Dead-Code
Incorrect versions of Solidity
A Proof of Concept (PoC) exploit was produced for Private-Sales.sol to drain the Bank of all Ether by exploiting a Reentry
vulnerability in lines 35-47. The PoC was built in remix and connected to the Ganache environment. The full Reentry exploit code (attacker.sol)
was also added to this report. It was noted that the following SmartContracts did not return any issues during analysis:
Migrations.sol,
HalbornInterface.sol
Evidences
Static Analysis: Private-Sale.sol
$ slither Private-Sale.sol Reentrancy in PrivateSale.getRefund(uint256) (Private-Sale.sol#35-47): External calls: - (refunded) = msg.sender.call{value: quantity * TICKET}() (Private-Sale.sol#41) State variables written after the call(s): - purchasedTickets[msg.sender] -= quantity (Private-Sale.sol#45) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities solc-0.8.10 is not recommended for deployment Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity Low level call in PrivateSale.getRefund(uint256) (Private-Sale.sol#35-47): - (refunded) = msg.sender.call{value: quantity * TICKET}() (Private-Sale.sol#41) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls |
Static Analysis: HalbornPool.sol
$ slither HalbornPool.sol Halborn._balances (MyToken.sol#12) shadows: - ERC20._balances (openzeppelin/contracts/token/ERC20/ERC20.sol#36) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing Halborn.EmergencyDestroy(address) (MyToken.sol#30-33) allows anyone to destruct the contract Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#suicidal DefiPool.deposit(uint256,address) (HalbornPool.sol#82-94) ignores return value by HAL.transferFrom(_sender,address(this),_amount) (HalbornPool.sol#87) DefiPool.withdraw(address) (HalbornPool.sol#100-119) ignores return value by HAL.transfer(_user,userBalance[_user]) (HalbornPool.sol#117) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-transfer Contract locking ether found: Contract DefiPool (HalbornPool.sol#20-134) has payable functions: - HalbornInterface.deposit(uint256,address) (HalbornInterface.sol#6-7) - HalbornInterface.withdraw(address) (HalbornInterface.sol#9-10) - DefiPool.deposit(uint256,address) (HalbornPool.sol#82-94) - DefiPool.withdraw(address) (HalbornPool.sol#100-119) But does not have a function to withdraw the ether Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#contracts-that-lock-ether Reentrancy in DefiPool.withdraw(address) (HalbornPool.sol#100-119): External calls: - HAL.transfer(_user,userBalance[_user]) (HalbornPool.sol#117) State variables written after the call(s): - userBalance[_user] = userBalance[_user] - initialUserBalance (HalbornPool.sol#118) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-1 Reentrancy in DefiPool.deposit(uint256,address) (HalbornPool.sol#82-94): External calls: - HAL.transferFrom(_sender,address(this),_amount) (HalbornPool.sol#87) State variables written after the call(s): - depositStart[msg.sender] = depositStart[msg.sender] + block.timestamp (HalbornPool.sol#91) - userBalance[_sender] = userBalance[_sender] + _amount (HalbornPool.sol#89) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2 Reentrancy in DefiPool.deposit(uint256,address) (HalbornPool.sol#82-94): External calls: - HAL.transferFrom(_sender,address(this),_amount) (HalbornPool.sol#87) Event emitted after the call(s): - Deposit(msg.sender,msg.value,block.timestamp) (HalbornPool.sol#93) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3 Different versions of Solidity is used: - Version used: ['^0.8.0', '^0.8.2'] - ^0.8.2 (HalbornInterface.sol#2) - ^0.8.2 (HalbornPool.sol#2) - ^0.8.2 (MyToken.sol#2) - ^0.8.0 (openzeppelin/contracts/access/Ownable.sol#4) - ^0.8.0 (openzeppelin/contracts/security/Pausable.sol#4) - ^0.8.0 (openzeppelin/contracts/token/ERC20/ERC20.sol#4) - ^0.8.0 (openzeppelin/contracts/token/ERC20/IERC20.sol#4) - ^0.8.0 (openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol#4) - ^0.8.0 (openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol#4) - ^0.8.0 (openzeppelin/contracts/utils/Context.sol#4) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#different-pragma-directives-are-used Context._msgData() (openzeppelin/contracts/utils/Context.sol#21-23) is never used and should be removed Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code Pragma version^0.8.2 (HalbornInterface.sol#2) allows old versions Pragma version^0.8.2 (HalbornPool.sol#2) allows old versions Pragma version^0.8.2 (MyToken.sol#2) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/access/Ownable.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/security/Pausable.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/token/ERC20/ERC20.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/token/ERC20/IERC20.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/utils/Context.sol#4) allows old versions solc-0.8.2 is not recommended for deployment Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity Parameter DefiPool.deposit(uint256,address)._amount (HalbornPool.sol#82) is not in mixedCase Parameter DefiPool.deposit(uint256,address)._sender (HalbornPool.sol#82) is not in mixedCase Parameter DefiPool.withdraw(address)._user (HalbornPool.sol#100) is not in mixedCase Function DefiPool.EmergencyWithraw(address,address,uint256) (HalbornPool.sol#131-133) is not in mixedCase Variable DefiPool.HAL (HalbornPool.sol#44) is not in mixedCase Function Halborn.EmergencyDestroy(address) (MyToken.sol#30-33) is not in mixedCase Parameter Halborn.EmergencyDestroy(address)._to (MyToken.sol#30) is not in mixedCase Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions Halborn.constructor() (MyToken.sol#13-15) uses literals with too many digits: - _mint(msg.sender,10000000000 * 10 ** decimals()) (MyToken.sol#14) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#too-many-digits DefiPool.allowed (HalbornPool.sol#27) is never used in DefiPool (HalbornPool.sol#20-134) Halborn._balances (MyToken.sol#12) is never used in Halborn (MyToken.sol#9-34) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unused-state-variable transfer(address,uint256) should be declared external: - DefiPool.transfer(address,uint256) (HalbornPool.sol#32-37) deposit(uint256,address) should be declared external: - DefiPool.deposit(uint256,address) (HalbornPool.sol#82-94) withdraw(address) should be declared external: - DefiPool.withdraw(address) (HalbornPool.sol#100-119) getContractBalance() should be declared external: - DefiPool.getContractBalance() (HalbornPool.sol#124-129) EmergencyWithraw(address,address,uint256) should be declared external: - DefiPool.EmergencyWithraw(address,address,uint256) (HalbornPool.sol#131-133) pause() should be declared external: - Halborn.pause() (MyToken.sol#16-18) unpause() should be declared external: - Halborn.unpause() (MyToken.sol#20-22) mint(address,uint256) should be declared external: - Halborn.mint(address,uint256) (MyToken.sol#24-26) transferbyOwner(address,address,uint256) should be declared external: - Halborn.transferbyOwner(address,address,uint256) (MyToken.sol#27-29) EmergencyDestroy(address) should be declared external: - Halborn.EmergencyDestroy(address) (MyToken.sol#30-33) renounceOwnership() should be declared external: - Ownable.renounceOwnership() (openzeppelin/contracts/access/Ownable.sol#54-56) transferOwnership(address) should be declared external: - Ownable.transferOwnership(address) (openzeppelin/contracts/access/Ownable.sol#62-65) name() should be declared external: - ERC20.name() (openzeppelin/contracts/token/ERC20/ERC20.sol#62-64) symbol() should be declared external: - ERC20.symbol() (openzeppelin/contracts/token/ERC20/ERC20.sol#70-72) totalSupply() should be declared external: - ERC20.totalSupply() (openzeppelin/contracts/token/ERC20/ERC20.sol#94-96) balanceOf(address) should be declared external: - ERC20.balanceOf(address) (openzeppelin/contracts/token/ERC20/ERC20.sol#101-103) transfer(address,uint256) should be declared external: - ERC20.transfer(address,uint256) (openzeppelin/contracts/token/ERC20/ERC20.sol#113-116) approve(address,uint256) should be declared external: - ERC20.approve(address,uint256) (openzeppelin/contracts/token/ERC20/ERC20.sol#132-135) transferFrom(address,address,uint256) should be declared external: - ERC20.transferFrom(address,address,uint256) (openzeppelin/contracts/token/ERC20/ERC20.sol#150-164) increaseAllowance(address,uint256) should be declared external: - ERC20.increaseAllowance(address,uint256) (openzeppelin/contracts/token/ERC20/ERC20.sol#178-181) decreaseAllowance(address,uint256) should be declared external: - ERC20.decreaseAllowance(address,uint256) (openzeppelin/contracts/token/ERC20/ERC20.sol#197-205) burn(uint256) should be declared external: - ERC20Burnable.burn(uint256) (openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol#20-22) burnFrom(address,uint256) should be declared external: - ERC20Burnable.burnFrom(address,uint256) (openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol#35-42) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external HalbornPool.sol analyzed (11 contracts with 77 detectors), 54 result(s) found |
Static Analysis: MyTokens.sol
$ slither MyToken.sol Halborn._balances (MyToken.sol#12) shadows: - ERC20._balances (openzeppelin/contracts/token/ERC20/ERC20.sol#36) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing Halborn.EmergencyDestroy(address) (MyToken.sol#30-33) allows anyone to destruct the contract Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#suicidal Different versions of Solidity is used: - Version used: ['^0.8.0', '^0.8.2'] - ^0.8.2 (MyToken.sol#2) - ^0.8.0 (openzeppelin/contracts/access/Ownable.sol#4) - ^0.8.0 (openzeppelin/contracts/security/Pausable.sol#4) - ^0.8.0 (openzeppelin/contracts/token/ERC20/ERC20.sol#4) - ^0.8.0 (openzeppelin/contracts/token/ERC20/IERC20.sol#4) - ^0.8.0 (openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol#4) - ^0.8.0 (openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol#4) - ^0.8.0 (openzeppelin/contracts/utils/Context.sol#4) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#different-pragma-directives-are-used Context._msgData() (openzeppelin/contracts/utils/Context.sol#21-23) is never used and should be removed Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code Pragma version^0.8.2 (MyToken.sol#2) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/access/Ownable.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/security/Pausable.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/token/ERC20/ERC20.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/token/ERC20/IERC20.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol#4) allows old versions Pragma version^0.8.0 (openzeppelin/contracts/utils/Context.sol#4) allows old versions solc-0.8.10 is not recommended for deployment Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity Function Halborn.EmergencyDestroy(address) (MyToken.sol#30-33) is not in mixedCase Parameter Halborn.EmergencyDestroy(address)._to (MyToken.sol#30) is not in mixedCase Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions Halborn.constructor() (MyToken.sol#13-15) uses literals with too many digits: - _mint(msg.sender,10000000000 * 10 ** decimals()) (MyToken.sol#14) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#too-many-digits Halborn._balances (MyToken.sol#12) is never used in Halborn (MyToken.sol#9-34) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unused-state-variable pause() should be declared external: - Halborn.pause() (MyToken.sol#16-18) unpause() should be declared external: - Halborn.unpause() (MyToken.sol#20-22) mint(address,uint256) should be declared external: - Halborn.mint(address,uint256) (MyToken.sol#24-26) transferbyOwner(address,address,uint256) should be declared external: - Halborn.transferbyOwner(address,address,uint256) (MyToken.sol#27-29) EmergencyDestroy(address) should be declared external: - Halborn.EmergencyDestroy(address) (MyToken.sol#30-33) renounceOwnership() should be declared external: - Ownable.renounceOwnership() (openzeppelin/contracts/access/Ownable.sol#54-56) transferOwnership(address) should be declared external: - Ownable.transferOwnership(address) (openzeppelin/contracts/access/Ownable.sol#62-65) name() should be declared external: - ERC20.name() (openzeppelin/contracts/token/ERC20/ERC20.sol#62-64) symbol() should be declared external: - ERC20.symbol() (openzeppelin/contracts/token/ERC20/ERC20.sol#70-72) totalSupply() should be declared external: - ERC20.totalSupply() (openzeppelin/contracts/token/ERC20/ERC20.sol#94-96) balanceOf(address) should be declared external: - ERC20.balanceOf(address) (openzeppelin/contracts/token/ERC20/ERC20.sol#101-103) transfer(address,uint256) should be declared external: - ERC20.transfer(address,uint256) (openzeppelin/contracts/token/ERC20/ERC20.sol#113-116) approve(address,uint256) should be declared external: - ERC20.approve(address,uint256) (openzeppelin/contracts/token/ERC20/ERC20.sol#132-135) transferFrom(address,address,uint256) should be declared external: - ERC20.transferFrom(address,address,uint256) (openzeppelin/contracts/token/ERC20/ERC20.sol#150-164) increaseAllowance(address,uint256) should be declared external: - ERC20.increaseAllowance(address,uint256) (openzeppelin/contracts/token/ERC20/ERC20.sol#178-181) decreaseAllowance(address,uint256) should be declared external: - ERC20.decreaseAllowance(address,uint256) (openzeppelin/contracts/token/ERC20/ERC20.sol#197-205) burn(uint256) should be declared external: - ERC20Burnable.burn(uint256) (openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol#20-22) burnFrom(address,uint256) should be declared external: - ERC20Burnable.burnFrom(address,uint256) (openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol#35-42) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external MyToken.sol analyzed (8 contracts with 77 detectors), 35 result(s) found |