Commit 7bca782b by Thomas Barabosch

Merge branch 'master' into acceptance_tests

parents 7fd7b2d0 3199b676
......@@ -3,6 +3,7 @@
- Refactoring: Unification of cwe_checker function interface
- Refactoring: Created utils module for JSON functionality
- Added check for CWE 248: Uncaught Exception
0.1 (2018-10-08)
=====
......
......@@ -9,6 +9,7 @@ Its main focus are ELF binaries that are commonly found on Linux and Unix operat
- [CWE-190](https://cwe.mitre.org/data/definitions/190.html): Integer Overflow or Wraparound
- [CWE-215](https://cwe.mitre.org/data/definitions/215.html): Information Exposure Through Debug Information
- [CWE-243](https://cwe.mitre.org/data/definitions/243.html): Creation of chroot Jail Without Changing Working Directory
- [CWE-248](https://cwe.mitre.org/data/definitions/248.html): Uncaught Exception
- [CWE-332](https://cwe.mitre.org/data/definitions/332.html): Insufficient Entropy in PRNG
- [CWE-367](https://cwe.mitre.org/data/definitions/367.html): Time-of-check Time-of-use (TOCTOU) Race Condition
- [CWE-426](https://cwe.mitre.org/data/definitions/426.html): Untrusted Search Path
......@@ -38,7 +39,7 @@ The second way is to utilize the installation script `install.sh`, which is just
The three way is to build it using the provided `Makefile`. In this case you must ensure that all dependencies are fulfilled:
- Ocaml 4.05.0
- Opam <= 1.2.2
- Opam 2.0.2
- BAP 1.5 (and its dependencies)
- yojson <= 1.4.1
- alcotest <= 0.8.3
......
open Bap.Std
open Core_kernel.Std
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)
(* 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
| Some(tid) -> Some(Tid.name tid)
| None -> None
(* check whether block contains a direct call to a symbol with name symbol_name *)
let contains_symbol block symbol_name =
match extract_direct_call_symbol block with
| Some(symb) -> symb = symbol_name
| None -> false
(* Checks whether a subfunction contains a catch block. *)
let contains_catch subfunction =
let blocks = Term.enum blk_t subfunction in
Seq.exists blocks (fun block -> contains_symbol block "@__cxa_begin_catch")
(* Find all calls to subfunctions that are reachable from this subfunction. The calls are returned
as a list, except for calls to "@__cxa_throw", which are logged as possibly uncaught exceptions. *)
let find_calls_and_throws subfunction ~tid_map =
let blocks = Term.enum blk_t subfunction in
Seq.fold blocks ~init:[] ~f:(fun call_list block ->
if contains_symbol block "@__cxa_throw" then
let () = print_uncatched_exception (Term.tid block) ~tid_map:tid_map in
call_list
else
match Symbol_utils.extract_direct_call_tid_from_block block with
| Some(tid) -> tid :: call_list
| None -> call_list
)
(* find exception throws with for which an exception handler was not necessarily allocated beforehand.
The return value is a list of all already checked functions.*)
let rec find_uncaught_exceptions subfunction already_checked_functions program ~tid_map =
if contains_catch subfunction then
(* This function contains a catch so we assume every throw reachable from here is catched. *)
already_checked_functions
else
let subfunction_calls = find_calls_and_throws subfunction ~tid_map:tid_map in
List.fold subfunction_calls ~init:already_checked_functions ~f:(fun already_checked subfunc ->
match List.exists ~f:(fun a -> a = subfunc) already_checked with
| true -> already_checked
| false -> find_uncaught_exceptions ~tid_map:tid_map (Core_kernel.Option.value_exn (Term.find sub_t program subfunc)) (subfunc :: already_checked) 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 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
()
(** This module implements a check for CWE-248 (Uncaught Exception)
An uncaught exception may lead to a crash and subsequentially to other unintended behavior.
See https://cwe.mitre.org/data/definitions/248.html for detailed description.
Right now we search for exception throws that are reachable in the callgraph without
touching a function that contains a catch block. We do not check whether a catch block
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
......@@ -10,6 +10,9 @@
["chdir", "chroot", "setreuid"], ["chdir", "chroot", "setuid"]],
"_comment": "valid chroot pathes according to http://www.unixwiz.net/techtips/chroot-practices.html"
},
"CWE248": {
"symbols": []
},
"CWE332": {
"pairs": [["srand", "rand"]]
},
......
......@@ -16,6 +16,7 @@ type cwe_module = {
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};
......
......@@ -129,3 +129,21 @@ let get_symbol_call_count_of_sub symbol_name sub prog =
end
| _ -> 0
let extract_direct_call_tid_from_block block =
let jmp_instructions = Term.enum jmp_t block in
Seq.fold jmp_instructions ~init:None ~f:(fun already_found instr ->
match already_found with
| Some(symb) -> Some(symb)
| None ->
match Jmp.kind instr with
| Goto _ | Ret _ | Int (_,_) -> None
| Call dst -> match Call.target dst with
| Direct tid ->
Some(tid)
| _ -> None)
let get_program_entry_points program =
let subfunctions = Term.enum sub_t program in
let entry_points = Seq.filter subfunctions ~f:(fun subfn -> Term.has_attr subfn Sub.entry_point) in
let main_fn = Seq.filter subfunctions ~f:(fun subfn -> "@main" = Tid.name (Term.tid subfn)) in
Seq.append main_fn entry_points
......@@ -60,3 +60,11 @@ val get_direct_callsites_of_sub :
(** Returns call count of symbol in function *)
val get_symbol_call_count_of_sub : string -> Bap.Std.Sub.t -> Bap.Std.Program.t -> int
(** Returns Some(target tid) if the block contains a direct call or None if it does not. *)
val extract_direct_call_tid_from_block : Bap.Std.blk Bap.Std.term -> Bap.Std.tid option
(** Returns a sequence of all entry points of the program.
TODO: The _start entry point usually calls a libc-function which then calls the main function. Since right now only direct
calls are tracked, our graph traversal may never find the main function. For now, we add it by hand to the entry points. *)
val get_program_entry_points : Bap.Std.program Bap.Std.term -> Bap.Std.sub Bap.Std.term Bap.Std.Seq.t
#include <iostream>
using namespace std;
void throw_exception(int i) {
cout<< " Throwing exception "<< i << endl;
throw i;
}
void do_catch(int i) {
try {
throw i;
}
catch(int error) {
cout<<"Exception " << i << "successfully catched."<<endl;
}
}
void maybe_catch(int i) {
if(i<42) {
try {
throw_exception(i);
}
catch(int errror) {
// Yay, catched.
cout<<"Exception " << i << " successfully catched."<<endl;
}
}
else {
// We don't catch anything here.
throw_exception(i);
}
}
int main() {
cout<<"Enter a number." <<endl;
int i;
cin >> i;
maybe_catch(i);
do_catch(i);
// For good measure, just throw an exception here.
throw (i+20);
}
#!/bin/bash
echo "Installing cross compiler for ARM architecture."
sudo apt install -y gcc-arm-linux-gnueabi
sudo apt install -y gcc-multilib-arm-linux-gnueabi g++-arm-linux-gnueabi
echo "Installing cross compiler for MIPS architecture."
sudo apt install -y gcc-mips-linux-gnu
sudo apt install -y gcc-multilib-mips-linux-gnu g++-7-mips-linux-gnu
echo "Installing cross compiler for PPC architecture."
sudo apt install -y gcc-powerpc-linux-gnu
sudo apt install -y gcc-multilib-powerpc-linux-gnu g++-7-powerpc-linux-gnu
echo "Done."
......@@ -4,6 +4,12 @@ CC_ARM=arm-linux-gnueabi-gcc-7
CC_MIPS=mips-linux-gnu-gcc-7
CC_PPC=powerpc-linux-gnu-gcc-7
CPP_x64=g++
CPP_X86=g++
CPP_ARM=arm-linux-gnueabi-g++-7
CPP_MIPS=mips-linux-gnu-g++-7
CPP_PPC=powerpc-linux-gnu-g++-7
CFLAGS_X64=-O0 -g -fno-stack-protector
CFLAGS_X86=-O0 -g -m32 -fno-stack-protector
CFLAGS_ARM=-O0 -g -fno-stack-protector
......@@ -16,30 +22,60 @@ define compile_x64
execstack -s build/$(1)_x64.out
endef
define compile_x64_cpp
@echo "Compiling x64 target:" $(1)
$(CPP_x64) $(CFLAGS_X64) -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
execstack -s build/$(1)_x86.out
endef
define compile_x86_cpp
@echo "Compiling x86 target:" $(1)
$(CPP_X86) $(CFLAGS_X86) -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
execstack -s build/$(1)_mips.out
endef
define compile_mips_cpp
@echo "Compiling mips target:" $(1)
$(CPP_MIPS) $(CFLAGS_MIPS) -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
execstack -s build/$(1)_arm.out
endef
define compile_arm_cpp
@echo "Compiling arm target:" $(1)
$(CPP_ARM) $(CFLAGS_ARM) -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
execstack -s build/$(1)_ppc.out
endef
define compile_ppc_cpp
@echo "Compiling ppc target:" $(1)
$(CPP_PPC) $(CFLAGS_PPC) -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))
......@@ -49,11 +85,20 @@ define compile_all
$(call compile_ppc,$(1))
endef
define compile_all_cpp
$(shell mkdir -p "build")
$(call compile_x64_cpp,$(1))
$(call compile_arm_cpp,$(1))
$(call compile_mips_cpp,$(1))
$(call compile_ppc_cpp,$(1))
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)
......@@ -68,4 +113,3 @@ all:
clean:
rm -rf build
......@@ -22,6 +22,10 @@ function run_arch() {
printf_new
bap artificial_samples/build/cwe_243_clean_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
echo "cwe_248_$1"
printf_new
bap artificial_samples/build/cwe_248_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
echo "cwe_323_$1"
printf_new
bap artificial_samples/build/cwe_332_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
......
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