forked from 0xKitsune/solstat
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added: Slither[arbitrary from in transferFrom] rule | 0xKitsune#61
- Loading branch information
1 parent
6aa6c0f
commit 851cb1e
Showing
5 changed files
with
172 additions
and
6 deletions.
There are no files selected for viewing
124 changes: 124 additions & 0 deletions
124
src/analyzer/vulnerabilities/arbitrary_from_in_transferfrom.rs
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,124 @@ | ||
use std::collections::HashSet; | ||
|
||
use solang_parser::pt::{self, Expression, Loc}; | ||
use solang_parser::{self, pt::SourceUnit}; | ||
|
||
use crate::analyzer::ast::{self, Target}; | ||
|
||
pub fn _arbitrary_from_in_transferfrom_vulnerability(source_unit: SourceUnit) -> HashSet<Loc> { | ||
//Create a new hashset that stores the location of each vulnerability target identified | ||
let mut vulnerability_locations: HashSet<Loc> = HashSet::new(); | ||
|
||
//Extract the target nodes from the source_unit | ||
let target_nodes = | ||
ast::extract_target_from_node(Target::FunctionDefinition, source_unit.into()); | ||
|
||
//For each target node that was extracted, check for the vulnerability patterns | ||
for _node in target_nodes { | ||
let contract_part = _node.contract_part().unwrap(); | ||
|
||
if let pt::ContractPart::FunctionDefinition(box_fn_definition) = contract_part { | ||
// if function has no params or no body, it is probably not affected by this vulnerability. | ||
if box_fn_definition.params.is_empty() || box_fn_definition.body.is_none() { | ||
continue; | ||
} | ||
|
||
// We extract the function parameters to later verify if they are used as the first parameter in the `transferFrom/SafeTransferFrom`. | ||
let fn_params = extract_fn_params_as_string(&box_fn_definition); | ||
|
||
if let pt::Statement::Block { | ||
loc: _, | ||
unchecked: _, | ||
statements, | ||
} = box_fn_definition.body.unwrap() | ||
{ | ||
// We loop through each body expression to determine if 'transferFrom/SafeTransferFrom' is used. | ||
for _statement in statements { | ||
if let pt::Statement::Expression(_, box_expression) = _statement.clone() { | ||
if let pt::Expression::FunctionCall( | ||
loc, | ||
box_fn_expression, | ||
fn_call_params, | ||
) = box_expression | ||
{ | ||
// If a call exists and any of the function parameters is used as the first parameter in the call, we mark it as a vulnerability. | ||
if should_be_added(box_fn_expression, &fn_params, &fn_call_params[0]) { | ||
vulnerability_locations.insert(loc); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
//Return the identified vulnerability locations | ||
vulnerability_locations | ||
} | ||
|
||
fn extract_fn_params_as_string(box_fn_definition: &Box<pt::FunctionDefinition>) -> Vec<String> { | ||
return box_fn_definition | ||
.params | ||
.iter() | ||
.filter(|v| v.clone().1.is_some()) | ||
.map(|v| v.clone().1.unwrap().name.unwrap().name) | ||
.collect(); | ||
} | ||
|
||
fn have_transfer_from_expression(box_fn_expression: Box<pt::Expression>) -> bool { | ||
if let pt::Expression::MemberAccess(_, _, member_identifier) = *box_fn_expression { | ||
return vec!["safetransferfrom", "transferfrom"] | ||
.contains(&member_identifier.name.to_lowercase().as_str()); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
fn transfer_from_uses_fn_params_as_from_variable( | ||
fn_params: &Vec<String>, | ||
from_param: &Expression, | ||
) -> bool { | ||
if let pt::Expression::Variable(var_identifier) = from_param { | ||
// If any funtion parameter is used as `from` paramenter | ||
return fn_params.contains(&var_identifier.name); | ||
} | ||
return false; | ||
} | ||
|
||
fn should_be_added( | ||
box_fn_expression: Box<pt::Expression>, | ||
fn_params: &Vec<String>, | ||
from_param: &Expression, | ||
) -> bool { | ||
return have_transfer_from_expression(box_fn_expression) | ||
&& transfer_from_uses_fn_params_as_from_variable(&fn_params, &from_param); | ||
} | ||
|
||
#[test] | ||
fn test_arbitrary_from_in_transferfrom_vulnerability() { | ||
let file_contents = r#" | ||
contract Contract0 { | ||
function a(address from, address to, uint256 amount) public { | ||
erc20.transferFrom(from, to, amount); | ||
} | ||
function b(address to, uint256 amount) public { | ||
erc20.transferFrom(msg.sender, to, amount); | ||
} | ||
function c(address _from, uint256 amount) public { | ||
erc20.transferFrom(_from, msg.sender, amount); | ||
} | ||
function d(address _from, address _to, uint256 amount) public { | ||
erc20.safeTransferFrom(_from, _to, amount); | ||
} | ||
} | ||
"#; | ||
|
||
let source_unit = solang_parser::parse(file_contents, 0).unwrap().0; | ||
|
||
let vulnerability_locations = _arbitrary_from_in_transferfrom_vulnerability(source_unit); | ||
assert_eq!(vulnerability_locations.len(), 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
31 changes: 31 additions & 0 deletions
31
src/report/report_sections/vulnerabilities/arbitrary_from_in_transferfrom.rs
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,31 @@ | ||
pub fn report_section_content() -> String { | ||
String::from( | ||
r##" | ||
## Arbitrary `from` in transferFrom/safeTransferFrom. | ||
Use `msg.sender` as `from` in `transferFrom/safeTransferFrom`. | ||
### Exploit Scenario: | ||
```js | ||
/** | ||
* Alice approves this contract to spend her ERC20 | ||
* tokens. Bob can call a and specify Alice's address as | ||
* the from parameter in transferFrom, allowing him to | ||
* transfer Alice's tokens to himself. | ||
* */ | ||
function withdraw(address from, address to, uint256 amount) public { | ||
erc20.transferFrom(from, to, am); | ||
} | ||
// or | ||
function emergencyWithdraw(address from, address to, uint256 amount) public { | ||
erc20.safeTransferFrom(from, to, am); | ||
} | ||
``` | ||
"##, | ||
) | ||
} |
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