Commit 75d6c2b3 by Thomas Barabosch Committed by Enkelmann

Check path (#31)

adds check_path flag to cwe_checker for finding paths from user input functions to CWE hits.
parent 56bf497a
......@@ -7,6 +7,7 @@ dev
- Refactoring of logging and JSON support via --json (PR #30)
- Added file output support via --out (PR #30)
- Surpress logging of info, error and warning to STDOUT via --no-logging (PR #32)
- Added check-path feature via --check-path that searches paths between interesting input functions and cwe hits (PR #31)
0.2 (2019-06-25)
=====
......
......@@ -22,6 +22,20 @@ colors = {'CWE190': YELLOW,
'CWE787': RED,
}
class CheckPath(object):
def __init__(self, source, source_addr, destination, destination_addr, path_str):
self.source = source
self.source_addr = self.__fix_address(source_addr)
self.destination = self.__fix_address(destination)
self.destination_addr = self.__fix_address(destination_addr)
self.path_str = self.__fix_address(path_str)
self.color = None
self.highlight = False
@staticmethod
def __fix_address(address):
return address.replace(':32u', '').replace(':64u', '')
class CweWarning(object):
......@@ -57,7 +71,20 @@ class Parser(object):
return result
@staticmethod
def _parse_check_path(j):
result = []
if 'check_path' in j:
for p in j['check_path']:
check_path = CheckPath(p['source'], p['source_addr'], p['destination'], p['destination_addr'], p['path_str'])
result.append(check_path)
return result
def parse(self):
with open(self._result_path) as fhandle:
j = json.load(fhandle)
return self._parse_cwe_warnings(j)
warnings = self._parse_cwe_warnings(j)
check_path = self._parse_check_path(j)
return warnings + check_path
from CweCheckerParser import CweWarning
class IdaGenerator(object):
def __init__(self, results):
......@@ -6,11 +8,17 @@ class IdaGenerator(object):
def generate(self):
script = "import sark\nimport idaapi\n"
for res in self._results:
if res.highlight and res.address:
first_address = res.address[0]
script += "sark.Line(%s).color = %s\n" % (first_address, res.color)
script += "sark.Line(%s).comments.regular = '%s'\n" % (first_address, res.description)
script += "print('[ %s ] %s')\n" % (first_address, res.description)
if isinstance(res, CweWarning):
if res.highlight and res.address:
first_address = res.address[0]
script += "sark.Line(%s).color = %s\n" % (first_address, res.color)
script += "sark.Line(%s).comments.regular = '%s'\n" % (first_address, res.description)
script += "print('[ %s ] %s')\n" % (first_address, res.description)
else:
script += "print('[ GENERAL ] %s')\n" % res.description
else:
script += "print('[ GENERAL ] %s')\n" % res.description
script += "print('[CheckPath] %s ( %s ) -> %s via %s')\n" % (res.source,
res.source_addr,
res.destination,
res.path_str)
return script
......@@ -35,6 +35,8 @@ If you want to print the output to a file with [--cwe-checker-out], you also nee
All command line options have to be prefixed with [--cwe-checker] (so that BAP knows to forward them to the {i cwe_checker} plugin).
The available command line options are:
- [-check-path] Find paths between input functions (configurable in the configuration file) and CWE hits.
Should be used together with the [-partial] command line option if you are only interested in paths to specific CWEs.
- [-config=[FILE]] Use [[FILE]] as the configuration file.
If you omit this option, {i cwe_checker} uses a standard configuration file located at [src/config.json].
- [-module-versions] Prints the version numbers of each check.
......
......@@ -74,8 +74,8 @@ let full_run project config =
List.iter known_modules ~f:(fun cwe -> execute_cwe_module cwe json program project tid_address_map)
end
let main config module_versions partial_update json_output file_output no_logging project =
let main config module_versions partial_update check_path json_output file_output no_logging project =
if no_logging then
begin
Log_utils.turn_off_logging ()
......@@ -105,14 +105,23 @@ let main config module_versions partial_update json_output file_output no_loggin
full_run project config
else
partial_run project config partial_update;
if check_path then
begin
let prog = Project.program project in
let tid_address_map = Address_translation.generate_tid_map prog in
let json = Yojson.Basic.from_file config in
let check_path_sources = Json_utils.get_symbols_from_json json "check_path" in
let check_path_sinks = Log_utils.get_cwe_warnings () in
Check_path.check_path prog tid_address_map check_path_sources check_path_sinks
end;
if json_output then
begin
match Project.get project filename with
| Some fname -> Log_utils.emit_cwe_warnings_json fname file_output
| None -> Log_utils.emit_cwe_warnings_json "" file_output
| Some fname -> Log_utils.emit_json fname file_output
| None -> Log_utils.emit_json "" file_output
end
else
Log_utils.emit_cwe_warnings_native file_output
Log_utils.emit_native file_output
end
end
......@@ -123,8 +132,9 @@ module Cmdline = struct
let json_output = flag "json" ~doc:"Outputs the result as JSON."
let file_output = param string "out" ~doc:"Path to output file."
let no_logging = flag "no-logging" ~doc:"Outputs no logging (info, error, warning). This does not pollute STDOUT when output json to it."
let check_path = flag "check-path" ~doc:"Checks if there is a path from an input function to a CWE hit."
let partial_update = param string "partial" ~doc:"Comma separated list of modules to apply on binary, e.g. 'CWE332,CWE476,CWE782'"
let () = when_ready (fun ({get=(!!)}) -> Project.register_pass' ~deps:["callsites"] (main !!config !!module_versions !!partial_update !!json_output !!file_output !!no_logging))
let () = when_ready (fun ({get=(!!)}) -> Project.register_pass' ~deps:["callsites"] (main !!config !!module_versions !!partial_update !!check_path !!json_output !!file_output !!no_logging))
let () = manpage [
`S "DESCRIPTION";
`P "This plugin checks various CWEs such as Insufficient Entropy in PRNG (CWE-332) or Use of Potentially Dangerous Function (CWE-676)"
......
......@@ -140,11 +140,11 @@ let main json_output file_output proj =
if json_output then
begin
match Project.get proj filename with
| Some fname -> Log_utils.emit_cwe_warnings_json fname file_output
| None -> Log_utils.emit_cwe_warnings_json "" file_output
| Some fname -> Log_utils.emit_json fname file_output
| None -> Log_utils.emit_json "" file_output
end
else
Log_utils.emit_cwe_warnings_native file_output
Log_utils.emit_native file_output
module Cmdline = struct
open Config
......
......@@ -12,11 +12,11 @@ let main json_output file_output project =
if json_output then
begin
match Project.get project filename with
| Some fname -> Log_utils.emit_cwe_warnings_json fname file_output
| None -> Log_utils.emit_cwe_warnings_json "" file_output
| Some fname -> Log_utils.emit_json fname file_output
| None -> Log_utils.emit_json "" file_output
end
else
Log_utils.emit_cwe_warnings_native file_output
Log_utils.emit_native file_output
module Cmdline = struct
open Config
......
open Core_kernel
open Bap.Std
open Graphlib.Std
open Format
include Self()
module CG = Graphs.Callgraph
module CFG = Graphs.Tid
type proof =
| Calls of CG.edge path
| Sites of CFG.edge path
(** Taken from https://stackoverflow.com/questions/8373460/substring-check-in-ocaml *)
let contains_substring search target =
String.substr_index ~pattern:search target <> None
let format_path get_source get_destination path tid_map =
let e_count = List.length (Seq.to_list (Path.edges path)) in
if e_count = 0 then "()" else
begin
let format_node n = sprintf "%s" (Address_translation.translate_tid_to_assembler_address_string n tid_map) in
let formated_start_node = format_node (get_source (Path.start path)) in
let formated_rest_nodes = List.map (Seq.to_list @@ Path.edges path) ~f:(fun e -> format_node (get_destination e)) in
let formated_full_path = "(" ^ formated_start_node ^ ", " ^ (String.concat ~sep:", " formated_rest_nodes) ^ ")" in
formated_full_path
end
let find_subfunction_name program name =
Term.enum sub_t program
|> Seq.find_map ~f:(fun s -> Option.some_if (contains_substring name (Sub.name s)) (Term.tid s))
let get_tids_from_cwe_hit (cwe_hit: Log_utils.CweWarning.t) =
cwe_hit.tids
let reaches cg callee target =
Graphlib.is_reachable (module CG) cg callee target
(* ignores indirect calls and jumps as well as return statements and interupts *)
let callsites cg target sub =
Term.enum blk_t sub |>
Seq.concat_map ~f:(fun blk ->
Term.enum jmp_t blk |> Seq.filter_map ~f:(fun j ->
match Jmp.kind j with
| Goto _ | Ret _ | Int (_,_) -> None
| Call destination -> begin match Call.target destination with
| Direct tid when reaches cg tid target -> Some (Term.tid blk)
| _ -> None
end))
let verify source destination program : proof option =
let cg = Program.to_graph program in
match Graphlib.shortest_path (module CG) cg source destination with
| Some path -> Some (Calls path)
| None ->
Term.enum sub_t program |> Seq.find_map ~f:(fun sub ->
let g = Sub.to_graph sub in
Seq.find_map (callsites cg source sub) ~f:(fun sc ->
Seq.find_map (callsites cg destination sub) ~f:(fun dc ->
if Tid.equal sc dc then None
else Graphlib.shortest_path (module CFG) g sc dc))) |>
Option.map ~f:(fun p -> Sites p)
let get_fst_tid_from_cwe_hit (cwe_hit: Log_utils.CweWarning.t) =
match cwe_hit.tids with
| [] -> None
| hd :: _ -> Some (Bap.Std.Tid.from_string_exn hd)
let cwe_hit_fst_addr cwe_hit =
match get_tids_from_cwe_hit cwe_hit with
| [] -> Bap.Std.Tid.from_string_exn "0x00"
| hd :: _ -> Bap.Std.Tid.from_string_exn hd
let block_has_callsite blk t =
Term.enum jmp_t blk |>
Seq.exists ~f:(fun j ->
match Jmp.kind j with
| Goto _ | Ret _ | Int (_,_) -> false
| Call destination -> begin match Call.target destination with
| Direct tid -> tid = t
| _ -> false
end)
let collect_callsites program t =
Term.enum sub_t program
|> Seq.filter_map ~f:(fun s -> if Term.enum blk_t s |>
Seq.exists ~f:(fun b -> block_has_callsite b t) then Some s else None)
|> Seq.map ~f:(fun s -> Term.tid s)
let sub_has_tid sub tid =
Term.enum blk_t sub
|> Seq.exists ~f:(fun blk -> Term.tid blk = tid || Blk.elts blk
|> Seq.exists ~f:(fun e -> match e with
| `Def d -> Term.tid d = tid
| `Jmp j -> Term.tid j = tid
| `Phi p -> Term.tid p = tid ))
let find_sub_tid_of_term_tid program tid =
match tid with
| Some t -> let s = Term.enum sub_t program
|> Seq.find ~f:(fun s -> sub_has_tid s t) in
begin
match s with
| Some f -> Some (Term.tid f)
| None -> None
end
| None -> None
let log_path p source source_tid destination tid_map =
let source_addr = Address_translation.translate_tid_to_assembler_address_string source_tid tid_map in
let destination_addr = Address_translation.translate_tid_to_assembler_address_string
(cwe_hit_fst_addr destination) tid_map in
begin match p with
| Calls p ->
let path_str = format_path CG.Edge.src CG.Edge.dst p tid_map in
let current_path = Log_utils.check_path_factory source source_addr destination_addr destination_addr ~path:[] ~path_str:path_str in
Log_utils.collect_check_path current_path
| Sites p -> let path_str = format_path CFG.Edge.src CFG.Edge.dst p tid_map in
let current_path = Log_utils.check_path_factory source source_addr destination_addr destination_addr ~path:[] ~path_str:path_str in
Log_utils.collect_check_path current_path
end
let verify_one program tid_map source destination source_tid destination_tid =
match verify source_tid destination_tid program with
| None -> ()
| Some p -> log_path p source source_tid destination tid_map
let find_source_sink_pathes source destination program tid_map =
match Option.both (find_subfunction_name program source) (find_sub_tid_of_term_tid program (get_fst_tid_from_cwe_hit destination)) with
| None -> () (*one or both functions are not utilized.*)
| Some (callsite_tid, destination_tid) ->
begin
collect_callsites program callsite_tid
|> Seq.iter ~f:(fun source_tid -> verify_one program tid_map source destination source_tid destination_tid )
end
let check_path prog tid_map input_functions cwe_hits =
List.iter input_functions ~f:(fun f ->
List.iter cwe_hits ~f:(fun h -> find_source_sink_pathes f h prog tid_map))
(**
This analyzer module checks if there exists a path from an input function (e.g. recv or scanf) to a location of a CWE hit. The existence of such a path is great news: we might trigger the CWE hit via user provided input. This helps analysts to further priotize the CWE hits.
The analysis prooves that there is a path that fulfills the following constraints:
- it starts at the location of an input function
- it ends at a CWE hit
This module is loosely based on the BAP tutorial (https://github.com/BinaryAnalysisPlatform/bap-tutorial/blob/master/src/path_check/path_check.ml).
*)
val name : string
val version : string
val check_path : Bap.Std.program Bap.Std.term -> Bap.Std.word Bap.Std.Tid.Map.t -> string list -> Log_utils.CweWarning.t list -> unit
......@@ -21,10 +21,11 @@ let contains_multiplication d =
let check_multiplication_before_symbol _proj _prog _sub blk jmp tid_map symbols =
Seq.iter (Term.enum def_t blk)
~f:(fun d -> if contains_multiplication d then
let address = (Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map) in
let symbol = (Symbol_utils.get_symbol_name_from_jmp jmp symbols) in
let description = sprintf "(Integer Overflow or Wraparound) Potential overflow due to multiplication at %s (%s)" address symbol in
let cwe_warning = cwe_warning_factory name version description ~addresses:[address] ~symbols:[symbol] in
let description = "(Integer Overflow or Wraparound) Potential overflow due to multiplication" in
let addresses = [(Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map)] in
let tids = [Address_translation.tid_to_string @@ Term.tid blk] in
let symbols = [(Symbol_utils.get_symbol_name_from_jmp jmp symbols)] in
let cwe_warning = cwe_warning_factory name version description ~addresses ~tids ~symbols in
collect_cwe_warning cwe_warning)
let check_cwe prog proj tid_map symbol_names _ =
......
......@@ -75,12 +75,13 @@ let check_subfunction prog tid_map sub pathes =
let path_checks = List.map pathes ~f:(fun path -> check_path prog tid_map sub path) in
if not (List.exists path_checks ~f:(fun x -> x = true)) then
let address = (Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map) in
let symbol = (Term.name sub) in
let tid = Address_translation.tid_to_string @@ Term.tid sub in
let symbol = Term.name sub in
let description = sprintf
"(The program utilizes chroot without dropping privileges and/or changing the directory) at %s (%s)"
address
symbol in
let cwe_warning = cwe_warning_factory name version description ~addresses:[address] ~symbols:[symbol] in
let cwe_warning = cwe_warning_factory name version description ~addresses:[address] ~tids:[tid] ~symbols:[symbol] in
collect_cwe_warning cwe_warning
end
......
......@@ -47,6 +47,7 @@ let handle_sub sub program tid_map _symbols source_sink_pair =
Seq.iter sink_calls ~f:(fun sink_call ->
if is_reachable sub source_call sink_call then
let address = (Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map) in
let tid = Address_translation.tid_to_string @@ Term.tid sub in
let symbol = (Term.name sub) in
let other = [["source"; source]; ["sink"; sink]] in
let description = sprintf
......@@ -61,6 +62,7 @@ let handle_sub sub program tid_map _symbols source_sink_pair =
description
~other:other
~addresses:[address]
~tids:[tid]
~symbols:[symbol] in
collect_cwe_warning cwe_warning
else
......
......@@ -14,10 +14,11 @@ let handle_sub sub program tid_map symbols =
if Symbol_utils.sub_calls_symbol program sub "system" then
let symbol = Term.name sub in
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map in
let tid = Address_translation.tid_to_string @@ Term.tid sub in
let description = sprintf "(Untrusted Search Path) sub %s at %s may be vulnerable to PATH manipulation."
symbol
address in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~symbols:[symbol] description in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~tids:[tid] ~symbols:[symbol] description in
collect_cwe_warning cwe_warning
else
()
......
......@@ -68,12 +68,13 @@ let log_cwe_warning sub i d tid_map =
let other =[["word"; word]] in
let symbol = Sub.name sub in
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid d) tid_map in
let tid = Address_translation.tid_to_string @@ Term.tid d in
let description = sprintf
"(Use of Uninitialized Variable) Found potentially unitialized stack variable (FP + %s) in function %s at %s"
word
symbol
address in
let cwe_warning = cwe_warning_factory name version ~other:other ~addresses:[address] ~symbols:[symbol] description in
let cwe_warning = cwe_warning_factory name version ~other:other ~addresses:[address] ~tids:[tid] ~symbols:[symbol] description in
collect_cwe_warning cwe_warning
let check_subfunction _prog proj tid_map sub =
......
......@@ -17,12 +17,13 @@ let check_input_is_pointer_size proj _prog _sub blk jmp tid_map symbols =
if get_pointer_size (Project.arch proj) = (Word.to_int_exn w) then
begin
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map in
let tid = Address_translation.tid_to_string @@ Term.tid blk in
let symbol = Symbol_utils.get_symbol_name_from_jmp jmp symbols in
let description = sprintf
"(Use of sizeof on a Pointer Type) sizeof on pointer at %s (%s)."
address
symbol in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~symbols:[symbol] description in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~tids:[tid] ~symbols:[symbol] description in
collect_cwe_warning cwe_warning
end
with _ -> Log_utils.error "Caught exception in module [CWE467]."
......
......@@ -244,6 +244,7 @@ let print_hit tid ~sub ~function_names ~tid_map =
if fn_name = (Tid.name call_tid) then
begin
let address = Address_translation.translate_tid_to_assembler_address_string tid tid_map in
let tids = [Address_translation.tid_to_string tid] in
let description = sprintf
"(NULL Pointer Dereference) There is no check if the return value is NULL at %s (%s)."
address
......@@ -252,6 +253,7 @@ let print_hit tid ~sub ~function_names ~tid_map =
name
version
~addresses:[address]
~tids:tids
~symbols:[fn_name]
description in
collect_cwe_warning cwe_warning;
......
......@@ -21,13 +21,14 @@ let check_umask_arg tid_map blk w =
let umask_arg = Word.to_int_exn w in
if is_chmod_style_arg umask_arg then
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map in
let tid = Address_translation.tid_to_string @@ Term.tid blk in
let umask_arg_str = sprintf "%d" umask_arg in
let description = sprintf
"(Use of umask() with chmod-style Argument) Function %s calls umask with argument %s"
address
umask_arg_str in
let other = [["umask_arg"; umask_arg_str]] in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~other:other description in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~tids:[tid] ~other:other description in
collect_cwe_warning cwe_warning
with _ -> Log_utils.error "Caught exception in module [CWE560]."
......
......@@ -26,6 +26,7 @@ let print_calls calls ~tid_map =
| (a, b, c) ->
begin
let address = Address_translation.translate_tid_to_assembler_address_string b tid_map in
let tid = Address_translation.tid_to_string b in
let other = [["dangerous_function"; c]] in
let description = sprintf
"(Use of Potentially Dangerous Function) %s (%s) -> %s."
......@@ -37,6 +38,7 @@ let print_calls calls ~tid_map =
version
~other:other
~addresses:[address]
~tids:[tid]
~symbols:[a]
description in
collect_cwe_warning cwe_warning
......
......@@ -10,12 +10,13 @@ let handle_sub sub program tid_map _symbols =
if Symbol_utils.sub_calls_symbol program sub "ioctl" then
begin
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map in
let tid = Address_translation.tid_to_string @@ Term.tid sub in
let symbol = Term.name sub in
let description = sprintf
"(Exposed IOCTL with Insufficient Access Control) Program uses ioctl at %s (%s). Be sure to double check the program and the corresponding driver."
symbol
address in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~symbols:[symbol] description in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~tids:[tid] ~symbols:[symbol] description in
collect_cwe_warning cwe_warning
end
else
......
......@@ -181,5 +181,13 @@
},
"CWE782": {
"symbols": []
},
"check_path": {
"_comment": "functions that take direct user input",
"symbols": [
"recv",
"fgets",
"scanf"
]
}
}
......@@ -13,3 +13,5 @@ let generate_tid_map prog =
| None -> addrs
| Some addr -> Map.add_exn addrs ~key:(Term.tid t) ~data:addr
end)#run prog Tid.Map.empty
let tid_to_string tid = Bap.Std.Tid.name tid
......@@ -12,3 +12,5 @@ val generate_tid_map :
val translate_tid_to_assembler_address_string :
Bap.Std.tid -> Bap.Std.word Bap.Std.Tid.Map.t -> string
val tid_to_string :
Bap.Std.tid -> string
......@@ -5,57 +5,91 @@ module CweWarning = struct
name : string;
version : string;
addresses: string list;
tids: string list;
symbols: string list;
other : string list list;
description : string;
} [@@deriving yojson]
end
module CweWarningResult = struct
module CheckPath = struct
type t = {
source : string;
destination : string;
source_addr : string;
destination_addr : string;
path : string list;
path_str : string;
} [@@deriving yojson]
end
module CweCheckerResult = struct
type t = {
binary : string;
time : float;
warnings : CweWarning.t list;
check_path : CheckPath.t list;
} [@@deriving yojson]
end
let cwe_warning_store = ref [||]
let cwe_warning_store = ref []
let check_path_store = ref []
let no_logging = ref false
let turn_off_logging () = no_logging := true
let cwe_warning_factory name version ?(other = []) ?(addresses = []) ?(symbols = []) description =
let cwe_warning_factory name version ?(other = []) ?(addresses = []) ?(tids = []) ?(symbols = []) description =
{
CweWarning.name = name;
CweWarning.version = version;
CweWarning.description = description;
CweWarning.other = other;
CweWarning.addresses = addresses;
CweWarning.tids = tids;
CweWarning.symbols = symbols;
}
let collect_cwe_warning warning = cwe_warning_store := Array.append !cwe_warning_store [|warning|]
let check_path_factory ?(path = []) ?(path_str = "") source source_addr destination destination_addr =
{
CheckPath.source = source;
CheckPath.source_addr = source_addr;
CheckPath.destination = destination;
CheckPath.destination_addr = destination_addr;
CheckPath.path = path;
CheckPath.path_str = path_str;
}
let collect_cwe_warning warning = cwe_warning_store := !cwe_warning_store @ [warning]
let collect_check_path path = check_path_store := !check_path_store @ [path]
let get_cwe_warnings () = !cwe_warning_store
let emit_cwe_warnings_json target_path out_path =
let emit_json target_path out_path =
let cwe_warning_result = {
CweWarningResult.binary = target_path;
CweWarningResult.time = Unix.time ();
CweWarningResult.warnings = Array.to_list !cwe_warning_store
CweCheckerResult.binary = target_path;
CweCheckerResult.time = Unix.time ();
CweCheckerResult.warnings = !cwe_warning_store;
CweCheckerResult.check_path = !check_path_store
} in
let output = Yojson.Safe.pretty_to_string (CweWarningResult.to_yojson cwe_warning_result) in
let output = Yojson.Safe.pretty_to_string (CweCheckerResult.to_yojson cwe_warning_result) in
if out_path = "" then
print_endline output
else
Out_channel.write_all out_path ~data:output
let emit_cwe_warnings_native out_path =
let output_lines = Array.map !cwe_warning_store ~f:(fun (cwe_warning:CweWarning.t) ->
sprintf "[%s] (%s) %s" cwe_warning.name cwe_warning.version cwe_warning.description) in
let emit_native out_path =
let output_check_path = List.map !check_path_store ~f:(fun (check_path:CheckPath.t) ->
sprintf "[CheckPath] %s(%s) -> %s via %s" check_path.source check_path.source_addr check_path.destination_addr check_path.path_str) in
let output_warnings = List.map !cwe_warning_store ~f:(fun (cwe_warning:CweWarning.t) ->
sprintf "[%s] (%s) %s" cwe_warning.name cwe_warning.version cwe_warning.description) in
let output_lines = output_warnings @ output_check_path in
if out_path = "" then
Array.iter output_lines ~f:print_endline
List.iter output_lines ~f:print_endline
else
Out_channel.write_lines out_path (Array.to_list output_lines)
Out_channel.write_lines out_path output_lines
let debug message = if !no_logging then () else print_endline ("DEBUG: " ^ message)
......
......@@ -8,7 +8,7 @@
the function collect_cwe_warning to store them globally.
At the moment, cwe_checker supports plain text and JSON output. The corresponding functions
are emit_cwe_warnings_native and emit_cwe_warnings_json.
are emit_native and emit_json.
In addition, there are several functions (debug, error, info) to notify the user of certain
events. Note that these functions may pollute the output.
......@@ -16,22 +16,80 @@
module CweWarning : sig
type t = {
name : string;
version : string;
addresses: string list;
symbols: string list;
other : string list list;
description : string;
}
name : string;
version : string;
addresses: string list;
tids: string list;
symbols: string list;
other : string list list;
description : string;
}
end
module CheckPath : sig
type t = {
source : string;
destination : string;
source_addr : string;
destination_addr : string;
path : string list;
path_str : string;
}
end
(**
This function turns on or off the logging of debug, error, and info messages to STDOUT / STDERR.
Use if you do not want to pollute your JSON output when outputting to STDOUT.
*)
val turn_off_logging : unit -> unit
val cwe_warning_factory : string -> string -> ?other:string list list -> ?addresses:string list -> ?symbols:string list -> string -> CweWarning.t
(**
Factory function to easily build an element of type CweWarning.t.
It takes the following input parameters:
- name: name of the CWE
- version: version of the cwe_check
- other: list of abritrary string elements (use as needed)
- addresses: list of relevant assembly addresses as strings
- tids: list of relevant TIDs as strings
- symbols: list of associated symbols as strings
- description: string description of the CWE
*)
val cwe_warning_factory : string -> string -> ?other:string list list -> ?addresses:string list -> ?tids:string list -> ?symbols:string list -> string -> CweWarning.t
(**
Factory function to easily build an element of type CheckPath.t.
It takes the following input parameters:
- path: a list of strings of node on the path
- path_str: a string of the path between source and destination
- source: symbol of source
- source_addr: assembly address of source
- destination: symbol / address of destination
- destination_addr: assembly address of destination
*)
val check_path_factory : ?path:string list -> ?path_str:string -> string -> string -> string -> string -> CheckPath.t
(**
Add one CweWarning.t element to an internal store. All elements are emited by calling one of the emit_* functions.
*)
val collect_cwe_warning : CweWarning.t -> unit
(**
Add one CheckPath.t element to an internal store. All elements are emited by calling one of the emit_* functions.
*)
val collect_check_path : CheckPath.t -> unit
(**
Returns the internal store of CweWarning.t elements as a list.
*)
val get_cwe_warnings : unit -> CweWarning.t list
val emit_cwe_warnings_json : string -> string -> unit
val emit_cwe_warnings_native : string -> unit
(**
Emits stored CweWarning.t and CheckPath.t elements as json.
target_path is the path of the current BAP project and out_path is the path a json output file.
*)
val emit_json : string -> string -> unit
(**
Emits stored CweWarning.t and CheckPath.t elements.
target_path is the path of the current BAP project and out_path is the path an output file.
*)
val emit_native : string -> unit
val debug : string -> unit
val error : string -> unit
......
import json
import os
import subprocess
import unittest
class TestCheckPath(unittest.TestCase):
def setUp(self):
if 'travis' in os.environ['USER']:
abs_path = os.path.abspath('test/artificial_samples/build/check_path_x64_gcc.out')
self.cmd = 'docker run --rm -v %s:/tmp/input cwe-checker:latest bap /tmp/input --pass=cwe-checker --cwe-checker-config=/home/bap/cwe_checker/src/config.json --cwe-checker-json --cwe-checker-check-path' % abs_path
else:
self.cmd = 'bap test/artificial_samples/build/check_path_x64_gcc.out --pass=cwe-checker --cwe-checker-config=src/config.json --cwe-checker-json --cwe-checker-check-path'
def test_check_path_01_x64_gcc(self):
output = subprocess.check_output(self.cmd.split())
j = json.loads(output)
self.assertTrue('check_path' in j)
self.assertEqual(len(j['check_path']), 7)
......@@ -33,9 +33,13 @@ cpp_flags = {'x64': '-g -fno-stack-protector',
cpp_linkers = {'x86': './dockcross-linux-x86 g++ -m32'}
def which(pgm):
# TODO: check if dockcross containers are installed
if pgm.startswith('.'):
# check dockcross installation
if pgm.startswith('.') and os.path.isfile(pgm.split()[0]):
return pgm
elif pgm.startswith('.') and not (os.path.isfile(pgm.split()[0])):
return None
# check compilers on path
path = os.getenv('PATH')
for p in path.split(os.path.pathsep):
p = os.path.join(p,pgm)
......
#include <stdio.h>
#include <stdlib.h>
void simple_check_path_in_function(){
// read in integer
int myInt;
scanf("%d", &myInt);
// provoke integer overflow
void *m = malloc(myInt * 8);
// free data
if (m != NULL)
free(m);
}
int read_int(){
int myInt;
scanf("%d", &myInt);
return myInt;
}
void check_path_across_functions(){
int i = read_int();
// provoke integer overflow
void *m = malloc(i * 8);
// free data
if (m != NULL)
free(m);
}
int main(void)
{
simple_check_path_in_function();
check_path_across_functions();
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment