Skip to content

Commit

Permalink
Refactoring of logging (#30)
Browse files Browse the repository at this point in the history
* Removes old version of log_utils, prototypes for new version.

* Implemented native logging

* Json-Output basically working.

* Added acceptance test for JSON parsing

* Adds some odoc to log_utils.

* Added support for file output (--cwe-checker-out)

* Add acceptance test for file output
  • Loading branch information
tbarabosch authored Jul 24, 2019
1 parent 4034acb commit 1d9991f
Show file tree
Hide file tree
Showing 25 changed files with 365 additions and 392 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ dev

- Added more documentation to checks (PR #26)
- Fixed check CWE367: use symbols defined in config.json (PR #28)
- Refactoring of logging and JSON support via --json (PR #30)
- Added file output support via --out (PR #30)

0.2 (2019-06-25)
=====
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ If you plan to develop cwe_checker, it is recommended to build it using the prov
- dune >= 1.6
- BAP 1.6 (and its dependencies)
- yojson >= 1.6.0
- ppx_deriving_json >= 3.5.1

This comment has been minimized.

Copy link
@Enkelmann

Enkelmann Jul 30, 2019

Contributor

The package name is ppx_deriving_yojson.

- alcotest >= 0.8.3 (for tests)
- Sark (latest) for IDA Pro annotations
- pytest >= 3.5.1 (for tests)
Expand Down
32 changes: 19 additions & 13 deletions plugins/cwe_checker/cwe_checker.ml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ let build_version_sexp () =
|> String.concat ~sep:" "

let print_module_versions () =
Log_utils.info
"[cwe_checker] module_versions: (%s)"
(build_version_sexp ())
Log_utils.info (sprintf
"[cwe_checker] module_versions: (%s)"
(build_version_sexp ()))

let execute_cwe_module cwe json program project tid_address_map =
let parameters = match cwe.has_parameters with
Expand All @@ -57,7 +57,7 @@ let partial_run project config modules =
let program = Project.program project in
let tid_address_map = Address_translation.generate_tid_map program in
let json = Yojson.Basic.from_file config in
Log_utils.info "[cwe_checker] Just running the following analyses: %s." modules;
Log_utils.info (sprintf "[cwe_checker] Just running the following analyses: %s." modules);
List.iter (String.split modules ~on: ',') ~f:(fun cwe ->
let cwe_mod = match List.find known_modules ~f:(fun x -> x.name = cwe) with
| Some(module_) -> module_
Expand All @@ -74,10 +74,7 @@ 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 project =
Log_utils.set_log_level Log_utils.DEBUG;
Log_utils.set_output stdout;
Log_utils.color_on ();
let main config module_versions partial_update json_output file_output project =

if module_versions then
begin
Expand All @@ -102,19 +99,28 @@ let main config module_versions partial_update project =
if partial_update = "" then
full_run project config
else
partial_run project config partial_update
partial_run project config partial_update;
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
end
else
Log_utils.emit_cwe_warnings_native file_output
end
end

module Cmdline = struct
open Config
let config = param string "config" ~doc:"Path to configuration file."
let module_versions = param bool "module_versions" ~doc:"Prints out the version numbers of all known modules."
let module_versions = flag "module_versions" ~doc:"Prints out the version numbers of all known modules."
let json_output = flag "json" ~doc:"Outputs the result as JSON."
let file_output = param string "out" ~doc:"Path to output file."
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))
let () = when_ready (fun ({get=(!!)}) -> Project.register_pass' ~deps:["callsites"] (main !!config !!module_versions !!partial_update !!json_output !!file_output))
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)"
`P "This plugin checks various CWEs such as Insufficient Entropy in PRNG (CWE-332) or Use of Potentially Dangerous Function (CWE-676)"
]
end
34 changes: 19 additions & 15 deletions plugins/cwe_checker_emulation/cwe_checker_emulation.ml
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,7 @@ end
- for all subroutins we fork a Primus machine
- all monitored events are collected globally
- after the last Primus machine has terminated we report all observed incidents *)
let main {Config.get=(!)} proj =
Log_utils.set_log_level Log_utils.DEBUG;
Log_utils.set_output stdout;
Log_utils.color_on ();

let main json_output file_output proj =
Primus.Machine.add_component (module Monitor);
begin
let prog = (Project.program proj) in
Expand All @@ -141,13 +137,21 @@ let main {Config.get=(!)} proj =
info "program terminated by a signal: %s" (Primus.Exn.to_string exn);
end;
analyze_events ();
proj

(** At the moment this plugin depends due to Primus on the plugin
trivial-condition-form. *)
let deps = [
"trivial-condition-form"
]

let () =
Config.when_ready (fun conf -> Project.register_pass ~deps (main conf))
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
end
else
Log_utils.emit_cwe_warnings_native file_output

module Cmdline = struct
open Config
let json_output = flag "json" ~doc:"Outputs the result as JSON."
let file_output = param string "out" ~doc:"Path to output file."
let () = when_ready (fun ({get=(!!)}) -> Project.register_pass' ~deps:["trivial-condition-form"] (main !!json_output !!file_output))
let () = manpage [
`S "DESCRIPTION";
`P "This plugin utilizes symbolic execution to find CWEs like Double Free (CWE415) or Use After Free (CWE416)."]
end
24 changes: 17 additions & 7 deletions plugins/cwe_checker_emulation/incident_reporter.ml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
open Core_kernel
open Cwe_checker_core
open Log_utils

let version = "0.1"

Expand All @@ -26,19 +27,28 @@ let get_incident_locations_from_ids ids location_tbl =
let incident_locations = ref [] in
Sexplib__Sexp_with_layout.List.iter ids ~f:(fun id -> incident_locations := (map_id_to_location (Sexp.to_string id) location_tbl)::(!incident_locations)); !incident_locations

let report_cwe_125 location_path =
Log_utils.warn "[CWE125] {%s} (Out-of-bounds Read) %s" version location_path;
Log_utils.warn "[CWE787] {%s} (Out-of-bounds Write) %s" version location_path
let report_cwe_125 location_path =
let description = sprintf "(Out-of-bounds Read) %s" location_path in
let cwe_warning = cwe_warning_factory "CWE125" version ~other:[["path";location_path]] description in
collect_cwe_warning cwe_warning;
let description = sprintf "(Out-of-bounds Write) %s" location_path in
let cwe_warning = cwe_warning_factory "CWE787" version ~other:[["path";location_path]] description in
collect_cwe_warning cwe_warning

let report_cwe_415 location_path =
Log_utils.warn "[CWE415] {%s} (Double Free) %s" version location_path
let description = sprintf "(Double Free) %s" location_path in
let cwe_warning = cwe_warning_factory "CWE415" version ~other:[["path";location_path]] description in
collect_cwe_warning cwe_warning

let report_cwe_416 location_path =
Log_utils.warn "[CWE416] {%s} (Use After Free) %s" version location_path
let description = sprintf "(Use After Free) %s" location_path in
let cwe_warning = cwe_warning_factory "CWE416" version ~other:[["path";location_path]] description in
collect_cwe_warning cwe_warning

let report_cwe_unknown location_path incident_str =
Log_utils.warn "[CWE UNKNOWN] {%s} (%s) %s" version incident_str location_path

let description = sprintf "(%s) %s" incident_str location_path in
let cwe_warning = cwe_warning_factory incident_str version ~other:[["path";location_path]] description in
collect_cwe_warning cwe_warning

(** Reports an incident. *)
let report incident location_tbl =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,27 @@ open Bap.Std
open Core_kernel
open Cwe_checker_core

let main project =
Log_utils.set_log_level Log_utils.DEBUG;
Log_utils.set_output stdout;
Log_utils.color_on ();
include Self()

let main json_output file_output project =

let program = Project.program project in
let tid_map = Address_translation.generate_tid_map program in
Type_inference.print_type_info_tags project tid_map
Type_inference.print_type_info_tags project tid_map;
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
end
else
Log_utils.emit_cwe_warnings_native file_output

let () = Project.register_pass' main ~deps:["cwe-checker-type-inference"]
module Cmdline = struct
open Config
let json_output = flag "json" ~doc:"Outputs the result as JSON."
let file_output = param string "out" ~doc:"Path to output file."
let () = when_ready (fun ({get=(!!)}) -> Project.register_pass' ~deps:["cwe-checker-type-inference"] (main !!json_output !!file_output))
let () = manpage [`S "DESCRIPTION";
`P "This plugin prints the results of the type inference plugin."]
end
28 changes: 15 additions & 13 deletions src/analysis/type_inference.ml
Original file line number Diff line number Diff line change
Expand Up @@ -617,13 +617,14 @@ let print_type_info_to_debug state block_tid ~tid_map ~sub_tid ~project =
end
| _ -> "Unknown"
in
Log_utils.debug
"[%s] {%s} TypeInfo at %s:\nRegister: %s\nStackOffset: %s"
name
version
(Address_translation.translate_tid_to_assembler_address_string block_tid tid_map)
register_string
stack_offset_str
let debug_str = sprintf
"[%s] {%s} TypeInfo at %s:\nRegister: %s\nStackOffset: %s"
name
version
(Address_translation.translate_tid_to_assembler_address_string block_tid tid_map)
register_string
stack_offset_str in
Log_utils.debug debug_str

let print_type_info_tags ~project ~tid_map =
let program = Project.program project in
Expand All @@ -635,12 +636,13 @@ let print_type_info_tags ~project ~tid_map =
match Term.get_attr block type_info_tag with
| Some(start_state) -> print_type_info_to_debug start_state (Term.tid block) ~tid_map ~sub_tid ~project
| None -> (* block has no type info tag, which should not happen *)
Log_utils.error
"[%s] {%s} Block has no TypeInfo at %s (block TID %s)"
name
version
(Address_translation.translate_tid_to_assembler_address_string (Term.tid block) tid_map)
(Tid.name (Term.tid block))
let error_str = sprintf
"[%s] {%s} Block has no TypeInfo at %s (block TID %s)"
name
version
(Address_translation.translate_tid_to_assembler_address_string (Term.tid block) tid_map)
(Tid.name (Term.tid block)) in
Log_utils.error error_str
)
)

Expand Down
12 changes: 6 additions & 6 deletions src/checkers/cwe_190.ml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
open Core_kernel
open Bap.Std
open Symbol_utils
open Log_utils

let name = "CWE190"
let version = "0.1"
Expand All @@ -20,12 +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
Log_utils.warn
"[%s] {%s} (Integer Overflow or Wraparound) Potential overflow due to multiplication %s (%s)."
name
version
(Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map)
(Symbol_utils.get_symbol_name_from_jmp jmp symbols))
let description = "(Integer Overflow or Wraparound) Potential overflow due to multiplication" in

This comment has been minimized.

Copy link
@Enkelmann

Enkelmann Jul 30, 2019

Contributor

This description does not contain an address, so no address will be printed on standard (non-json) output. Thus users cannot locate the CWE.

This comment has been minimized.

Copy link
@tbarabosch

tbarabosch Jul 30, 2019

Author Contributor

correct, I am going to fix it.

let addresses = [(Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map)] in
let symbols = [(Symbol_utils.get_symbol_name_from_jmp jmp symbols)] in
let cwe_warning = cwe_warning_factory name version description ~addresses ~symbols in
collect_cwe_warning cwe_warning)

let check_cwe prog proj tid_map symbol_names _ =
match symbol_names with
Expand Down
11 changes: 8 additions & 3 deletions src/checkers/cwe_215.ml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
open Core_kernel
open Bap.Std

open Log_utils

(* TODO: IVG via gitter:
I see, so you need the CU information, and yes BAP doesn't provide this.
Expand All @@ -26,9 +26,14 @@ let check_cwe _ project _ _ _ =
let cmd = Format.sprintf "readelf --debug-dump=decodedline %s | grep CU" fname in
try
let in_chan = Unix.open_process_in cmd in
In_channel.input_lines in_chan |> List.iter ~f:(fun l -> Log_utils.warn "[%s] {%s} (Information Exposure Through Debug Information) %s" name version l)
In_channel.input_lines in_chan |> List.iter ~f:(fun l ->
let description = sprintf "(Information Exposure Through Debug Information) %s" l in
let cwe_warning = cwe_warning_factory name version description ~symbols:[l] in
collect_cwe_warning cwe_warning)


with
Unix.Unix_error (e,fm,argm) ->
Log_utils.error "[%s] {%s} %s %s %s" name version (Unix.error_message e) fm argm
Log_utils.error (sprintf "[%s] {%s} %s %s %s" name version (Unix.error_message e) fm argm)
end
| _ -> failwith "[CWE215] symbol_names not as expected"
15 changes: 9 additions & 6 deletions src/checkers/cwe_243.ml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
open Core_kernel
open Bap.Std
open Symbol_utils
open Log_utils

include Self()

Expand Down Expand Up @@ -73,12 +74,14 @@ let check_subfunction prog tid_map sub pathes =
begin
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
Log_utils.warn
"[%s] {%s} (The program utilizes chroot without dropping privileges and/or changing the directory) at %s (%s)"
name
version
(Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map)
(Term.name sub)
let address = (Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map) 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
collect_cwe_warning cwe_warning
end

let check_cwe prog _proj tid_map pathes _ =
Expand Down
12 changes: 6 additions & 6 deletions src/checkers/cwe_248.ml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
open Core_kernel
open Bap.Std
open Log_utils

let name = "CWE248"
let version = "0.1"

(* Print the findings to the log *)
let print_uncatched_exception block_tid ~tid_map =
Log_utils.warn
"[%s] {%s} (Possibly Uncaught Exception) (Exception thrown at %s)."
name
version
(Address_translation.translate_tid_to_assembler_address_string block_tid tid_map)

let address = (Address_translation.translate_tid_to_assembler_address_string block_tid tid_map) in
let description = sprintf "(Possibly Uncaught Exception) (Exception thrown at %s)." address in
let cwe_warning = cwe_warning_factory name version description in
collect_cwe_warning cwe_warning

(* Extract the name of a direct call, if the block contains a direct call. *)
let extract_direct_call_symbol block =
match Symbol_utils.extract_direct_call_tid_from_block block with
Expand Down
8 changes: 6 additions & 2 deletions src/checkers/cwe_332.ml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
open Core_kernel

open Symbol_utils
open Log_utils

let name = "CWE332"
let version = "0.1"
Expand All @@ -10,6 +10,10 @@ let check_cwe program _proj _tid_map _symbol_pairs _ =
| None -> begin
match (find_symbol program "rand") with
| None -> ()
| Some _ -> Log_utils.warn "[%s] {%s} (Insufficient Entropy in PRNG) program uses rand without calling srand before" name version
| Some _ -> begin
let description = "(Insufficient Entropy in PRNG) program uses rand without calling srand before" in
let cwe_warning = cwe_warning_factory name version description in

This comment has been minimized.

Copy link
@Enkelmann

Enkelmann Jul 30, 2019

Contributor

The warning contains no address to locate the CWE for both the standard output and the json output.

This comment has been minimized.

Copy link
@tbarabosch

tbarabosch Jul 30, 2019

Author Contributor

In this case, that is intended. It is a general property, i.e. there is the symbol rand but not srand. Therefore, there is no address.

collect_cwe_warning cwe_warning
end
end
| Some (_srand_tid, _rand_tid) -> ()
Loading

1 comment on commit 1d9991f

@Enkelmann
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks nice! I think some more documentation for the users of log_utils.ml would be nice, but that may be redundant when we write the documentation for all command line options.

Please sign in to comment.