Open source detector on dangerous block.timestamp usage

pull/108/head
Josselin 6 years ago
parent eb1a848283
commit d49ea4475d
  1. 18
      README.md
  2. 1
      scripts/tests_generate_expected_json_4.sh
  3. 1
      scripts/travis_test_4.sh
  4. 1
      scripts/travis_test_5.sh
  5. 4
      slither/__main__.py
  6. 96
      slither/detectors/operations/block_timestamp.py
  7. 1
      tests/expected_json/timestamp.timestamp.json
  8. 21
      tests/timestamp.sol

@ -66,14 +66,16 @@ Num | Detector | What it Detects | Impact | Confidence
11 | `tx-origin` | [Dangerous usage of `tx.origin`](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#dangerous-usage-of-txorigin) | Medium | Medium
12 | `uninitialized-local` | [Uninitialized local variables](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#uninitialized-local-variables) | Medium | Medium
13 | `unused-return` | [Unused return values](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#unused-return) | Medium | Medium
14 | `assembly` | [Assembly usage](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#assembly-usage) | Informational | High
15 | `constable-states` | [State variables that could be declared constant](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#state-variables-that-could-be-declared-constant) | Informational | High
16 | `external-function` | [Public function that could be declared as external](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#public-function-that-could-be-declared-as-external) | Informational | High
17 | `low-level-calls` | [Low level calls](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#low-level-calls) | Informational | High
18 | `naming-convention` | [Conformance to Solidity naming conventions](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#conformance-to-solidity-naming-conventions) | Informational | High
19 | `pragma` | [If different pragma directives are used](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#different-pragma-directives-are-used) | Informational | High
20 | `solc-version` | [Old versions of Solidity (< 0.4.23)](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#old-versions-of-solidity) | Informational | High
21 | `unused-state` | [Unused state variables](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#unused-state-variables) | Informational | High
14 | `timestamp` | [Dangerous usage of `block.timestamp`](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#block-timestamp) | Low | Medium
15 | `assembly` | [Assembly usage](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#assembly-usage) | Informational | High
16 | `constable-states` | [State variables that could be declared constant](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#state-variables-that-could-be-declared-constant) | Informational | High
17 | `external-function` | [Public function that could be declared as external](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#public-function-that-could-be-declared-as-external) | Informational | High
18 | `low-level-calls` | [Low level calls](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#low-level-calls) | Informational | High
19 | `naming-convention` | [Conformance to Solidity naming conventions](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#conformance-to-solidity-naming-conventions) | Informational | High
20 | `pragma` | [If different pragma directives are used](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#different-pragma-directives-are-used) | Informational | High
21 | `solc-version` | [Old versions of Solidity (< 0.4.23)](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#old-versions-of-solidity) | Informational | High
22 | `unused-state` | [Unused state variables](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#unused-state-variables) | Informational | High
[Contact us](https://www.trailofbits.com/contact/) to get access to additional detectors.

@ -37,3 +37,4 @@ generate_expected_json tests/constant.sol "constant-function"
generate_expected_json tests/unused_return.sol "unused-return"
generate_expected_json tests/shadowing_state_variable.sol "shadowing-state"
generate_expected_json tests/shadowing_abstract.sol "shadowing-abstract"
generate_expected_json tests/timestamp.sol "timestamp"

@ -88,6 +88,7 @@ test_slither tests/constant.sol "constant-function"
test_slither tests/unused_return.sol "unused-return"
test_slither tests/shadowing_abstract.sol "shadowing-abstract"
test_slither tests/shadowing_state_variable.sol "shadowing-state"
test_slither tests/timestamp.sol "timestamp"

@ -84,6 +84,7 @@ test_slither tests/naming_convention.sol "naming-convention"
test_slither tests/controlled_delegatecall.sol "controlled-delegatecall"
test_slither tests/constant-0.5.1.sol "constant-function"
test_slither tests/unused_return.sol "unused-return"
test_slither tests/timestamp.sol "timestamp"
### Test scripts

@ -131,6 +131,7 @@ def get_detectors_and_printers():
from slither.detectors.attributes.const_functions import ConstantFunctions
from slither.detectors.shadowing.abstract import ShadowingAbstractDetection
from slither.detectors.shadowing.state import StateShadowing
from slither.detectors.operations.block_timestamp import Timestamp
detectors = [Backdoor,
UninitializedStateVarsDetection,
@ -154,7 +155,8 @@ def get_detectors_and_printers():
ControlledDelegateCall,
ConstantFunctions,
ShadowingAbstractDetection,
StateShadowing]
StateShadowing,
Timestamp]
from slither.printers.summary.function import FunctionSummary
from slither.printers.summary.contract import ContractSummary

@ -0,0 +1,96 @@
"""
Module detecting send to arbitrary address
To avoid FP, it does not report:
- If msg.sender is used as index (withdraw situation)
- If the function is protected
- If the value sent is msg.value (repay situation)
- If there is a call to transferFrom
TODO: dont report if the value is tainted by msg.value
"""
from slither.core.declarations import Function
from slither.analyses.taint.all_variables import is_tainted as is_tainted_from_inputs
from slither.analyses.taint.specific_variable import is_tainted
from slither.analyses.taint.specific_variable import \
run_taint as run_taint_variable
from slither.core.declarations.solidity_variables import (SolidityFunction,
SolidityVariableComposed)
from slither.detectors.abstract_detector import (AbstractDetector,
DetectorClassification)
from slither.slithir.operations import Binary, BinaryType
class Timestamp(AbstractDetector):
"""
"""
ARGUMENT = 'timestamp'
HELP = 'Dangerous usage of `block.timestamp`'
IMPACT = DetectorClassification.LOW
CONFIDENCE = DetectorClassification.MEDIUM
WIKI = 'https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#block-timestamp'
def timestamp(self, func):
"""
"""
ret = set()
for node in func.nodes:
if node.contains_require_or_assert():
for var in node.variables_read:
if is_tainted(var, SolidityVariableComposed('block.timestamp')):
ret.add(node)
for ir in node.irs:
if isinstance(ir, Binary) and BinaryType.return_bool(ir.type):
for var in ir.read:
if is_tainted(var, SolidityVariableComposed('block.timestamp')):
ret.add(node)
return list(ret)
def detect_dangerous_timestamp(self, contract):
"""
Args:
contract (Contract)
Returns:
list((Function), (list (Node)))
"""
ret = []
for f in [f for f in contract.functions if f.contract == contract]:
nodes = self.timestamp(f)
if nodes:
ret.append((f, nodes))
return ret
def detect(self):
"""
"""
results = []
# Taint block.timestamp
taint = SolidityVariableComposed('block.timestamp')
run_taint_variable(self.slither, taint)
for c in self.contracts:
dangerous_timestamp = self.detect_dangerous_timestamp(c)
for (func, nodes) in dangerous_timestamp:
info = "{}.{} ({}) uses timestamp for comparisons\n"
info = info.format(func.contract.name,
func.name,
func.source_mapping_str)
info += '\tDangerous comparisons:\n'
for node in nodes:
info += '\t- {} ({})\n'.format(node.expression, node.source_mapping_str)
self.log(info)
json = self.generate_json_result(info)
self.add_function_to_json(func, json)
self.add_nodes_to_json(nodes, json)
results.append(json)
return results

@ -0,0 +1 @@
[{"check": "timestamp", "impact": "Low", "confidence": "Medium", "description": "Timestamp.bad0 (tests/timestamp.sol#4-6) uses timestamp for comparisons\n\tDangerous comparisons:\n\t- require(bool)(block.timestamp == 0) (tests/timestamp.sol#5)\n", "function": {"name": "bad0", "source_mapping": {"start": 47, "length": 70, "filename": "tests/timestamp.sol", "lines": [4, 5, 6]}, "contract": {"name": "Timestamp", "source_mapping": {"start": 0, "length": 402, "filename": "tests/timestamp.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]}}}, "expressions": [{"expression": "require(bool)(block.timestamp == 0)", "source_mapping": {"start": 81, "length": 29, "filename": "tests/timestamp.sol", "lines": [5]}}]}, {"check": "timestamp", "impact": "Low", "confidence": "Medium", "description": "Timestamp.bad1 (tests/timestamp.sol#8-11) uses timestamp for comparisons\n\tDangerous comparisons:\n\t- require(bool)(time == 0) (tests/timestamp.sol#10)\n", "function": {"name": "bad1", "source_mapping": {"start": 126, "length": 96, "filename": "tests/timestamp.sol", "lines": [8, 9, 10, 11]}, "contract": {"name": "Timestamp", "source_mapping": {"start": 0, "length": 402, "filename": "tests/timestamp.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]}}}, "expressions": [{"expression": "require(bool)(time == 0)", "source_mapping": {"start": 197, "length": 18, "filename": "tests/timestamp.sol", "lines": [10]}}]}, {"check": "timestamp", "impact": "Low", "confidence": "Medium", "description": "Timestamp.bad2 (tests/timestamp.sol#13-15) uses timestamp for comparisons\n\tDangerous comparisons:\n\t- block.timestamp > 0 (tests/timestamp.sol#14)\n", "function": {"name": "bad2", "source_mapping": {"start": 231, "length": 79, "filename": "tests/timestamp.sol", "lines": [13, 14, 15]}, "contract": {"name": "Timestamp", "source_mapping": {"start": 0, "length": 402, "filename": "tests/timestamp.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]}}}, "expressions": [{"expression": "block.timestamp > 0", "source_mapping": {"start": 279, "length": 24, "filename": "tests/timestamp.sol", "lines": [14]}}]}]

@ -0,0 +1,21 @@
contract Timestamp{
event Time(uint);
function bad0() external{
require(block.timestamp == 0);
}
function bad1() external{
uint time = block.timestamp;
require(time == 0);
}
function bad2() external returns(bool){
return block.timestamp>0;
}
function good() external returns(uint){
emit Time(block.timestamp);
}
}
Loading…
Cancel
Save