Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
cwe_checker
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
fact-depend
cwe_checker
Commits
434d0727
Unverified
Commit
434d0727
authored
Dec 14, 2020
by
Enkelmann
Committed by
GitHub
Dec 14, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
reimplement CWE 367 in Rust for the Ghidra backend (#117)
parent
0068784d
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
194 additions
and
2 deletions
+194
-2
main.rs
caller/src/main.rs
+1
-1
checkers.rs
cwe_checker_rs/src/checkers.rs
+1
-0
cwe_190.rs
cwe_checker_rs/src/checkers/cwe_190.rs
+1
-1
cwe_367.rs
cwe_checker_rs/src/checkers/cwe_367.rs
+167
-0
lib.rs
cwe_checker_rs/src/lib.rs
+1
-0
lib.rs
test/src/lib.rs
+23
-0
No files found.
caller/src/main.rs
View file @
434d0727
...
...
@@ -143,7 +143,7 @@ fn run_with_ghidra(args: CmdlineArgs) {
let
pointer_inference_results
=
if
modules
.iter
()
.any
(|
module
|
module
.name
==
"CWE476"
||
module
.name
==
"Memory"
)
.any
(|
module
|
module
.name
==
"CWE476"
||
module
.name
==
"Memory"
||
module
.name
==
"CWE367"
)
{
Some
(
analysis_results
.compute_pointer_inference
(
&
config
[
"Memory"
]))
}
else
{
...
...
cwe_checker_rs/src/checkers.rs
View file @
434d0727
pub
mod
cwe_190
;
pub
mod
cwe_332
;
pub
mod
cwe_367
;
pub
mod
cwe_426
;
pub
mod
cwe_467
;
pub
mod
cwe_476
;
...
...
cwe_checker_rs/src/checkers/cwe_190.rs
View file @
434d0727
...
...
@@ -93,7 +93,7 @@ fn generate_cwe_warning(callsite: &Tid, called_symbol: &ExternSymbol) -> CweWarn
))
.tids
(
vec!
[
format!
(
"{}"
,
callsite
)])
.addresses
(
vec!
[
callsite
.address
.clone
()])
.symbols
(
vec!
(
called_symbol
.name
.clone
())
)
.symbols
(
vec!
[
called_symbol
.name
.clone
()]
)
}
/// Run the CWE check.
...
...
cwe_checker_rs/src/checkers/cwe_367.rs
0 → 100644
View file @
434d0727
//! This module implements a check for CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition.
//!
//! Time-of-check Time-of-use race conditions happen when a property of a resource
//! (e.g. access rights of a file) get checked before the resource is accessed, leaving
//! a short time window for an attacker to change the entity and thus invalidating
//! the check before the access.
//!
//! See <https://cwe.mitre.org/data/definitions/367.html> for a detailed description.
//!
//! ## How the check works
//!
//! For pairs of (check-call, use-call), configurable in config.json, we check whether
//! a function may call the check-call before the use-call.
//!
//! ## False Positives
//!
//! - The check-call and the use-call may access different, unrelated resources
//! (e. g. different files).
//!
//! ## False Negatives
//!
//! - If the check-call and the use-call happen in different functions it will not
//! be found by the check.
use
crate
::
analysis
::
graph
::{
Edge
,
Graph
,
Node
};
use
crate
::
intermediate_representation
::
Jmp
;
use
crate
::
prelude
::
*
;
use
crate
::
utils
::
log
::{
CweWarning
,
LogMessage
};
use
crate
::
CweModule
;
use
petgraph
::
graph
::
NodeIndex
;
use
petgraph
::
visit
::
EdgeRef
;
use
std
::
collections
::{
HashMap
,
HashSet
};
pub
static
CWE_MODULE
:
CweModule
=
CweModule
{
name
:
"CWE367"
,
version
:
"0.1"
,
run
:
check_cwe
,
};
/// The configuration struct contains pairs of the form `(source_symbol, sink_symbol)`.
/// The `source_symbol` corresponds to a check-call and the `sink_symbol` corresponds to a use-call.
/// An execution path from a source call to a sink call corresponds to a possible Time-of-check Time-of-use Race Condition.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
struct
Config
{
pairs
:
Vec
<
(
String
,
String
)
>
,
}
/// Check whether a call to the `sink_symbol` is reachable from the `source_node`
/// through a path of intraprocedural edges in the control flow graph.
///
/// A simple depth-first-search on the graph is used to find such a path.
fn
is_reachable
(
graph
:
&
Graph
,
source_node
:
NodeIndex
,
source_symbol
:
&
Tid
,
sink_symbol
:
&
Tid
,
)
->
Option
<
(
Tid
,
Tid
)
>
{
let
mut
visited_nodes
=
HashSet
::
new
();
visited_nodes
.insert
(
source_node
);
let
mut
worklist
=
vec!
[
source_node
];
while
let
Some
(
node
)
=
worklist
.pop
()
{
for
edge
in
graph
.edges
(
node
)
{
if
let
Edge
::
ExternCallStub
(
jmp
)
=
edge
.weight
()
{
if
let
Jmp
::
Call
{
target
,
..
}
=
&
jmp
.term
{
if
target
==
sink_symbol
{
// We found a CWE hit
let
source_tid
=
graph
[
source_node
]
.get_block
()
.tid
.clone
();
return
Some
((
source_tid
,
jmp
.tid
.clone
()));
}
else
if
target
==
source_symbol
{
// Do not search past another source call,
// since subsequent sink calls probably belong to the new source.
continue
;
}
}
}
// Add the target node to the worklist if it was not already visited
// and as long as the edge does not leave the function.
match
edge
.weight
()
{
Edge
::
Block
|
Edge
::
CRCallStub
|
Edge
::
CRCombine
(
_
)
|
Edge
::
Jump
(
_
,
_
)
|
Edge
::
ExternCallStub
(
_
)
=>
{
if
visited_nodes
.get
(
&
edge
.target
())
.is_none
()
{
visited_nodes
.insert
(
edge
.target
());
worklist
.push
(
edge
.target
())
}
}
Edge
::
Call
(
_
)
|
Edge
::
CRReturnStub
=>
(),
// These edges would leave the function control flow graph.
}
}
}
None
}
/// Generate a CWE warning for a found CWE hit.
fn
generate_cwe_warning
(
source
:
&
str
,
sink
:
&
str
,
source_callsite
:
Tid
,
sink_callsite
:
Tid
,
sub_name
:
&
str
,
)
->
CweWarning
{
CweWarning
::
new
(
CWE_MODULE
.name
,
CWE_MODULE
.version
,
format!
(
"(Time-of-check Time-of-use Race Condition) '{}' is reachable from '{}' at {} ({}). This could lead to a TOCTOU."
,
sink
,
source
,
sink_callsite
.address
,
sub_name
))
.tids
(
vec!
[
format!
(
"{}"
,
source_callsite
),
format!
(
"{}"
,
sink_callsite
)])
.addresses
(
vec!
[
source_callsite
.address
,
sink_callsite
.address
])
.symbols
(
vec!
[
source
.into
(),
sink
.into
()])
}
/// Run the check. See the module-level documentation for more information.
pub
fn
check_cwe
(
analysis_results
:
&
AnalysisResults
,
cwe_params
:
&
serde_json
::
Value
,
)
->
(
Vec
<
LogMessage
>
,
Vec
<
CweWarning
>
)
{
let
config
:
Config
=
serde_json
::
from_value
(
cwe_params
.clone
())
.unwrap
();
let
project
=
analysis_results
.project
;
let
graph
=
analysis_results
.pointer_inference
.unwrap
()
.get_graph
();
let
mut
cwe_warnings
=
Vec
::
new
();
let
symbol_map
:
HashMap
<&
str
,
Tid
>
=
project
.program
.term
.extern_symbols
.iter
()
.map
(|
symbol
|
(
symbol
.name
.as_str
(),
symbol
.tid
.clone
()))
.collect
();
for
(
source
,
sink
)
in
config
.pairs
{
if
let
(
Some
(
source_tid
),
Some
(
sink_tid
))
=
(
symbol_map
.get
(
source
.as_str
()),
symbol_map
.get
(
sink
.as_str
()),
)
{
for
edge
in
graph
.edge_references
()
{
if
let
Edge
::
ExternCallStub
(
jmp
)
=
edge
.weight
()
{
if
let
Jmp
::
Call
{
target
,
..
}
=
&
jmp
.term
{
if
target
==
source_tid
{
if
let
Some
((
source_callsite
,
sink_callsite
))
=
is_reachable
(
graph
,
edge
.target
(),
target
,
sink_tid
)
{
let
sub_name
=
match
graph
[
edge
.target
()]
{
Node
::
BlkStart
(
_blk
,
sub
)
=>
sub
.term.name
.as_str
(),
_
=>
panic!
(
"Malformed control flow graph."
),
};
cwe_warnings
.push
(
generate_cwe_warning
(
source
.as_str
(),
sink
.as_str
(),
source_callsite
,
sink_callsite
,
sub_name
,
));
}
}
}
}
}
}
}
(
Vec
::
new
(),
cwe_warnings
)
}
cwe_checker_rs/src/lib.rs
View file @
434d0727
...
...
@@ -56,6 +56,7 @@ pub fn get_modules() -> Vec<&'static CweModule> {
vec!
[
&
crate
::
checkers
::
cwe_190
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_332
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_367
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_426
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_467
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_476
::
CWE_MODULE
,
...
...
test/src/lib.rs
View file @
434d0727
...
...
@@ -223,6 +223,29 @@ mod tests {
#[test]
#[ignore]
fn
cwe_367
()
{
let
mut
error_log
=
Vec
::
new
();
let
mut
tests
=
all_test_cases
(
"cwe_367"
,
"CWE367"
);
mark_architecture_skipped
(
&
mut
tests
,
"ppc64"
);
// Ghidra generates mangled function names here for some reason.
mark_architecture_skipped
(
&
mut
tests
,
"ppc64le"
);
// Ghidra generates mangled function names here for some reason.
mark_skipped
(
&
mut
tests
,
"x86"
,
"mingw32-gcc"
);
// Symbol names are prefixed with an underscore in the Ghidra output.
for
test_case
in
tests
{
let
num_expected_occurences
=
1
;
if
let
Err
(
error
)
=
test_case
.run_test
(
"[CWE367]"
,
num_expected_occurences
)
{
error_log
.push
((
test_case
.get_filepath
(),
error
));
}
}
if
!
error_log
.is_empty
()
{
print_errors
(
error_log
);
panic!
();
}
}
#[test]
#[ignore]
fn
cwe_415
()
{
let
mut
error_log
=
Vec
::
new
();
let
mut
tests
=
all_test_cases
(
"cwe_415"
,
"Memory"
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment