-
Notifications
You must be signed in to change notification settings - Fork 996
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for enhanced analyses through code comments (#1089)
* Add support for enhanced analyses through code comments - Parse /// ... on variable declaration - Disable reentrancy for variables marked as 'sec:non-reentrant' - Create a new detector looking for wrong access control ('@Custom:security write-protection="some_func")
- Loading branch information
Showing
10 changed files
with
431 additions
and
1 deletion.
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
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,81 @@ | ||
""" | ||
Module detecting suicidal contract | ||
A suicidal contract is an unprotected function that calls selfdestruct | ||
""" | ||
from typing import List | ||
|
||
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification | ||
from slither.core.declarations import Function, Contract | ||
from slither.utils.output import Output | ||
|
||
|
||
class ProtectedVariables(AbstractDetector): | ||
|
||
ARGUMENT = "protected-vars" | ||
HELP = "Detected unprotected variables" | ||
IMPACT = DetectorClassification.HIGH | ||
CONFIDENCE = DetectorClassification.HIGH | ||
|
||
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#protected-variables" | ||
|
||
WIKI_TITLE = "Protected Variables" | ||
WIKI_DESCRIPTION = "Detect unprotected variable that are marked protected" | ||
|
||
# region wiki_exploit_scenario | ||
WIKI_EXPLOIT_SCENARIO = """ | ||
```solidity | ||
contract Buggy{ | ||
/// @custom:security write-protection="onlyOwner()" | ||
address owner; | ||
function set_protected() public onlyOwner(){ | ||
owner = msg.sender; | ||
} | ||
function set_not_protected() public{ | ||
owner = msg.sender; | ||
} | ||
} | ||
``` | ||
`owner` must be always written by function using `onlyOwner` (`write-protection="onlyOwner()"`), however anyone can call `set_not_protected`. | ||
""" | ||
# endregion wiki_exploit_scenario | ||
|
||
WIKI_RECOMMENDATION = "Add access controls to the vulnerable function" | ||
|
||
def _analyze_function(self, function: Function, contract: Contract) -> List[Output]: | ||
results = [] | ||
|
||
for state_variable_written in function.state_variables_written: | ||
if state_variable_written.write_protection: | ||
for function_sig in state_variable_written.write_protection: | ||
function_protection = contract.get_function_from_signature(function_sig) | ||
if not function_protection: | ||
function_protection = contract.get_modifier_from_signature(function_sig) | ||
if not function_protection: | ||
self.logger.error(f"{function_sig} not found") | ||
continue | ||
if function_protection not in function.all_internal_calls(): | ||
info = [ | ||
function, | ||
" should have ", | ||
function_protection, | ||
" to protect ", | ||
state_variable_written, | ||
"\n", | ||
] | ||
|
||
res = self.generate_result(info) | ||
results.append(res) | ||
return results | ||
|
||
def _detect(self): | ||
"""Detect the suicidal functions""" | ||
results = [] | ||
for contract in self.compilation_unit.contracts_derived: | ||
for function in contract.functions_entry_points: | ||
results += self._analyze_function(function, contract) | ||
|
||
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
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,35 @@ | ||
interface I{ | ||
function external_call() external; | ||
} | ||
|
||
contract ReentrancyAndWrite{ | ||
|
||
/// @custom:security non-reentrant | ||
/// @custom:security write-protection="onlyOwner()" | ||
I external_contract; | ||
|
||
modifier onlyOwner(){ | ||
// lets assume there is an access control | ||
_; | ||
} | ||
|
||
mapping(address => uint) balances; | ||
|
||
function withdraw() public{ | ||
uint balance = balances[msg.sender]; | ||
|
||
external_contract.external_call(); | ||
|
||
balances[msg.sender] = 0; | ||
payable(msg.sender).transfer(balance); | ||
} | ||
|
||
function set_protected() public onlyOwner(){ | ||
external_contract = I(msg.sender); | ||
} | ||
|
||
function set_not_protected() public{ | ||
external_contract = I(msg.sender); | ||
} | ||
} | ||
|
Oops, something went wrong.