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 ...@@ -7,6 +7,7 @@ dev
- Refactoring of logging and JSON support via --json (PR #30) - Refactoring of logging and JSON support via --json (PR #30)
- Added file output support via --out (PR #30) - Added file output support via --out (PR #30)
- Surpress logging of info, error and warning to STDOUT via --no-logging (PR #32) - 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) 0.2 (2019-06-25)
===== =====
......
...@@ -22,6 +22,20 @@ colors = {'CWE190': YELLOW, ...@@ -22,6 +22,20 @@ colors = {'CWE190': YELLOW,
'CWE787': RED, '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): class CweWarning(object):
...@@ -57,7 +71,20 @@ class Parser(object): ...@@ -57,7 +71,20 @@ class Parser(object):
return result 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): def parse(self):
with open(self._result_path) as fhandle: with open(self._result_path) as fhandle:
j = json.load(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): class IdaGenerator(object):
def __init__(self, results): def __init__(self, results):
...@@ -6,11 +8,17 @@ class IdaGenerator(object): ...@@ -6,11 +8,17 @@ class IdaGenerator(object):
def generate(self): def generate(self):
script = "import sark\nimport idaapi\n" script = "import sark\nimport idaapi\n"
for res in self._results: for res in self._results:
if res.highlight and res.address: if isinstance(res, CweWarning):
first_address = res.address[0] if res.highlight and res.address:
script += "sark.Line(%s).color = %s\n" % (first_address, res.color) first_address = res.address[0]
script += "sark.Line(%s).comments.regular = '%s'\n" % (first_address, res.description) script += "sark.Line(%s).color = %s\n" % (first_address, res.color)
script += "print('[ %s ] %s')\n" % (first_address, res.description) 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: 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 return script
...@@ -35,6 +35,8 @@ If you want to print the output to a file with [--cwe-checker-out], you also nee ...@@ -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). 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: 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. - [-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]. 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. - [-module-versions] Prints the version numbers of each check.
......
...@@ -74,8 +74,8 @@ let full_run project config = ...@@ -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) List.iter known_modules ~f:(fun cwe -> execute_cwe_module cwe json program project tid_address_map)
end 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 if no_logging then
begin begin
Log_utils.turn_off_logging () Log_utils.turn_off_logging ()
...@@ -105,14 +105,23 @@ let main config module_versions partial_update json_output file_output no_loggin ...@@ -105,14 +105,23 @@ let main config module_versions partial_update json_output file_output no_loggin
full_run project config full_run project config
else else
partial_run project config partial_update; 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 if json_output then
begin begin
match Project.get project filename with match Project.get project filename with
| Some fname -> Log_utils.emit_cwe_warnings_json fname file_output | Some fname -> Log_utils.emit_json fname file_output
| None -> Log_utils.emit_cwe_warnings_json "" file_output | None -> Log_utils.emit_json "" file_output
end end
else else
Log_utils.emit_cwe_warnings_native file_output Log_utils.emit_native file_output
end end
end end
...@@ -123,8 +132,9 @@ module Cmdline = struct ...@@ -123,8 +132,9 @@ module Cmdline = struct
let json_output = flag "json" ~doc:"Outputs the result as JSON." let json_output = flag "json" ~doc:"Outputs the result as JSON."
let file_output = param string "out" ~doc:"Path to output file." 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 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 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 [ let () = manpage [
`S "DESCRIPTION"; `S "DESCRIPTION";
`P "This plugin checks various CWEs such as Insufficient Entropy in PRNG (CWE-332) or Use of Potentially Dangerous Function (CWE-676)" `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 = ...@@ -140,11 +140,11 @@ let main json_output file_output proj =
if json_output then if json_output then
begin begin
match Project.get proj filename with match Project.get proj filename with
| Some fname -> Log_utils.emit_cwe_warnings_json fname file_output | Some fname -> Log_utils.emit_json fname file_output
| None -> Log_utils.emit_cwe_warnings_json "" file_output | None -> Log_utils.emit_json "" file_output
end end
else else
Log_utils.emit_cwe_warnings_native file_output Log_utils.emit_native file_output
module Cmdline = struct module Cmdline = struct
open Config open Config
......
...@@ -12,11 +12,11 @@ let main json_output file_output project = ...@@ -12,11 +12,11 @@ let main json_output file_output project =
if json_output then if json_output then
begin begin
match Project.get project filename with match Project.get project filename with
| Some fname -> Log_utils.emit_cwe_warnings_json fname file_output | Some fname -> Log_utils.emit_json fname file_output
| None -> Log_utils.emit_cwe_warnings_json "" file_output | None -> Log_utils.emit_json "" file_output
end end
else else
Log_utils.emit_cwe_warnings_native file_output Log_utils.emit_native file_output
module Cmdline = struct module Cmdline = struct
open Config 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 = ...@@ -21,10 +21,11 @@ let contains_multiplication d =
let check_multiplication_before_symbol _proj _prog _sub blk jmp tid_map symbols = let check_multiplication_before_symbol _proj _prog _sub blk jmp tid_map symbols =
Seq.iter (Term.enum def_t blk) Seq.iter (Term.enum def_t blk)
~f:(fun d -> if contains_multiplication d then ~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 description = "(Integer Overflow or Wraparound) Potential overflow due to multiplication" in
let symbol = (Symbol_utils.get_symbol_name_from_jmp jmp symbols) in let addresses = [(Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map)] in
let description = sprintf "(Integer Overflow or Wraparound) Potential overflow due to multiplication at %s (%s)" address symbol in let tids = [Address_translation.tid_to_string @@ Term.tid blk] in
let cwe_warning = cwe_warning_factory name version description ~addresses:[address] ~symbols:[symbol] 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) collect_cwe_warning cwe_warning)
let check_cwe prog proj tid_map symbol_names _ = let check_cwe prog proj tid_map symbol_names _ =
......
...@@ -75,12 +75,13 @@ let check_subfunction prog tid_map sub pathes = ...@@ -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 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 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 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 let description = sprintf
"(The program utilizes chroot without dropping privileges and/or changing the directory) at %s (%s)" "(The program utilizes chroot without dropping privileges and/or changing the directory) at %s (%s)"
address address
symbol in 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 collect_cwe_warning cwe_warning
end end
......
...@@ -47,6 +47,7 @@ let handle_sub sub program tid_map _symbols source_sink_pair = ...@@ -47,6 +47,7 @@ let handle_sub sub program tid_map _symbols source_sink_pair =
Seq.iter sink_calls ~f:(fun sink_call -> Seq.iter sink_calls ~f:(fun sink_call ->
if is_reachable sub source_call sink_call then 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 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 symbol = (Term.name sub) in
let other = [["source"; source]; ["sink"; sink]] in let other = [["source"; source]; ["sink"; sink]] in
let description = sprintf let description = sprintf
...@@ -61,6 +62,7 @@ let handle_sub sub program tid_map _symbols source_sink_pair = ...@@ -61,6 +62,7 @@ let handle_sub sub program tid_map _symbols source_sink_pair =
description description
~other:other ~other:other
~addresses:[address] ~addresses:[address]
~tids:[tid]
~symbols:[symbol] in ~symbols:[symbol] in
collect_cwe_warning cwe_warning collect_cwe_warning cwe_warning
else else
......
...@@ -14,10 +14,11 @@ let handle_sub sub program tid_map symbols = ...@@ -14,10 +14,11 @@ let handle_sub sub program tid_map symbols =
if Symbol_utils.sub_calls_symbol program sub "system" then if Symbol_utils.sub_calls_symbol program sub "system" then
let symbol = Term.name sub in let symbol = Term.name sub in
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map 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." let description = sprintf "(Untrusted Search Path) sub %s at %s may be vulnerable to PATH manipulation."
symbol symbol
address in 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 collect_cwe_warning cwe_warning
else else
() ()
......
...@@ -68,12 +68,13 @@ let log_cwe_warning sub i d tid_map = ...@@ -68,12 +68,13 @@ let log_cwe_warning sub i d tid_map =
let other =[["word"; word]] in let other =[["word"; word]] in
let symbol = Sub.name sub in let symbol = Sub.name sub in
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid d) tid_map 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 let description = sprintf
"(Use of Uninitialized Variable) Found potentially unitialized stack variable (FP + %s) in function %s at %s" "(Use of Uninitialized Variable) Found potentially unitialized stack variable (FP + %s) in function %s at %s"
word word
symbol symbol
address in 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 collect_cwe_warning cwe_warning
let check_subfunction _prog proj tid_map sub = 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 = ...@@ -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 if get_pointer_size (Project.arch proj) = (Word.to_int_exn w) then
begin begin
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map in 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 symbol = Symbol_utils.get_symbol_name_from_jmp jmp symbols in
let description = sprintf let description = sprintf
"(Use of sizeof on a Pointer Type) sizeof on pointer at %s (%s)." "(Use of sizeof on a Pointer Type) sizeof on pointer at %s (%s)."
address address
symbol in 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 collect_cwe_warning cwe_warning
end end
with _ -> Log_utils.error "Caught exception in module [CWE467]." with _ -> Log_utils.error "Caught exception in module [CWE467]."
......
...@@ -244,6 +244,7 @@ let print_hit tid ~sub ~function_names ~tid_map = ...@@ -244,6 +244,7 @@ let print_hit tid ~sub ~function_names ~tid_map =
if fn_name = (Tid.name call_tid) then if fn_name = (Tid.name call_tid) then
begin begin
let address = Address_translation.translate_tid_to_assembler_address_string tid tid_map in 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 let description = sprintf
"(NULL Pointer Dereference) There is no check if the return value is NULL at %s (%s)." "(NULL Pointer Dereference) There is no check if the return value is NULL at %s (%s)."
address address
...@@ -252,6 +253,7 @@ let print_hit tid ~sub ~function_names ~tid_map = ...@@ -252,6 +253,7 @@ let print_hit tid ~sub ~function_names ~tid_map =
name name
version version
~addresses:[address] ~addresses:[address]
~tids:tids
~symbols:[fn_name] ~symbols:[fn_name]
description in description in
collect_cwe_warning cwe_warning; collect_cwe_warning cwe_warning;
......
...@@ -21,13 +21,14 @@ let check_umask_arg tid_map blk w = ...@@ -21,13 +21,14 @@ let check_umask_arg tid_map blk w =
let umask_arg = Word.to_int_exn w in let umask_arg = Word.to_int_exn w in
if is_chmod_style_arg umask_arg then 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 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 umask_arg_str = sprintf "%d" umask_arg in
let description = sprintf let description = sprintf
"(Use of umask() with chmod-style Argument) Function %s calls umask with argument %s" "(Use of umask() with chmod-style Argument) Function %s calls umask with argument %s"
address address
umask_arg_str in umask_arg_str in
let other = [["umask_arg"; 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 collect_cwe_warning cwe_warning
with _ -> Log_utils.error "Caught exception in module [CWE560]." with _ -> Log_utils.error "Caught exception in module [CWE560]."
......
...@@ -26,6 +26,7 @@ let print_calls calls ~tid_map = ...@@ -26,6 +26,7 @@ let print_calls calls ~tid_map =
| (a, b, c) -> | (a, b, c) ->
begin begin
let address = Address_translation.translate_tid_to_assembler_address_string b tid_map in 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 other = [["dangerous_function"; c]] in
let description = sprintf let description = sprintf
"(Use of Potentially Dangerous Function) %s (%s) -> %s." "(Use of Potentially Dangerous Function) %s (%s) -> %s."
...@@ -37,6 +38,7 @@ let print_calls calls ~tid_map = ...@@ -37,6 +38,7 @@ let print_calls calls ~tid_map =
version version
~other:other ~other:other
~addresses:[address] ~addresses:[address]
~tids:[tid]
~symbols:[a] ~symbols:[a]
description in description in
collect_cwe_warning cwe_warning collect_cwe_warning cwe_warning
......
...@@ -10,12 +10,13 @@ let handle_sub sub program tid_map _symbols = ...@@ -10,12 +10,13 @@ let handle_sub sub program tid_map _symbols =
if Symbol_utils.sub_calls_symbol program sub "ioctl" then if Symbol_utils.sub_calls_symbol program sub "ioctl" then
begin begin
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map 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 symbol = Term.name sub in let symbol = Term.name sub in
let description = sprintf 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." "(Exposed IOCTL with Insufficient Access Control) Program uses ioctl at %s (%s). Be sure to double check the program and the corresponding driver."
symbol symbol
address in 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 collect_cwe_warning cwe_warning
end end
else else
......
...@@ -181,5 +181,13 @@ ...@@ -181,5 +181,13 @@
}, },
"CWE782": { "CWE782": {
"symbols": [] "symbols": []
},
"check_path": {
"_comment": "functions that take direct user input",
"symbols": [
"recv",
"fgets",
"scanf"
]
} }
} }
...@@ -13,3 +13,5 @@ let generate_tid_map prog = ...@@ -13,3 +13,5 @@ let generate_tid_map prog =
| None -> addrs | None -> addrs
| Some addr -> Map.add_exn addrs ~key:(Term.tid t) ~data:addr | Some addr -> Map.add_exn addrs ~key:(Term.tid t) ~data:addr
end)#run prog Tid.Map.empty end)#run prog Tid.Map.empty
let tid_to_string tid = Bap.Std.Tid.name tid
...@@ -12,3 +12,5 @@ val generate_tid_map : ...@@ -12,3 +12,5 @@ val generate_tid_map :
val translate_tid_to_assembler_address_string : val translate_tid_to_assembler_address_string :
Bap.Std.tid -> Bap.Std.word Bap.Std.Tid.Map.t -> 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 ...@@ -5,57 +5,91 @@ module CweWarning = struct
name : string; name : string;
version : string; version : string;
addresses: string list; addresses: string list;
tids: string list;
symbols: string list; symbols: string list;
other : string list list; other : string list list;
description : string; description : string;
} [@@deriving yojson] } [@@deriving yojson]
end 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 = { type t = {
binary : string; binary : string;
time : float; time : float;
warnings : CweWarning.t list; warnings : CweWarning.t list;
check_path : CheckPath.t list;
} [@@deriving yojson] } [@@deriving yojson]
end end
let cwe_warning_store = ref [||] let cwe_warning_store = ref []
let check_path_store = ref []
let no_logging = ref false let no_logging = ref false
let turn_off_logging () = no_logging := true 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.name = name;
CweWarning.version = version; CweWarning.version = version;
CweWarning.description = description; CweWarning.description = description;
CweWarning.other = other; CweWarning.other = other;
CweWarning.addresses = addresses; CweWarning.addresses = addresses;
CweWarning.tids = tids;
CweWarning.symbols = symbols; 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 = { let cwe_warning_result = {
CweWarningResult.binary = target_path; CweCheckerResult.binary = target_path;
CweWarningResult.time = Unix.time (); CweCheckerResult.time = Unix.time ();
CweWarningResult.warnings = Array.to_list !cwe_warning_store CweCheckerResult.warnings = !cwe_warning_store;
CweCheckerResult.check_path = !check_path_store
} in } 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 if out_path = "" then
print_endline output print_endline output
else else
Out_channel.write_all out_path ~data:output Out_channel.write_all out_path ~data:output
let emit_cwe_warnings_native out_path = let emit_native out_path =
let output_lines = Array.map !cwe_warning_store ~f:(fun (cwe_warning:CweWarning.t) -> let output_check_path = List.map !check_path_store ~f:(fun (check_path:CheckPath.t) ->
sprintf "[%s] (%s) %s" cwe_warning.name cwe_warning.version cwe_warning.description) in 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 if out_path = "" then
Array.iter output_lines ~f:print_endline List.iter output_lines ~f:print_endline
else 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) let debug message = if !no_logging then () else print_endline ("DEBUG: " ^ message)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
the function collect_cwe_warning to store them globally. the function collect_cwe_warning to store them globally.
At the moment, cwe_checker supports plain text and JSON output. The corresponding functions 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 In addition, there are several functions (debug, error, info) to notify the user of certain
events. Note that these functions may pollute the output. events. Note that these functions may pollute the output.
...@@ -16,22 +16,80 @@ ...@@ -16,22 +16,80 @@
module CweWarning : sig module CweWarning : sig
type t = { type t = {
name : string; name : string;
version : string; version : string;
addresses: string list; addresses: string list;
symbols: string list; tids: string list;
other : string list list; symbols: string list;
description : string; other : string list list;
} description : string;
}
end 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 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 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 debug : string -> unit
val error : 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', ...@@ -33,9 +33,13 @@ cpp_flags = {'x64': '-g -fno-stack-protector',
cpp_linkers = {'x86': './dockcross-linux-x86 g++ -m32'} cpp_linkers = {'x86': './dockcross-linux-x86 g++ -m32'}
def which(pgm): def which(pgm):
# TODO: check if dockcross containers are installed # check dockcross installation
if pgm.startswith('.'): if pgm.startswith('.') and os.path.isfile(pgm.split()[0]):
return pgm return pgm
elif pgm.startswith('.') and not (os.path.isfile(pgm.split()[0])):
return None
# check compilers on path
path = os.getenv('PATH') path = os.getenv('PATH')
for p in path.split(os.path.pathsep): for p in path.split(os.path.pathsep):
p = os.path.join(p,pgm) 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