mirror of https://github.com/crytic/slither
commit
05d801fe8f
@ -1,3 +1,9 @@ |
||||
contract A{ |
||||
pragma solidity 0.8.19; |
||||
|
||||
} |
||||
error RevertIt(); |
||||
|
||||
contract Example { |
||||
function reverts() external pure { |
||||
revert RevertIt(); |
||||
} |
||||
} |
@ -1,5 +1,16 @@ |
||||
import "./a.sol"; |
||||
|
||||
contract B is A{ |
||||
pragma solidity 0.8.19; |
||||
|
||||
enum B { |
||||
a, |
||||
b |
||||
} |
||||
|
||||
contract T { |
||||
Example e = new Example(); |
||||
function b() public returns(uint) { |
||||
B b = B.a; |
||||
return 4; |
||||
} |
||||
} |
@ -0,0 +1,104 @@ |
||||
""" |
||||
Module detecting usage of more than one dynamic type in abi.encodePacked() arguments which could lead to collision |
||||
""" |
||||
|
||||
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification |
||||
from slither.core.declarations.solidity_variables import SolidityFunction |
||||
from slither.slithir.operations import SolidityCall |
||||
from slither.analyses.data_dependency.data_dependency import is_tainted |
||||
from slither.core.solidity_types import ElementaryType |
||||
from slither.core.solidity_types import ArrayType |
||||
|
||||
|
||||
def _is_dynamic_type(arg): |
||||
""" |
||||
Args: |
||||
arg (function argument) |
||||
Returns: |
||||
Bool |
||||
""" |
||||
if isinstance(arg.type, ElementaryType) and (arg.type.name in ["string", "bytes"]): |
||||
return True |
||||
if isinstance(arg.type, ArrayType) and arg.type.length is None: |
||||
return True |
||||
|
||||
return False |
||||
|
||||
|
||||
def _detect_abi_encodePacked_collision(contract): |
||||
""" |
||||
Args: |
||||
contract (Contract) |
||||
Returns: |
||||
list((Function), (list (Node))) |
||||
""" |
||||
ret = [] |
||||
# pylint: disable=too-many-nested-blocks |
||||
for f in contract.functions_and_modifiers_declared: |
||||
for n in f.nodes: |
||||
for ir in n.irs: |
||||
if isinstance(ir, SolidityCall) and ir.function == SolidityFunction( |
||||
"abi.encodePacked()" |
||||
): |
||||
dynamic_type_count = 0 |
||||
for arg in ir.arguments: |
||||
if is_tainted(arg, contract) and _is_dynamic_type(arg): |
||||
dynamic_type_count += 1 |
||||
elif dynamic_type_count > 1: |
||||
ret.append((f, n)) |
||||
dynamic_type_count = 0 |
||||
else: |
||||
dynamic_type_count = 0 |
||||
if dynamic_type_count > 1: |
||||
ret.append((f, n)) |
||||
return ret |
||||
|
||||
|
||||
class EncodePackedCollision(AbstractDetector): |
||||
""" |
||||
Detect usage of more than one dynamic type in abi.encodePacked() arguments which could to collision |
||||
""" |
||||
|
||||
ARGUMENT = "encode-packed-collision" |
||||
HELP = "ABI encodePacked Collision" |
||||
IMPACT = DetectorClassification.HIGH |
||||
CONFIDENCE = DetectorClassification.HIGH |
||||
|
||||
WIKI = ( |
||||
"https://github.com/crytic/slither/wiki/Detector-Documentation#abi-encodePacked-collision" |
||||
) |
||||
|
||||
WIKI_TITLE = "ABI encodePacked Collision" |
||||
WIKI_DESCRIPTION = """Detect collision due to dynamic type usages in `abi.encodePacked`""" |
||||
|
||||
WIKI_EXPLOIT_SCENARIO = """ |
||||
```solidity |
||||
contract Sign { |
||||
function get_hash_for_signature(string name, string doc) external returns(bytes32) { |
||||
return keccak256(abi.encodePacked(name, doc)); |
||||
} |
||||
} |
||||
``` |
||||
Bob calls `get_hash_for_signature` with (`bob`, `This is the content`). The hash returned is used as an ID. |
||||
Eve creates a collision with the ID using (`bo`, `bThis is the content`) and compromises the system. |
||||
""" |
||||
WIKI_RECOMMENDATION = """Do not use more than one dynamic type in `abi.encodePacked()` |
||||
(see the [Solidity documentation](https://solidity.readthedocs.io/en/v0.5.10/abi-spec.html?highlight=abi.encodePacked#non-standard-packed-modeDynamic)). |
||||
Use `abi.encode()`, preferably.""" |
||||
|
||||
def _detect(self): |
||||
"""Detect usage of more than one dynamic type in abi.encodePacked(..) arguments which could lead to collision""" |
||||
results = [] |
||||
for c in self.compilation_unit.contracts: |
||||
values = _detect_abi_encodePacked_collision(c) |
||||
for func, node in values: |
||||
info = [ |
||||
func, |
||||
" calls abi.encodePacked() with multiple dynamic arguments:\n\t- ", |
||||
node, |
||||
"\n", |
||||
] |
||||
json = self.generate_result(info) |
||||
results.append(json) |
||||
|
||||
return results |
@ -0,0 +1,15 @@ |
||||
EncodePackedCollision.bad4(bytes,bytes) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#34-36) calls abi.encodePacked() with multiple dynamic arguments: |
||||
- packed = abi.encodePacked(a,a2,a3,a) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#35) |
||||
|
||||
EncodePackedCollision.bad2(string,uint256[]) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#24-26) calls abi.encodePacked() with multiple dynamic arguments: |
||||
- packed = abi.encodePacked(stra,arra) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#25) |
||||
|
||||
EncodePackedCollision.bad3_get_hash_for_signature(string,string) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#29-31) calls abi.encodePacked() with multiple dynamic arguments: |
||||
- keccak256(bytes)(abi.encodePacked(name,doc)) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#30) |
||||
|
||||
EncodePackedCollision.bad0(string,string) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#14-16) calls abi.encodePacked() with multiple dynamic arguments: |
||||
- packed = abi.encodePacked(stra,strb) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#15) |
||||
|
||||
EncodePackedCollision.bad1(string,bytes) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#19-21) calls abi.encodePacked() with multiple dynamic arguments: |
||||
- packed = abi.encodePacked(stra,bytesa) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#20) |
||||
|
@ -1,2 +1,2 @@ |
||||
C.f() (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#3-7) contains an incorrect shift operation: a = 8 >> a (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#5) |
||||
C.f() (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#3-8) contains an incorrect shift operation: a = 8 >> a (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#5) |
||||
|
||||
|
@ -1,4 +1,8 @@ |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18) |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#31) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#22) |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#19) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#23) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#36) |
||||
|
||||
|
@ -1,4 +1,8 @@ |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18) |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#36) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#22) |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#23) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#31) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#19) |
||||
|
||||
|
@ -1,4 +1,8 @@ |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#22) |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#19) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18) |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#23) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#31) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#36) |
||||
|
||||
|
@ -1,4 +1,8 @@ |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#22) |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#31) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18) |
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#23) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#19) |
||||
|
||||
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#36) |
||||
|
||||
|
@ -0,0 +1,78 @@ |
||||
contract ABIencodePacked{ |
||||
|
||||
uint a; |
||||
string str1 = "a"; |
||||
string str2 = "bc"; |
||||
bytes _bytes = "hello world"; |
||||
uint[] arr; |
||||
uint[2] arr2; |
||||
string[3] str_arr3; /* This nested dynamic type is not supported in abi.encodePacked mode by solc */ |
||||
string[] str_array; /* This nested dynamic type is not supported in abi.encodePacked mode by solc */ |
||||
bytes[] bytes_array; /* This nested dynamic type and tuples are not supported in abi.encodePacked mode by solc */ |
||||
|
||||
/* Two dynamic types */ |
||||
function bad0(string calldata stra, string calldata strb) external{ |
||||
bytes memory packed = abi.encodePacked(stra, strb); |
||||
} |
||||
|
||||
/* Two dynamic types */ |
||||
function bad1(string calldata stra, bytes calldata bytesa) external{ |
||||
bytes memory packed = abi.encodePacked(stra, bytesa); |
||||
} |
||||
|
||||
/* Two dynamic types */ |
||||
function bad2(string calldata stra, uint[] calldata arra) external{ |
||||
bytes memory packed = abi.encodePacked(stra, arra); |
||||
} |
||||
|
||||
/* Two dynamic types */ |
||||
function bad3_get_hash_for_signature(string calldata name, string calldata doc) external returns (bytes32) { |
||||
return keccak256(abi.encodePacked(name, doc)); |
||||
} |
||||
|
||||
/* Two dynamic types between non dynamic types */ |
||||
function bad4(bytes calldata a2, bytes calldata a3) external { |
||||
bytes memory packed = abi.encodePacked(a, a2, a3, a); |
||||
} |
||||
|
||||
/* Two dynamic types but static values*/ |
||||
function good0() external{ |
||||
bytes memory packed = abi.encodePacked(str1, str2); |
||||
} |
||||
|
||||
/* Two dynamic types but static values*/ |
||||
function good1() external{ |
||||
bytes memory packed = abi.encodePacked(str1, _bytes); |
||||
} |
||||
|
||||
/* Two dynamic types but static values*/ |
||||
function good2() external{ |
||||
bytes memory packed = abi.encodePacked(str1, arr); |
||||
} |
||||
|
||||
/* No dynamic types */ |
||||
function good3() external{ |
||||
bytes memory packed = abi.encodePacked(a); |
||||
} |
||||
|
||||
/* One dynamic type */ |
||||
function good4() external{ |
||||
bytes memory packed = abi.encodePacked(str1); |
||||
} |
||||
|
||||
/* One dynamic type */ |
||||
function good5() external{ |
||||
bytes memory packed = abi.encodePacked(a, str1); |
||||
} |
||||
|
||||
/* One dynamic type */ |
||||
function good6() external{ |
||||
bytes memory packed = abi.encodePacked(str1, arr2); |
||||
} |
||||
|
||||
/* Two dynamic types but not consecutive*/ |
||||
function good7(string calldata a, uint b, string calldata c) external{ |
||||
bytes memory packed = abi.encodePacked(a, b, c); |
||||
} |
||||
} |
||||
|
Binary file not shown.
Binary file not shown.
@ -1,8 +1,9 @@ |
||||
contract C { |
||||
|
||||
function f() internal returns (uint a) { |
||||
function f() internal returns (uint a, uint b) { |
||||
assembly { |
||||
a := shr(a, 8) |
||||
b := shl(248, 0xff) |
||||
} |
||||
} |
||||
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue