Commit 41b84a41 by Enkelmann Committed by Thomas Barabosch

Cwe476 (#11)

* improved CWE476-check with dataflow analysis
parent 44cb572a
......@@ -7,6 +7,7 @@
- Added automated test suite (run with make test) (PR #7)
- Improved cross compiling for acceptance test cases by using dockcross (PR #8)
- Added BAP recipe for standard cwe_checker run (PR #9)
- Improved check for CWE-476 (NULL Pointer Dereference) using data flow analysis (PR #11)
0.1 (2018-10-08)
=====
......
......@@ -27,7 +27,7 @@ let check_multiplication_before_symbol proj prog sub blk jmp tid_map symbols =
(Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map)
(Symbol_utils.get_symbol_name_from_jmp jmp symbols))
let check_cwe prog proj tid_map symbol_names =
let check_cwe prog proj tid_map symbol_names _ =
match symbol_names with
| hd::[] ->
let symbols = Symbol_utils.build_symbols hd prog in
......
......@@ -5,4 +5,4 @@ https://cwe.mitre.org/data/definitions/190.html
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
......@@ -30,7 +30,7 @@ let read_lines in_chan =
List.rev !lines
(* TODO: check if program contains strings like "DEBUG"*)
let check_cwe _ project _ _ =
let check_cwe _ project _ _ _ =
match Project.get project filename with
| Some fname -> begin
let cmd = Format.sprintf "readelf --debug-dump=decodedline %s | grep CU" fname in
......@@ -42,4 +42,3 @@ let check_cwe _ project _ _ =
Log_utils.error "[%s] {%s} %s %s %s" name version (Unix.error_message e) fm argm
end
| _ -> failwith "[CWE215] symbol_names not as expected"
......@@ -3,4 +3,4 @@
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
......@@ -81,10 +81,9 @@ let check_subfunction prog tid_map sub pathes =
(Term.name sub)
end
let check_cwe prog proj tid_map pathes =
let check_cwe prog proj tid_map pathes _ =
let chroot_symbol = find_symbol prog "chroot" in
match chroot_symbol with
| Some _ ->
Seq.iter (Term.enum sub_t prog) ~f:(fun sub -> check_subfunction prog tid_map sub pathes)
| _ -> ()
......@@ -5,4 +5,4 @@ See https://cwe.mitre.org/data/definitions/243.html for detailed description. *)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
......@@ -59,7 +59,7 @@ let rec find_uncaught_exceptions subfunction already_checked_functions program ~
(* Search for uncatched exceptions for each entry point into the binary.
TODO: Exceptions, that are catched when starting from one entry point, but not from another, are masked this
way. We should check whether this produces a lot of false negatives. *)
let check_cwe program project tid_map symbol_pairs =
let check_cwe program project tid_map symbol_pairs _ =
let entry_points = Symbol_utils.get_program_entry_points program in
let _ = Seq.fold entry_points ~init:[] ~f:(fun already_checked_functions sub -> find_uncaught_exceptions ~tid_map:tid_map sub already_checked_functions program) in
()
......@@ -8,4 +8,4 @@ can actually catch the thrown exceptions, thus we generate some false negatives.
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
......@@ -7,7 +7,7 @@ open Symbol_utils
let name = "CWE332"
let version = "0.1"
let check_cwe program proj tid_map symbol_pairs =
let check_cwe program proj tid_map symbol_pairs _ =
match Option.both (find_symbol program "srand") (find_symbol program "rand") with
| None -> begin
match (find_symbol program "rand") with
......@@ -15,4 +15,3 @@ let check_cwe program proj tid_map symbol_pairs =
| Some _ -> Log_utils.warn "[%s] {%s} (Insufficient Entropy in PRNG) program uses rand without calling srand before" name version
end
| Some (srand_tid, rand_tid) -> ()
......@@ -6,4 +6,4 @@ See https://cwe.mitre.org/data/definitions/332.html for detailed description. *)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> 'a -> 'b -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> 'a -> 'b -> string list list -> 'c -> unit
......@@ -57,6 +57,6 @@ let handle_sub sub program tid_map symbols source sink =
else
()
let check_cwe program proj tid_map symbol_pairs =
let check_cwe program proj tid_map symbol_pairs _ =
let symbols = Symbol_utils.build_symbols ["access"; "open";] in
Seq.iter (Term.enum sub_t program) ~f:(fun s -> handle_sub s program tid_map symbols "access" "open")
......@@ -5,4 +5,4 @@ https://en.wikipedia.org/wiki/Time_of_check_to_time_of_use
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
......@@ -21,7 +21,7 @@ let handle_sub sub program tid_map symbols =
end
else ()
let check_cwe program proj tid_map symbols =
let check_cwe program proj tid_map symbols _ =
match symbols with
| hd::[] ->
Seq.iter (Term.enum sub_t program) ~f:(fun s -> handle_sub s program tid_map hd)
......
......@@ -11,4 +11,4 @@ drops privileges on startup. (Debian uses a modified bash which does not do thi
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
......@@ -92,5 +92,5 @@ let check_subfunction prog proj tid_map sub =
end
end)
let check_cwe prog proj tid_map symbol_names =
let check_cwe prog proj tid_map symbol_names _ =
Seq.iter (Term.enum sub_t prog) ~f:(fun sub -> check_subfunction prog proj tid_map sub)
......@@ -4,4 +4,4 @@ See https://cwe.mitre.org/data/definitions/457.html for detailed description. *)
val name: string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
......@@ -24,7 +24,7 @@ let check_input_is_pointer_size proj prog sub blk jmp tid_map symbols =
| _ -> ())
let check_cwe prog proj tid_map symbol_names =
let check_cwe prog proj tid_map symbol_names _ =
match symbol_names with
| hd::[] ->
let symbols = Symbol_utils.build_symbols hd prog in
......
......@@ -9,4 +9,4 @@ See https://cwe.mitre.org/data/definitions/467.html for detailed description. *)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
open Core_kernel.Std
open Bap.Std
open Symbol_utils
open Core_kernel.Std
let name = "CWE476"
let version = "0.1"
let find_blk_tid_in_sub blk_tid sub =
Seq.find (Term.enum blk_t sub) ~f:(fun b -> (Term.tid b) = blk_tid)
let get_jmps blk = Seq.filter (Blk.elts blk) ~f:(fun elt -> match elt with
| `Phi phi -> false
| `Def def -> false
| `Jmp jmp -> true )
|> Seq.map ~f:(fun j -> match j with
| `Jmp jmp -> jmp
| _ -> assert(false))
let jmp_cond_checks_zf jmp =
let e = Jmp.cond jmp in
(Exp.to_string e) = "~ZF" || (Exp.to_string e) = "ZF"
(* Check if next block contains when zf = 0 goto, if not then there is a chance that this yields a null pointer deref *)
let check_null_pointer proj prog sub blk jmp tid_map symbols =
Seq.iter (Graphs.Tid.Node.succs (Term.tid blk) (Sub.to_graph sub)) ~f:(
fun next_blk -> match find_blk_tid_in_sub next_blk sub with
| Some b -> begin
(* ToDo: Check if there is a definition of ZF = 0 *)
let jmps = get_jmps b in
match Seq.find jmps ~f:jmp_cond_checks_zf with
| Some _ -> ()
| None -> Log_utils.warn "[%s] {%s} (NULL Pointer Dereference) There is no check if the return value is NULL at %s (%s)."
let version = "0.2"
(* Access type denotes whether a variable var gets accessed or the memory stored at
address var gets accessed *)
type access_type = | Access of Bil.var | MemAccess of Bil.var | NoAccess
(* The union of two accesses is the higher access with MemAcces > Access > NoAccess *)
let union_access access1 access2 : access_type =
match (access1, access2) with
| (MemAccess(_), _) -> access1
| (_, MemAccess(_))
| (_, Access(_)) -> access2
| _ -> access1
(* union of three accesses for convenience *)
let union_access_triple access1 access2 access3 =
union_access access1 access2
|> union_access access3
(* the state contains a list of pairs of register names containing an unchecked
return value and the term identifiers of the block where the unchecked
return value was generated. *)
module State = struct
type t = (Var.t * Tid.t) list
(** adds var as a tainted register (with the taint source given by tid) *)
let add state var tid =
let state = List.Assoc.remove state ~equal:Var.(=) var in
List.Assoc.add state ~equal:Var.(=) var tid
(** returns Some(tid) if var is a tainted register, None otherwise *)
let find state var =
List.Assoc.find state ~equal:Var.(=) var
(** returns the tid associated with a tainted register *)
let find_exn state var =
Option.value_exn (find state var)
(** only remove the register var from the list of tainted registers *)
let remove_var state var =
List.Assoc.remove state ~equal:Var.(=) var
(** filters out all registers from the state with the same tid *)
let remove_tid state var =
let tid = find_exn state var in
List.filter state ~f:(fun (_, state_elem_tid) -> not (state_elem_tid = tid))
(** two states are equal if they contain the same set of tainted registers*)
let equal state1 state2 =
(List.length state1) = (List.length state2) &&
not (List.exists state1 ~f:(fun (var, tid) -> Option.is_none (find state2 var) ))
(** The union of two states is the union of the tainted registers*)
let union state1 state2 =
List.fold state2 ~init:state1 ~f:(fun state (var, tid) ->
if Option.is_some (find state var) then
state
else
(var, tid) :: state
)
(** remove virtual registers from the state (useful at the end of a block) *)
let remove_virtual_registers state =
List.filter state ~f:(fun (var, tid) -> Var.is_physical var)
end
(* check whether an expression contains an unchecked value. *)
let rec contains_unchecked exp state : access_type =
match exp with
| Bil.Load(mem, addr, _, _)->
begin
let acc = contains_unchecked addr state in
match acc with
| MemAccess(_) -> acc
| Access(var) -> MemAccess(var)
| NoAccess -> NoAccess
end
| Bil.Store(mem, addr, val_expression, _,_) ->
begin
let acc = union_access (contains_unchecked addr state) (contains_unchecked val_expression state) in
match acc with
| MemAccess(_) -> acc
| Access(var) -> MemAccess(var)
| NoAccess -> NoAccess
end
| Bil.BinOp(_, exp1, exp2) -> union_access (contains_unchecked exp1 state) (contains_unchecked exp2 state)
| Bil.UnOp(_, exp) -> contains_unchecked exp state
| Bil.Var(var) ->
begin
match State.find state var with
| Some(_) -> Access(var)
| None -> NoAccess
end
| Bil.Int(_) -> NoAccess
| Bil.Cast(_, _, exp) -> contains_unchecked exp state
| Bil.Let(var, exp1, exp2) ->
union_access_triple (contains_unchecked exp1 state) (contains_unchecked exp2 state) (contains_unchecked (Bil.var var) state)
| Bil.Unknown(_) -> NoAccess
| Bil.Ite(if_, then_, else_) ->
union_access_triple (contains_unchecked if_ state) (contains_unchecked then_ state) (contains_unchecked else_ state)
| Bil.Extract(_,_, exp) -> contains_unchecked exp state
| Bil.Concat(exp1, exp2) -> union_access (contains_unchecked exp1 state) (contains_unchecked exp2 state)
(* If an formerly unchecked return value was checked then remove all registers pointing
to the source of this return value from state. *)
let checks_value exp state : State.t =
match exp with
| Bil.Ite(if_, then_, else_) -> begin
match contains_unchecked if_ state with
| Access(var) ->
(* We filter out all registers with the same generating tid, since we have checked
the return value of this source *)
State.remove_tid state var
| MemAccess(_) (* This is a memory access before checking the return value, so do nothing here. *)
| NoAccess -> state
end
| _ -> state
let append_to_hits (cwe_hits:Tid.t list ref) (tid:Tid.t) =
match List.find cwe_hits.contents ~f:(fun elem -> elem = tid) with
| Some(_) -> ()
| None -> (cwe_hits := (tid :: cwe_hits.contents))
(** flags any access (not just memory access) from an unchecked source as a cwe_hit. *)
let flag_any_access exp state ~cwe_hits =
match contains_unchecked exp state with
| MemAccess(var) | Access(var) ->
let tid = State.find_exn state var in
append_to_hits cwe_hits tid;
State.remove_tid state var
| NoAccess -> state
(** flag all unchecked registers as cwe_hits, return empty state *)
let flag_all_unchecked_registers state ~cwe_hits =
let () = List.iter state ~f:(fun (var, tid) ->
append_to_hits cwe_hits tid) in
[]
(** Updates the state depending on the def. If memory is accessed using an unchecked return value,
then the access is added to the list of cwe_hits. *)
let update_state_def def state ~cwe_hits =
let (lhs, rhs) = (Def.lhs def, Def.rhs def) in
let state = checks_value rhs state in
match contains_unchecked rhs state with
| MemAccess(var) -> begin (* we found a case of unchecked return value *)
let tid = State.find_exn state var in
append_to_hits cwe_hits tid;
State.remove_tid state var
end
| Access(var) -> (* taint the lhs as an unchecked return value *)
let tid = State.find_exn state var in
State.add state lhs tid
| NoAccess -> (* no access to an unchecked return value in rhs. Since lhs is overwritten, it cannot be an unchecked return value anymore. *)
State.remove_var state lhs
(** Taint the return registers of a function as unchecked return values. *)
let taint_return_registers func_tid state ~program ~block =
let func = Term.find_exn sub_t program func_tid in
let arguments = Term.enum arg_t func in
(* Every return register is tainted as unchecked return value. *)
Seq.fold arguments ~init:state ~f:(fun state arg ->
match Bap.Std.Arg.intent arg with
| None | Some(In) -> state
| Some(Out) | Some(Both) ->
let variable = match Bap.Std.Arg.rhs arg with
| Bil.Var(var) -> var
| _ -> failwith "[CWE476] Return register wasn't a register." in
State.add state variable (Term.tid block)
)
(** Updates the state depending on the jump. On a jump to a function from the function list
taint all return registers as unchecked return values. *)
let update_state_jmp jmp state ~cwe_hits ~function_names ~program ~block ~strict_call_policy =
(* first check the guard condition for unchecked access. Any normal access clears the access from being unchecked *)
let condition_exp = Jmp.cond jmp in
let state = begin
match contains_unchecked condition_exp state with
| Access(var) ->
State.remove_tid state var
| MemAccess(var) -> (* a memory access using an unchecked value is still an error *)
let tid = State.find_exn state var in
let () = append_to_hits cwe_hits tid in
State.remove_tid state var
| NoAccess -> state
end in
match Jmp.kind jmp with
| Goto(Indirect(exp)) -> flag_any_access exp state cwe_hits
| Goto(Direct(_)) -> state
| Ret(_) -> if strict_call_policy then
flag_all_unchecked_registers state cwe_hits
else
state
| Int(_, _) -> flag_all_unchecked_registers state cwe_hits
| Call(call) ->
let state = match Call.return call with
| Some(Indirect(exp)) -> flag_any_access exp state cwe_hits
| _ -> state in
let state = match Call.target call with
| Indirect(exp) -> flag_any_access exp state cwe_hits
| _ -> state in
let state = match strict_call_policy with
| true -> (* all unchecked registers get flagged as hits *)
flag_all_unchecked_registers state cwe_hits
| false -> (* we assume that the callee will check all remaining unchecked values *)
[] in
match Call.target call with
| Indirect(_) -> state (* already handled above *)
| Direct(tid) ->
if List.exists function_names ~f:(fun elem -> String.(=) elem (Tid.name tid)) then
taint_return_registers tid state program block
else
state
(** updates a block analysis.
The strict call policy decides the behaviour on call and return instructions:
strict: all unchecked values get flagged as cwe-hits
non-strict: the state gets cleared, it is assumed that the target of the call/return
instruction checks all remaining unchecked values. *)
let update_block_analysis block register_state ~cwe_hits ~function_names ~program ~strict_call_policy =
let elements = Blk.elts block in
let register_state = Seq.fold elements ~init:register_state ~f:(fun state element ->
match element with
| `Def def -> update_state_def def state ~cwe_hits
| `Phi phi -> state (* We ignore phi terms for this analysis. *)
| `Jmp jmp -> update_state_jmp jmp state ~cwe_hits ~function_names ~program ~block ~strict_call_policy
) in
State.remove_virtual_registers register_state (* virtual registers should not be accessed outside of the block where they are defined. *)
let print_hit tid ~sub ~function_names ~tid_map =
let block = Option.value_exn (Term.find blk_t sub tid) in
let jmps = Term.enum jmp_t block in
let _ = Seq.find_exn jmps ~f:(fun jmp ->
match Jmp.kind jmp with
| Call(call) -> begin
match Call.target call with
| Direct(call_tid) -> Option.is_some (List.find function_names ~f:(fun name ->
if name = (Tid.name call_tid) then begin
Log_utils.warn "[%s] {%s} (NULL Pointer Dereference) There is no check if the return value is NULL at %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)
(Address_translation.translate_tid_to_assembler_address_string tid tid_map)
name;
true
end else
false
))
| _ -> false
end
| _ -> assert(false))
let check_cwe prog proj tid_map symbol_names =
match symbol_names with
| hd::[] ->
let symbols = Symbol_utils.build_symbols hd prog in
let calls = call_finder#run prog [] in
let relevant_calls = filter_calls_to_symbols calls symbols in
check_calls relevant_calls prog proj tid_map symbols check_null_pointer
| _ -> failwith "[CWE476] symbol_names not as expected"
| _ -> false
) in ()
let check_cwe prog proj tid_map symbol_names parameters =
let symbols = match symbol_names with
| hd :: _ -> hd
| _ -> failwith "[CWE476] symbol_names not as expected" in
let (strict_call_policy_string, max_steps_string) = match parameters with
| par1 :: par2 :: _ -> (par1, par2)
| _ -> failwith "[CWE476] parameters not as expected" in
let strict_call_policy = match String.split strict_call_policy_string ~on:'=' with
| "strict_call_policy" :: policy :: [] -> bool_of_string policy
| _ -> failwith "[CWE476] parameters not as expected" in
let max_steps = match String.split max_steps_string ~on:'=' with
| "max_steps" :: num :: [] -> int_of_string num
| _ -> failwith "[CWE476] parameters not as expected" in
let function_names = List.map symbols ~f:(fun symb -> "@" ^ symb) in
let subfunctions = Term.enum sub_t prog in
Seq.iter subfunctions ~f:(fun subfn ->
let cfg = Sub.to_cfg subfn in
let cwe_hits = ref [] in
let empty = Map.empty Graphs.Ir.Node.comparator in
let init = Graphlib.Std.Solution.create empty [] in
let equal = State.equal in
let merge = State.union in
let f = (fun node state ->
let block = Graphs.Ir.Node.label node in
update_block_analysis block state ~cwe_hits ~function_names ~program:prog ~strict_call_policy
) in
let _ = Graphlib.Std.Graphlib.fixpoint (module Graphs.Ir) cfg ~steps:max_steps ~rev:false ~init:init ~equal:equal ~merge:merge ~f:f in
List.iter (!cwe_hits) ~f:(fun hit -> print_hit hit ~sub:subfn ~function_names ~tid_map)
)
(** This module implements a check for CWE-476 (NULL Pointer Dereference).
It checks if the result of a function that may return a NULL value is checked immediately
for NULL. The symbols are configurable in config.json.
See https://cwe.mitre.org/data/definitions/476.html for detailed description. *)
val name: string
It checks if the result of a function that may return a NULL value is checked
for NULL before any memory gets accessed using the return values. The symbols
are configurable in config.json. See https://cwe.mitre.org/data/definitions/476.html
for detailed description.
Parameters:
- strict_call_policy={true, false}: Determines behaviour on call and return instructions.
If false, we assume that the callee, resp. the caller on a return instruction,
checks all unchecked values still contained in the registers. If true, every
unchecked value on a call or return instruction gets reported.
- max_steps=<num>: Max number of steps for the dataflow fixpoint algorithm.
Notes: The check relies on Bap-generated stubs to identify return registers of the
checked functions. Therefore it only works for functions for which Bap generates
these stubs. *)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
......@@ -35,7 +35,7 @@ let resolve_symbols prog symbols =
Seq.filter ~f:(fun s -> List.exists ~f:(fun x -> x = Sub.name s) symbols)
let check_cwe prog proj tid_map symbol_names =
let check_cwe prog proj tid_map symbol_names _ =
match symbol_names with
| hd::[] ->
let subfunctions = Term.enum sub_t prog in
......@@ -43,4 +43,3 @@ let check_cwe prog proj tid_map symbol_names =
get_calls_to_symbols cg subfunctions (resolve_symbols prog hd)
|> print_calls ~tid_map:tid_map
| _ -> failwith "[CWE676] symbol_names not as expected"
......@@ -5,4 +5,4 @@ See https://cwe.mitre.org/data/definitions/676.html for detailed description. *)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
......@@ -15,5 +15,5 @@ let handle_sub sub program tid_map symbols =
else
()
let check_cwe program proj tid_map symbols =
let check_cwe program proj tid_map symbols _ =
Seq.iter (Term.enum sub_t program) ~f:(fun s -> handle_sub s program tid_map symbols)
......@@ -4,4 +4,4 @@ https://cwe.mitre.org/data/definitions/782.html *)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
......@@ -95,6 +95,10 @@
"CWE476": {
"_comment": "any function that possibly returns a NULL value.",
"_comment1": "included functions of the following libs: stdlib.h, locale.h, stdio.h, cstring.h, wchar.h",
"parameters": [
"strict_call_policy=true",
"max_steps=100"
],
"symbols": [
"malloc",
"calloc",
......
......@@ -7,24 +7,25 @@ open Yojson.Basic.Util
include Self()
type cwe_module = {
cwe_func : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> unit;
cwe_func : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit;
name : string;
version : string;
requires_pairs : bool;
has_parameters : bool;
}
let known_modules = [{cwe_func = Cwe_190.check_cwe; name = Cwe_190.name; version = Cwe_190.version; requires_pairs = false};
{cwe_func = Cwe_215.check_cwe; name = Cwe_215.name; version = Cwe_215.version; requires_pairs = false};
{cwe_func = Cwe_243.check_cwe; name = Cwe_243.name; version = Cwe_243.version; requires_pairs = true};
{cwe_func = Cwe_248.check_cwe; name = Cwe_248.name; version = Cwe_248.version; requires_pairs = false};
{cwe_func = Cwe_332.check_cwe; name = Cwe_332.name; version = Cwe_332.version; requires_pairs = true};
{cwe_func = Cwe_367.check_cwe; name = Cwe_367.name; version = Cwe_367.version; requires_pairs = true};
{cwe_func = Cwe_426.check_cwe; name = Cwe_426.name; version = Cwe_426.version; requires_pairs = false};
{cwe_func = Cwe_457.check_cwe; name = Cwe_457.name; version = Cwe_457.version; requires_pairs = false};
{cwe_func = Cwe_467.check_cwe; name = Cwe_467.name; version = Cwe_467.version; requires_pairs = false};
{cwe_func = Cwe_476.check_cwe; name = Cwe_476.name; version = Cwe_476.version; requires_pairs = false};
{cwe_func = Cwe_676.check_cwe; name = Cwe_676.name; version = Cwe_676.version; requires_pairs = false};
{cwe_func = Cwe_782.check_cwe; name = Cwe_782.name; version = Cwe_782.version; requires_pairs = false}]
let known_modules = [{cwe_func = Cwe_190.check_cwe; name = Cwe_190.name; version = Cwe_190.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_215.check_cwe; name = Cwe_215.name; version = Cwe_215.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_243.check_cwe; name = Cwe_243.name; version = Cwe_243.version; requires_pairs = true; has_parameters = false};
{cwe_func = Cwe_248.check_cwe; name = Cwe_248.name; version = Cwe_248.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_332.check_cwe; name = Cwe_332.name; version = Cwe_332.version; requires_pairs = true; has_parameters = false};
{cwe_func = Cwe_367.check_cwe; name = Cwe_367.name; version = Cwe_367.version; requires_pairs = true; has_parameters = false};
{cwe_func = Cwe_426.check_cwe; name = Cwe_426.name; version = Cwe_426.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_457.check_cwe; name = Cwe_457.name; version = Cwe_457.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_467.check_cwe; name = Cwe_467.name; version = Cwe_467.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_476.check_cwe; name = Cwe_476.name; version = Cwe_476.version; requires_pairs = false; has_parameters = true};
{cwe_func = Cwe_676.check_cwe; name = Cwe_676.name; version = Cwe_676.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_782.check_cwe; name = Cwe_782.name; version = Cwe_782.version; requires_pairs = false; has_parameters = false}]
let build_version_sexp () =
List.map known_modules ~f:(fun cwe -> Format.sprintf "(\"%s\" \"%s\")" cwe.name cwe.version)
......@@ -36,15 +37,18 @@ let print_module_versions () =
(build_version_sexp ())
let execute_cwe_module cwe json program project tid_address_map =
let parameters = match cwe.has_parameters with
| false -> []
| true -> Json_utils.get_parameter_list_from_json json cwe.name in
if cwe.requires_pairs = true then
begin
let symbol_pairs = Json_utils.get_symbol_lists_from_json json cwe.name in
cwe.cwe_func program project tid_address_map symbol_pairs
cwe.cwe_func program project tid_address_map symbol_pairs parameters
end
else
begin
let symbols = Json_utils.get_symbols_from_json json cwe.name in
cwe.cwe_func program project tid_address_map [symbols]
cwe.cwe_func program project tid_address_map [symbols] parameters
end
let partial_run project config modules =
......
......@@ -23,3 +23,10 @@ let get_symbol_lists_from_json json cwe =
|> filter_member "pairs"
|> flatten
|> List.map ~f:(fun l -> List.map (to_list l) ~f:to_string)
let get_parameter_list_from_json json cwe =
[json]
|> filter_member cwe
|> filter_member "parameters"
|> flatten
|> List.map ~f:to_string
......@@ -2,3 +2,4 @@
val get_symbol_lists_from_json : Yojson.Basic.json -> string -> string list list
val get_symbols_from_json : Yojson.Basic.json -> string -> string list
val get_parameter_list_from_json : Yojson.Basic.json -> string -> string list
#include <stdlib.h>
void func1(){
void* data = malloc(20);
void* data = malloc(20000);
if (data == NULL){
exit(42);
}
......@@ -9,7 +9,8 @@ void func1(){
}
void func2(){
void* data = malloc(20);
int* data = malloc(200000);
printf("%i", data[0]);
free(data);
}
......
......@@ -10,114 +10,117 @@ CPP_ARM=arm-linux-gnueabi-g++-5
CPP_MIPS=mips-linux-gnu-g++-5
CPP_PPC=powerpc-linux-gnu-g++-5
CFLAGS_X64=-O0 -g -fno-stack-protector -std=c11
CFLAGS_X86=-O0 -g -m32 -fno-stack-protector -std=c11
CFLAGS_ARM=-O0 -g -fno-stack-protector -std=c11
CFLAGS_MIPS=-O0 -g -fno-stack-protector -std=c11
CFLAGS_PPC=-O0 -g -fno-stack-protector -std=c11
CPPFLAGS_X64=-O0 -g -fno-stack-protector
CPPFLAGS_X86=-O0 -g -m32 -fno-stack-protector
CPPFLAGS_ARM=-O0 -g -fno-stack-protector
CPPFLAGS_MIPS=-O0 -g -fno-stack-protector
CPPFLAGS_PPC=-O0 -g -fno-stack-protector
CFLAGS_X64=-g -fno-stack-protector -std=c11
CFLAGS_X86=-g -m32 -fno-stack-protector -std=c11
CFLAGS_ARM=-g -fno-stack-protector -std=c11
CFLAGS_MIPS=-g -fno-stack-protector -std=c11
CFLAGS_PPC=-g -fno-stack-protector -std=c11
CPPFLAGS_X64=-g -fno-stack-protector
CPPFLAGS_X86=-g -m32 -fno-stack-protector
CPPFLAGS_ARM=-g -fno-stack-protector
CPPFLAGS_MIPS=-g -fno-stack-protector
CPPFLAGS_PPC=-g -fno-stack-protector
OPTIMIZE=-O3
NO_OPTIMIZE=-O0
define compile_x64
@echo "Compiling x64 target:" $(1)
$(CC_x64) $(CFLAGS_X64) -o build/$(1)_x64.out $(1).c
$(CC_x64) $(CFLAGS_X64) $(2) -o build/$(1)_x64.out $(1).c
execstack -s build/$(1)_x64.out
endef
define compile_x64_cpp
@echo "Compiling x64 target:" $(1)
$(CPP_x64) $(CPPFLAGS_X64) -o build/$(1)_x64.out $(1).cpp
$(CPP_x64) $(CPPFLAGS_X64) $(2) -o build/$(1)_x64.out $(1).cpp
execstack -s build/$(1)_x64.out
endef
define compile_x86
@echo "Compiling x86 target:" $(1)
$(CC_X86) $(CFLAGS_X86) -o build/$(1)_x86.out $(1).c
$(CC_X86) $(CFLAGS_X86) $(2) -o build/$(1)_x86.out $(1).c
execstack -s build/$(1)_x86.out
endef
define compile_x86_cpp
@echo "Compiling x86 target:" $(1)
$(CPP_X86) $(CPPFLAGS_X86) -o build/$(1)_x86.out $(1).cpp
$(CPP_X86) $(CPPFLAGS_X86) $(2) -o build/$(1)_x86.out $(1).cpp
execstack -s build/$(1)_x86.out
endef
define compile_mips
@echo "Compiling mips target:" $(1)
$(CC_MIPS) $(CFLAGS_MIPS) -o build/$(1)_mips.out $(1).c
$(CC_MIPS) $(CFLAGS_MIPS) $(2) -o build/$(1)_mips.out $(1).c
execstack -s build/$(1)_mips.out
endef
define compile_mips_cpp
@echo "Compiling mips target:" $(1)
$(CPP_MIPS) $(CPPFLAGS_MIPS) -o build/$(1)_mips.out $(1).cpp
$(CPP_MIPS) $(CPPFLAGS_MIPS) $(2) -o build/$(1)_mips.out $(1).cpp
execstack -s build/$(1)_mips.out
endef
define compile_arm
@echo "Compiling arm target:" $(1)
$(CC_ARM) $(CFLAGS_ARM) -o build/$(1)_arm.out $(1).c
$(CC_ARM) $(CFLAGS_ARM) $(2) -o build/$(1)_arm.out $(1).c
execstack -s build/$(1)_arm.out
endef
define compile_arm_cpp
@echo "Compiling arm target:" $(1)
$(CPP_ARM) $(CPPFLAGS_ARM) -o build/$(1)_arm.out $(1).cpp
$(CPP_ARM) $(CPPFLAGS_ARM) $(2) -o build/$(1)_arm.out $(1).cpp
execstack -s build/$(1)_arm.out
endef
define compile_ppc
@echo "Compiling ppc target:" $(1)
$(CC_PPC) $(CFLAGS_PPC) -o build/$(1)_ppc.out $(1).c
$(CC_PPC) $(CFLAGS_PPC) $(2) -o build/$(1)_ppc.out $(1).c
execstack -s build/$(1)_ppc.out
endef
define compile_ppc_cpp
@echo "Compiling ppc target:" $(1)
$(CPP_PPC) $(CPPFLAGS_PPC) -o build/$(1)_ppc.out $(1).cpp
$(CPP_PPC) $(CPPFLAGS_PPC) $(2) -o build/$(1)_ppc.out $(1).cpp
execstack -s build/$(1)_ppc.out
endef
define compile_all
$(shell mkdir -p "build")
$(call compile_x64,$(1))
$(call compile_x86,$(1))
$(call compile_arm,$(1))
$(call compile_mips,$(1))
$(call compile_ppc,$(1))
$(call compile_x64,$(1),$(2))
$(call compile_x86,$(1),$(2))
$(call compile_arm,$(1),$(2))
$(call compile_mips,$(1),$(2))
$(call compile_ppc,$(1),$(2))
endef
define compile_all_cpp
$(shell mkdir -p "build")
$(call compile_x64_cpp,$(1))
$(call compile_x86_cpp,$(1))
$(call compile_arm_cpp,$(1))
$(call compile_mips_cpp,$(1))
$(call compile_ppc_cpp,$(1))
$(call compile_x64_cpp,$(1),$(2))
$(call compile_x86_cpp,$(1),$(2))
$(call compile_arm_cpp,$(1),$(2))
$(call compile_mips_cpp,$(1),$(2))
$(call compile_ppc_cpp,$(1),$(2))
endef
all:
$(call compile_all,c_constructs)
$(call compile_all,cwe_190)
$(call compile_all,cwe_243)
$(call compile_all,cwe_243_clean)
$(call compile_all_cpp,cwe_248)
$(call compile_all,cwe_332)
$(call compile_all,cwe_367)
$(call compile_all,cwe_415)
$(call compile_all,cwe_426)
$(call compile_all,cwe_457)
$(call compile_all,cwe_467)
$(call compile_all,cwe_476)
$(call compile_all,cwe_478)
$(call compile_all,cwe_676)
$(call compile_x64,cwe_782)
$(call compile_all,arrays)
$(call compile_all,memory_access)
$(call compile_all,c_constructs,$(NO_OPTIMIZE))
$(call compile_all,cwe_190,$(NO_OPTIMIZE))
$(call compile_all,cwe_243,$(NO_OPTIMIZE))
$(call compile_all,cwe_243_clean,$(NO_OPTIMIZE))
$(call compile_all_cpp,cwe_248,$(NO_OPTIMIZE))
$(call compile_all,cwe_332,$(NO_OPTIMIZE))
$(call compile_all,cwe_367,$(NO_OPTIMIZE))
$(call compile_all,cwe_415,$(NO_OPTIMIZE))
$(call compile_all,cwe_426,$(NO_OPTIMIZE))
$(call compile_all,cwe_457,$(NO_OPTIMIZE))
$(call compile_all,cwe_467,$(NO_OPTIMIZE))
$(call compile_all,cwe_476,$(OPTIMIZE))
$(call compile_all,cwe_478,$(NO_OPTIMIZE))
$(call compile_all,cwe_676,$(NO_OPTIMIZE))
$(call compile_x64,cwe_782,$(NO_OPTIMIZE))
$(call compile_all,arrays,$(NO_OPTIMIZE))
$(call compile_all,memory_access,$(NO_OPTIMIZE))
clean:
rm -rf build
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