-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #71 from pessimistic-io/develop
Slitherin 0.3.0
- Loading branch information
Showing
11 changed files
with
335 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Arbitrary Call | ||
|
||
## Configuration | ||
|
||
- Check: `pess-arbitrary-call` | ||
- Severity: `High` | ||
- Confidence: `Low` | ||
|
||
## Description | ||
|
||
The detector iterates over all low-level calls, checks if the destination or calldata could be tainted(manipulated). | ||
|
||
### Potential Improvement | ||
|
||
Filter out role protected calls, divide detector to multiple detectors with different severity and confidence | ||
|
||
## Vulnerable Scenario | ||
|
||
[test scenarios](../tests/arbitrary_call_test.sol) | ||
|
||
## Recommendation | ||
|
||
Do not allow users to make arbitrary calls. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
{ | ||
"dependencies": { | ||
"@openzeppelin/contracts": "^4.9.2" | ||
"@openzeppelin/contracts": "^4.9.3" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
from collections import namedtuple | ||
from dataclasses import dataclass | ||
from typing import Dict, List, Optional, Tuple, Set | ||
|
||
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification | ||
from slither.slithir.operations import TypeConversion, Operation | ||
from slither.core.declarations import ( | ||
Contract, | ||
SolidityVariableComposed, | ||
FunctionContract, | ||
) | ||
from slither.core.cfg.node import Node | ||
from slither.slithir.operations import LowLevelCall | ||
from slither.analyses.data_dependency.data_dependency import is_dependent, is_tainted | ||
|
||
|
||
# TODO/Possible improvements: | ||
# look for transferFroms if there is any transferFrom and contract contains whole arbitrary call - it is screwed | ||
# Filter out role protected | ||
|
||
|
||
class ArbitraryCall(AbstractDetector): | ||
""" | ||
Detects arbitrary and dangerous calls. | ||
(only detects low-level calls) | ||
""" | ||
|
||
ARGUMENT = "pess-arbitrary-call" # slither will launch the detector with slither.py --detect mydetector | ||
HELP = "someaddress.call(somedata)" | ||
IMPACT = DetectorClassification.HIGH | ||
CONFIDENCE = DetectorClassification.LOW | ||
|
||
WIKI = ( | ||
"https://github.com/pessimistic-io/slitherin/blob/master/docs/arbitrary_call.md" | ||
) | ||
WIKI_TITLE = "Arbitrary calls" | ||
WIKI_DESCRIPTION = "Check docs" | ||
WIKI_EXPLOIT_SCENARIO = "Attacker can manipulate on inputs" | ||
WIKI_RECOMMENDATION = "Do not allow arbitrary calls" | ||
|
||
def analyze_function( | ||
self, function: FunctionContract | ||
) -> List[Tuple[FunctionContract, Node, LowLevelCall, bool, bool]]: | ||
results = [] | ||
for node in function.nodes: | ||
for ir in node.irs: | ||
try: | ||
if isinstance(ir, LowLevelCall): | ||
destination_tainted = is_tainted(ir.destination, node, True) | ||
if ( | ||
destination_tainted | ||
and ir.destination == SolidityVariableComposed("msg.sender") | ||
): | ||
# We don't care about msg.sender, since will be only reentrancy issue | ||
destination_tainted = False | ||
args_tainted = any( | ||
is_tainted(arg, node, True) for arg in ir.arguments | ||
) # seems like ir.arguments = [data] for all low-level calls | ||
if args_tainted or destination_tainted: | ||
results.append( | ||
( | ||
function, | ||
node, | ||
ir, | ||
args_tainted, | ||
destination_tainted, | ||
) | ||
) | ||
|
||
except Exception as e: | ||
print("ArbitraryCall:Failed to check types", e) | ||
print(ir) | ||
|
||
return results | ||
|
||
def analyze_contract(self, contract: Contract): | ||
stores_approve = False | ||
all_tainted_calls: List[ | ||
Tuple[FunctionContract, Node, LowLevelCall, bool, bool] | ||
] = [] | ||
results = [] | ||
for f in contract.functions: | ||
res = self.analyze_function(f) | ||
if res: | ||
all_tainted_calls.extend(res) | ||
|
||
for call_fn, node, ir, args_tainted, destination_tainted in all_tainted_calls: | ||
info = ["Manipulated call found: ", node, " in ", call_fn, "\n"] | ||
if args_tainted and destination_tainted: | ||
text = "Both destination and calldata could be manipulated" | ||
else: | ||
part = "calldata" if args_tainted else "destination" | ||
text = f"Only the {part} could be manipulated" | ||
info += [f"{text}\n"] | ||
|
||
for f in contract.functions: | ||
if f.visibility not in ["external", "public"]: | ||
continue | ||
|
||
fn_taints_args = False | ||
fn_taints_destination = False | ||
|
||
if args_tainted and any( | ||
is_dependent(ir.arguments[0], fn_arg, node) | ||
for fn_arg in f.variables | ||
): | ||
fn_taints_args = True | ||
|
||
if destination_tainted and any( | ||
is_dependent(ir.destination, fn_arg, node) for fn_arg in f.variables | ||
): | ||
fn_taints_destination = True | ||
|
||
if not (fn_taints_args or fn_taints_destination): | ||
continue | ||
|
||
if fn_taints_args and fn_taints_destination: | ||
text = "The call could be fully manipulated (arbitrary call)" | ||
else: | ||
part = "calldata" if fn_taints_args else "destination" | ||
text = f"The {part} could be manipulated" | ||
info += [f"\t{text} through ", f, "\n"] | ||
|
||
res = self.generate_result(info) | ||
res.add(node) | ||
results.append(res) | ||
return results | ||
|
||
def _detect(self): | ||
results = [] | ||
for contract in self.compilation_unit.contracts_derived: | ||
r = self.analyze_contract(contract) | ||
if r: | ||
results.extend(r) | ||
return results |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.