Commit f81cd3d1 by Thomas Barabosch

Release commit.

parents
# Created by https://www.gitignore.io/api/c,ocaml,python
### C ###
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
### OCaml ###
*.annot
*.cmo
*.cma
*.cmi
*.cmx
*.cmxs
*.cmxa
# ocamlbuild working directory
_build/
# ocamlbuild targets
*.byte
*.native
# oasis generated files
setup.data
setup.log
# Merlin configuring file for Vim and Emacs
.merlin
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
### Python Patch ###
.venv/
### Python.VirtualEnv Stack ###
# Virtualenv
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
[Bb]in
[Ii]nclude
[Ll]ib
[Ll]ib64
[Ll]ocal
[Ss]cripts
pyvenv.cfg
pip-selfcheck.json
# dont upload our real life zoo
test/real_world_samples
test/run_real_world_samples.sh
# End of https://www.gitignore.io/api/c,ocaml,python
# based on https://github.com/BinaryAnalysisPlatform/bap/blob/master/docker/Dockerfile
FROM ubuntu:xenial
MAINTAINER Thomas Barabosch <thomas.barabosch@fkie.fraunhofer.de>
RUN apt-get -y update && apt-get -y install \
build-essential \
curl \
git \
libx11-dev \
m4 \
pkg-config \
python-pip \
software-properties-common \
sudo \
unzip \
wget
RUN apt-get -y install opam binutils-multiarch clang libgmp-dev libzip-dev llvm-3.8-dev zlib1g-dev
RUN useradd -m bap && echo "bap:bap" | chpasswd && adduser bap sudo
RUN sed -i.bkp -e \
's/%sudo\s\+ALL=(ALL\(:ALL\)\?)\s\+ALL/%sudo ALL=NOPASSWD:ALL/g' \
/etc/sudoers
USER bap
WORKDIR /home/bap
# install Bap
RUN opam init --auto-setup --comp=4.05.0 --yes
RUN git clone -b testing --single-branch https://github.com/BinaryAnalysisPlatform/opam-repository.git
RUN opam repo add bap opam-repository
RUN opam update
RUN OPAMJOBS=1 opam depext --install bap --yes
RUN sudo pip install bap
# install CWE_Checker and dependencies
RUN OPAMJOBS=1 opam install yojson alcotest
COPY . /home/bap/cwe_checker/
RUN sudo chown -R bap:bap /home/bap/cwe_checker
ENV PATH="/home/bap/.opam/4.05.0/bin/:${PATH}"
RUN cd /home/bap/cwe_checker/src;\
bapbuild -r -Is checkers,utils -pkgs yojson,unix cwe_checker.plugin; \
bapbundle install cwe_checker.plugin; \
cd -
ENTRYPOINT ["opam", "config", "exec", "--"]
.phony: all
all:
cd src; bapbuild -r -Is checkers,utils -pkgs yojson,unix cwe_checker.plugin; bapbundle install cwe_checker.plugin; cd ..
test:
bapbuild -r -Is src,src/checkers,src/utils,test -pkgs yojson,unix,alcotest test/test_cwe_checker.byte
./test/test_cwe_checker.byte
clean:
bapbuild -clean
uninstall:
bapbundle remove cwe_checker.plugin
# cwe_checker #
## What is cwe_checker? ##
*cwe_checker* detects common bug classes such as use of dangerous functions and simple integer overflows. These bug classes are formally known as [Common Weakness Enumerations](https://cwe.mitre.org/) (CWEs). Its main goal is to aid analysts to quickly find vulnerable code paths.
Its main focus are ELF binaries that are commonly found on Linux and Unix operating systems. *cwe_checker* is built on top of [BAP](https://github.com/BinaryAnalysisPlatform/bap)(Binary Analysis Platform). By using BAP, we are not restricted to one low level instruction set architectures like Intel x86. BAP lifts several of them to one common intermediate represenetation (IR). cwe_checker implements its analyses on this IR. At time of writing, BAP 1.5 supports Intel x86/x64, ARM, MIPS, and PPC amongst others. Hence, this makes *cwe_checker* a valuable tool in firmware analysis.
*cwe_checker* implements a modular architecture that allows to add new analyses with ease. So far the following analyses are implemented:
- [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-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
- [CWE-457](https://cwe.mitre.org/data/definitions/457.html): Use of Uninitialized Variable
- [CWE-467](https://cwe.mitre.org/data/definitions/467.html): Use of sizeof() on a Pointer Type
- [CWE-476](https://cwe.mitre.org/data/definitions/476.html): NULL Pointer Dereference
- [CWE-676](https://cwe.mitre.org/data/definitions/676.html): Use of Potentially Dangerous Function
- [CWE-782](https://cwe.mitre.org/data/definitions/782.html): Exposed IOCTL with Insufficient Access Control
Please note that some of the above analyses only are partially implemented at the moment. Furthermore, false positives are to be expected due to shortcuts and the nature of static analysis.
*cwe_checker* comes with a script called `cwe_checker_to_ida`, which parses the output of *cwe_checker* and generates a IDAPython script. This script annotates the found CWEs in IDA Pro, which helps during manual analysis of a binary. The following screenshot shows some results:
![](https://github.com/fkie-cad/cwe_checker/raw/master/doc/images/example_ida_anotation.png "IDA Pro anotation")
## Why use cwe_checker? ##
The following arguments should convince you to give *cwe_checker* a try:
- it is very easy to setup, just build the Docker container!
- it analyzes ELF binaries of several CPU architectures including x86, ARM, MIPS, and PPC
- it is extensible due to its plugin-based architecture
- it is configureable, e.g. apply analyses to new APIs
- view results annotated in IDA Pro
- *cwe_checker* can be integrated as a plugin into [FACT](https://github.com/fkie-cad/FACT_core)
## How to install cwe_checker? ##
There are two ways to install cwe_checker. The recommended way is to utilize the installation script `install.sh`, which is just a wrapper around Docker. Make sure to have the latest version of Docker.
The second 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
- BAP 1.5 (and its dependencies)
- yojson 1.4.1
- alcotest 0.8.3
- Sark for IDA Pro annotations
Just run `make all` to compile and register the plugin with BAP.
## How to use cwe_checker? ##
The usage is straight forward: adjust the `config.json` (if needed) and call BAP with *cwe_checker* as a pass.
``` bash
bap PATH_TO_BINARY --pass=cwe-checker --cwe-checker-config=src/config.json
```
*cwe_checker* outputs to stdin. This output is parsable (sexep). There is a script `cwe_checker_to_ida` to visualize the results in IDA Pro.
## How to extend cwe_checker? ##
New plugins should be added to src/checkers. Implement a .ml and .mli file. See the existing modules for an interface description. If necessary add a section to `config.json` to allow users to configure your plugin. Finally, add your plugin to `cwe_checker.ml`.
### Contribute ###
Contributions are always welcomed. Just fork it and open a pull request!
## Acknowledgements ##
This project is partly financed by [German Federal Office for Information Security (BSI)](https://www.bsi.bund.de).
A special thanks goes out to the BAP community (especially the official gitter) for answering questions and discussing solutions.
## License
```
Copyright (C) 2018 - Fraunhofer FKIE (thomas.barabosch@fkie.fraunhofer.de)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Some plug-ins may have different licenses. If so, a license file is provided in the plug-in's folder.
```
import logging
colors = {"CWE190": "0xBBBBBB",
"CWE215": None,
"CWE243": None,
"CWE332": None,
"CWE367": "0xFF15AA",
"CWE426": "0xAA15FF",
"CWE457": "0x4286F4",
"CWE467": "0xFFD400",
"CWE476": "0x2AFF00",
"CWE676": "0x8833FF",
"CWE-782": "0xCC00BB",
}
class CweWarning(object):
def __init__(self, name, plugin_version, warning):
self.name = name
self.plugin_version = plugin_version
self.warning = warning
self.color = None
self.address = None
self.cwe_number = None
self.highlight = True
class CweWarningParser(object):
'''
Parses a CWE warning emitted by the BAP plugin CweChecker
'''
@staticmethod
def _remove_color(s):
'''
Removes 'color' from string
See https://stackoverflow.com/questions/287871/print-in-terminal-with-colors/293633#293633
'''
return s.replace('\x1b[0m', '').strip()
def parse(self, warning):
try:
splitted_line = warning.split('WARN')
cwe_warning = splitted_line[1].replace(
'u32', '').replace("64u", '').replace(':', '')
cwe_name = self._remove_color(cwe_warning.split(')')[0]) + ')'
cwe_name = cwe_name.split('{')[0].strip() + ' ' + cwe_name.split('}')[1].strip()
plugin_version = cwe_warning.split('{')[1].split('}')[0]
cwe_message = ')'.join(cwe_warning.split(')')[1:])
cwe_message = cwe_message.replace('.', '').replace('32u', '')
return CweWarning(cwe_name, plugin_version, cwe_message)
except Exception as e:
logging.error('[CweWarningParser] Error while parsing CWE warning: %s.' % str(e))
return None
class Parser(object):
def __init__(self, result_path):
self._result_path = result_path
self._parsers = {"CWE190": self._parse_cwe190,
"CWE215": self._not_highlighted,
"CWE243": self._not_highlighted,
"CWE332": self._not_highlighted,
"CWE367": self._parse_at,
"CWE426": self._parse_at,
"CWE457": self._parse_cwe457,
"CWE467": self._parse_cwe467,
"CWE476": self._parse_cwe476,
"CWE676": self._parse_cwe676,
"CWE782": self._parse_cwe782,
}
def _read_in_config(self):
lines = None
with open(self._result_path, "r") as f:
lines = f.readlines()
if not lines:
print("[Parser] Could not read in file %s" % self._result_path)
raise Exception()
return lines
def _not_highlighted(self, warning):
warning.highlight = False
return warning
def _parse_at(self, warning):
warning.address = warning.warning.split("at ")[-1].split()[0].strip()
return warning
def _parse_cwe190(self, warning):
if "multiplication" in warning.warning:
warning.address = warning.warning.split("multiplication ")[1].split()[0]
else:
warning.address = warning.warning.split("addition ")[1].split()[0]
return warning
def _parse_cwe457(self, warning):
warning.address = warning.warning.split("at ")[-1].split(":")[0].strip()
return warning
def _parse_cwe467(self, warning):
warning.address = warning.warning.split("at ")[1].split()[0]
return warning
def _parse_cwe476(self, warning):
warning.address = warning.warning.split("at ")[1].split()[0]
return warning
def _parse_cwe676(self, warning):
warning.address = warning.warning.split("(")[1].split(")")[0].split(":")[0]
return warning
def _parse_cwe782(self, warning):
warning.address = warning.warning.spit("(")[1].split(")")[0].strip()
return warning
def _extract_cwe_number(self, name):
tmp = name.split("]")[0]
return tmp[1:]
def parse(self):
result = []
cwe_parser = CweWarningParser()
lines = self._read_in_config()
for line in lines:
line = line.strip()
if 'WARN' in line:
try:
warning = cwe_parser.parse(line)
cwe_number = self._extract_cwe_number(warning.name)
warning.cwe_number = cwe_number
if cwe_number in self._parsers:
warning = self._parsers[cwe_number](warning)
warning.color = colors[warning.cwe_number]
if warning.address != "UNKNOWN":
result.append(warning)
else:
print("Warning: %s not supported." % cwe_number)
except Exception as e:
print('Error while parsing: %s' % str(e))
print(line)
return result
class IdaGenerator(object):
def __init__(self, results):
self._results = results
def generate(self):
script = "import sark\nimport idaapi\n"
for res in self._results:
if res.highlight:
script += "sark.Line(%s).color = %s\n" % (res.address, res.color)
script += "sark.Line(%s).comments.regular = '%s'\n" % (res.address, res.name)
script += "print('[ %s ] %s')\n" % (res.address, res.name)
else:
script += "print('[ GENERAL ] %s')\n" % res.name
return script
import argparse
from CweCheckerParser import Parser
from Generator import IdaGenerator
def parse_args():
parser = argparse.ArgumentParser(
description='Generates an anotation script for IDA Pro based on CweChecker results.')
parser.add_argument(
'-i', '--cwe_checker_result', type=str, required=True,
help='The path to the output of CweChecker.')
parser.add_argument(
'-o', '--anotation_script_output', type=str, required=True,
help='The output path of the anotation script.')
args = parser.parse_args()
return args
def save_generated_script(outpath, generated_script):
with open(outpath, "w") as f:
f.write(generated_script)
def main():
args = parse_args()
results = Parser(args.cwe_checker_result).parse()
generated_script = IdaGenerator(results).generate()
save_generated_script(args.anotation_script_output, generated_script)
if __name__ == '__main__':
main()
#!/usr/bin/env bash
echo "Cleaning up"
rm -rf src/_build
rm -f src/cwe_checker.plugin
echo "Building docker container"
docker build --build-arg http_proxy=$http_proxy --build-arg https_proxy=$https_proxy --build-arg HTTP_PROXY=$http_proxy --build-arg HTTPS_PROXY=$https_proxy -t cwe-checker .
exit 0
open Core_kernel.Std
open Bap.Std
open Symbol_utils
let name = "CWE190"
let version = "0.1"
let collect_muliplications = Exp.fold ~init:0 (object
inherit [Int.t] Exp.visitor
method! enter_binop op o1 o2 binops = match op with
| Bil.TIMES | Bil.LSHIFT -> binops + 1
| _ -> binops
end)
let contains_multiplication d =
let rhs = Def.rhs d in
let binops = collect_muliplications rhs in
binops > 0
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 check_cwe prog proj tid_map symbol_names =
let symbols = Symbol_utils.build_symbols symbol_names 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_multiplication_before_symbol
(** TODO
CWE-190 (Integer Overflow or Wraparound)
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 -> unit
open Core_kernel.Std
open Bap.Std
open Unix
(* TODO: IVG via gitter:
I see, so you need the CU information, and yes BAP doesn't provide this.
The right way to do this thing (a little bit complicated, but it
will preserve the abstractions), would be the following:
- Define the abstract interface for the CU providers (you can use Source module
or just define it manually with the interface you like)
- Write plugins that will provide implementations
(i.e., using readelf, objdump, IDA, LLVM, or whatever). The implementation shall
subscribe to Project.Info.file information stream and generate CU information every time a new file is open.
Of course, for the prototype your approach will work,
but in general case it is better to use the approach described above. *)
let name = "CWE215"
let version = "0.1"
let read_lines in_chan =
let lines = ref [] in
try
while true; do
lines := input_line in_chan :: !lines
done; !lines
with End_of_file ->
In_channel.close in_chan;
List.rev !lines
(* TODO: check if program contains strings like "DEBUG"*)
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
try
let in_chan = Unix.open_process_in cmd in
read_lines in_chan |> List.iter ~f:(fun l -> Log_utils.warn "[%s] {%s} (Information Exposure Through Debug Information) %s" name version l)
with
Unix.Unix_error (e,fm,argm) ->
Log_utils.error "[%s] {%s} %s %s %s" name version (Unix.error_message e) fm argm
end
| _ -> ()
(** This module checks if a binary contains sensitive debugging information that could be leveraged to
get a better understanding of it in less time. This is basically CWE-215 (https://cwe.mitre.org/data/definitions/215.html *)
val name : string
val version : string
val check_cwe : Bap.Std.project -> unit
open Core_kernel.Std
open Bap.Std
open Symbol_utils
include Self()
let name = "CWE243"
let version = "0.1"
let get_call_dests_of_blk blk_tid sub =
match Term.find blk_t sub blk_tid with
| Some blk -> begin
Term.enum jmp_t blk
|> Seq.filter_map ~f:(fun jmp -> match Jmp.kind jmp with
| Goto _ | Ret _ | Int (_,_) -> None
| Call destination -> begin
match Call.target destination with
| Direct addr -> Some addr
| _ -> None
end)
end |> Seq.to_list
| _ -> []
let get_call_dests_of_sub sub =
let entry_blk =(Term.first blk_t sub) in
match entry_blk with
| Some blk -> begin
let blks = Graphlib.Std.Graphlib.postorder_traverse (module Graphs.Tid) (Sub.to_graph sub) ~start:(Term.tid blk) ~rev:true in
List.concat_map (Seq.to_list blks) ~f:(fun blk -> get_call_dests_of_blk blk sub)
end
| _ -> []
let rec check dests symbols =
match dests with
| [] -> (List.length symbols) = 0
| hd :: tl ->
begin
match symbols with
| [] -> true
| first_symbol :: symbol_rest -> begin
match first_symbol.address with
| Some address -> if address = hd then check tl symbol_rest else check tl symbols
| _ -> false
end
end
let check_route sub symbols =
let call_dests = get_call_dests_of_sub sub in
let res = check call_dests symbols in
if res then res else res
(** Checks one possible valid path (combination of APIs) of chroot. *)
let check_path prog tid_map sub path =
let symbols = build_symbols path prog in
if List.length symbols = List.length path then
begin
if List.length symbols = List.length path then
check_route sub symbols
else
false
end
else
false
(** Checks a subfunction for CWE-243. Only functions that actually call "chroot" are considered.
It checks each of the configured VALID pathes found in config.json, e.g.
"chroot_pathes": [["chroot", "chdir"], ["chdir", "chroot", "setresuid"], ["chdir", "chroot", "seteuid"],
["chdir", "chroot", "setreuid"], ["chdir", "chroot", "setuid"]].
If all of them fail then we supose that the program handles chroot on
*)
let check_subfunction prog tid_map sub pathes =
if sub_calls_symbol prog sub "chroot" then
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)
end
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)
| _ -> ()
(** This module implements a check for CWE-243 (Creation of chroot Jail Without Changing Working Directory).
According to http://www.unixwiz.net/techtips/chroot-practices.html, there are several ways to achieve the
safe creation of a chroot jail, e.g. chdir -> chroot -> setuid. They are configurable in config.json.
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
open Bap.Std
open Core_kernel.Std
open Graph_utils
open Symbol_utils
let name = "CWE332"
let version = "0.1"
(* ToDo: Implement more checks for other PRNGs.
See https://wiki.openssl.org/index.php/Random_Numbers *)
let check_cwe program proj tid_map =
match Option.both (find_symbol program "srand") (find_symbol program "rand") with
| 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
end
| Some (srand_tid, rand_tid) -> ()
(** This module implements a check for CWE332 (Insufficient Entropy in PRNG).
This can happen, for instance, if the PRNG is not seeded. A classical example
would be calling rand without srand. This could lead to predictable random
numbers and could, for example, weaken crypto functionality.
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 -> unit
open Bap.Std
open Core_kernel.Std
let name = "CWE367"
let version = "0.1"
let get_calls_to_symbol symbol_name callsites program =
match Symbol_utils.find_symbol program symbol_name with
| Some symbol ->
begin
Seq.filter callsites ~f:(fun callsite -> match Jmp.kind callsite with
| Goto _ | Ret _ | Int (_,_) -> false
| Call destination -> match Call.target destination with
| Direct addr -> addr = symbol
| _ -> false)
end
| None -> Seq.empty
let get_blk_tid_of_tid sub tid =
let blk = Seq.find (Term.enum blk_t sub) ~f:(
fun b ->
match Term.last jmp_t b with
| Some last_term -> tid = (Term.tid last_term)
| None -> false) in
match blk with
| Some b -> Term.tid b
| _ -> assert(false)
let is_reachable sub source sink =
let cfg = Sub.to_graph sub in
let source_tid = Term.tid source in
let sink_tid = Term.tid sink in
let source_blk = get_blk_tid_of_tid sub source_tid in
let sink_blk = get_blk_tid_of_tid sub sink_tid in
Graphlib.Std.Graphlib.is_reachable (module Graphs.Tid) cfg source_blk sink_blk
let handle_sub sub program tid_map symbols source sink =
if (Symbol_utils.sub_calls_symbol program sub source) && (Symbol_utils.sub_calls_symbol program sub sink) then
begin
let calls = Symbol_utils.get_direct_callsites_of_sub sub in
let source_calls = get_calls_to_symbol source calls program in
let sink_calls = get_calls_to_symbol sink calls program in
Seq.iter source_calls ~f:(fun source_call ->
Seq.iter sink_calls ~f:(fun sink_call ->
if is_reachable sub source_call sink_call then
Log_utils.warn
"[%s] {%s} (Time-of-check Time-of-use Race Condition) %s is reachable from %s at %s (%s). This could lead to a TOCTOU."
name
version
sink
source
(Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map)
(Term.name sub)
else
()))
end
else
()
(* TODO: access -> open is just one example of a TOCTOU *)
let check_cwe program proj tid_map =
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")
(** TODO
CWE-367 (Time-of-check Time-of-use (TOCTOU) Race Condition)
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 -> unit
open Bap.Std
open Core_kernel.Std
let name = "CWE426"
let version = "0.1"
let calls_privilege_changing_sub sub program symbols =
List.exists symbols ~f:(fun s -> Symbol_utils.sub_calls_symbol program sub s)
let handle_sub sub program tid_map symbols =
if calls_privilege_changing_sub sub program symbols then
begin
if Symbol_utils.sub_calls_symbol program sub "system" then
Log_utils.warn "[%s] {%s} (Untrusted Search Path) sub %s at %s may be vulnerable to PATH manipulation."
name
version
(Term.name sub)
(Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map)
else
()
end
else ()
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)
(** This module checks for CWE-426 (Untrusted Search Path) (https://cwe.mitre.org/data/definitions/426.html). Basically, the program searches
for critical resources on an untrusted search path that can be adjusted by an adversary. For example, see Nebula Level 1
(https://exploit-exercises.com/nebula/level01/).
According to "man system" the following problems can arise:
"Do not use system() from a program with set-user-ID or set-group-ID privileges, because strange values for some environment variables might
be used to subvert system integrity. Use the exec(3) family of functions instead, but not execlp(3) or execvp(3). system() will not, in
fact, work properly from programs with set-user-ID or set-group-ID privileges on systems on which /bin/sh is bash version 2, since bash 2
drops privileges on startup. (Debian uses a modified bash which does not do this when invoked as sh.)"
*)
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 -> unit
open Core_kernel.Std
open Bap.Std
let name = "CWE457"
let version = "0.1"
let get_defs sub_ssa =
Term.enum blk_t sub_ssa
|> Seq.concat_map ~f:(fun blk -> Term.enum def_t blk)
let collect_stores_of_exp = Exp.fold ~init:0 (object
inherit [int] Exp.visitor
method! enter_store ~mem:_ ~addr:addr ~exp:exp _ _ stores =
stores + 1
end)
let exp_has_store e =
collect_stores_of_exp e > 0
let ints_of_exp = Exp.fold ~init:Word.Set.empty (object
inherit [Word.Set.t] Exp.visitor
method! enter_int i ints = Set.add ints i
end)
let vars_of_exp = Exp.fold ~init:Var.Set.empty (object
inherit [Var.Set.t] Exp.visitor
method! enter_var var vars = Set.add vars var
end)
let vars_contain_mem vars =
let mems = Set.filter vars ~f:(fun var -> match Var.to_string var with
| "mem" -> true
| _ -> false) in
Set.length mems > 0
(*FIXME: this is architecture dependent and ugly*)
let get_min_fp_offset arch =
match arch with
| `x86 | `x86_64 -> 0x10000
| _ -> 0x0
(*FIXME: this is architecture dependent and ugly*)
let get_fp_of_arch arch =
match arch with
| `x86 -> "EBP"
| `x86_64 -> "RBP"
| `armv4 | `armv5 | `armv6 | `armv7 | `armv4eb | `armv5eb | `armv6eb | `armv7eb -> "R11"
| `mips | `mips64 | `mips64el | `mipsel -> "FP"
| `ppc | `ppc64 | `ppc64le -> "R31"
| _ -> Log_utils.error "[%s] {%s} Unknown architecture." name version; "UNKNOWN"
let vars_contain_fp vars fp_pointer =
let regs = Set.filter vars ~f:(fun var -> Var.to_string var = fp_pointer) in
Set.length regs > 0
let is_interesting_load_store def fp_pointer =
let vars = vars_of_exp (Def.rhs def) in
let contains_fp = vars_contain_fp vars fp_pointer in
let contains_mem = vars_contain_mem vars in
contains_mem && contains_fp
(*TODO: implement real filtering*)
let filter_mem_address i min_fp_offset = Set.filter i ~f:(fun elem -> (Word.of_int ~width:32 min_fp_offset) < elem)
let check_subfunction prog proj tid_map sub =
let fp_pointer = get_fp_of_arch (Project.arch proj) in
let min_fp_offset = get_min_fp_offset (Project.arch proj) in
let stores = ref [||] in
let defs = get_defs sub in
Seq.iter defs ~f:(fun d ->
if is_interesting_load_store d fp_pointer then
let rhs = Def.rhs d in
let ints = ints_of_exp rhs in
begin
if exp_has_store rhs then
begin
let filter_mem_addresses = filter_mem_address ints min_fp_offset in
Set.iter filter_mem_addresses ~f:(fun addr -> stores := Array.append !stores [|addr|])
end
else
begin
let filter_mem_addresses = filter_mem_address ints min_fp_offset in
Set.iter filter_mem_addresses ~f:(fun i -> if not (Array.exists !stores ~f:(fun elem -> elem = i)) then
begin
Log_utils.warn "[%s] {%s} (Use of Uninitialized Variable) Found potentially unitialized stack variable (FP + %s) in function %s at %s"
name
version
(Word.to_string i)
(Sub.name sub)
(Address_translation.translate_tid_to_assembler_address_string (Term.tid d) tid_map)
end)
end
end)
let check_cwe prog proj tid_map =
Seq.iter (Term.enum sub_t prog) ~f:(fun sub -> check_subfunction prog proj tid_map sub)
(** This module implements a check for CWE-457 (Use of Uninitialized Variable).
TODO
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 -> unit
open Core_kernel.Std
open Bap.Std
open Symbol_utils
let name = "CWE467"
let version = "0.1"
let get_pointer_size arch =
Size.in_bytes @@ Arch.addr_size arch
let check_input_is_pointer_size proj prog sub blk jmp tid_map symbols =
Seq.iter (Term.enum def_t blk) ~f:(fun d -> match Exp.eval @@ Def.rhs d with
| Imm w ->
begin
try
if get_pointer_size (Project.arch proj) = (Word.to_int_exn w) then
Log_utils.warn "[%s] {%s} (Use of sizeof on a Pointer Type) sizeof on pointer 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)
with _ -> Log_utils.error "Caught exception in module [CWE467]."
end
| _ -> ())
let check_cwe prog proj tid_map symbol_names =
let symbols = Symbol_utils.build_symbols symbol_names 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_input_is_pointer_size
(** This module implements a check for CWE-467 (Use of sizeof() on a Pointer Type).
In a nutshell, it before a function call to symbols like malloc and memmove, which
take a size parameter and a pointer to data as input, if not accidentally the size
of the pointer instead of the data is passed. This can have severe consequences.
The check is quite basic: it checks if before the call an immediate value that
equals the size of a pointer (e.g. 4 bytes on x86) is referenced (e.g. pushed
onto the stack).The symbols are configurable in config.json.
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 -> unit
open Core_kernel.Std
open Bap.Std
open Symbol_utils
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)."
name
version
(Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map)
(Symbol_utils.get_symbol_name_from_jmp jmp symbols)
end
| _ -> assert(false))
let check_cwe prog proj tid_map symbol_names =
let symbols = Symbol_utils.build_symbols symbol_names 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
(** 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
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 -> unit
open Bap.Std
open Core_kernel.Std
let name = "CWE676"
let version = "0.1"
let get_call_to_target cg callee target =
Term.enum blk_t callee |>
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 dst -> match Call.target dst with
| Direct tid when tid = (Term.tid target) ->
Some (Term.name callee, Term.tid blk, Term.name target)
| _ -> None))
let get_calls_to_symbols cg subfunctions symbols =
(Seq.concat_map subfunctions ~f:(fun subfunction ->
Seq.concat_map symbols ~f:(fun symbol -> get_call_to_target cg subfunction symbol)))
(* FIXME: refactor variable names *)
let print_calls calls ~tid_map =
Seq.iter calls ~f:(fun call -> match call with
| (a, b, c) -> Log_utils.warn
"[%s] {%s} (Use of Potentially Dangerous Function) %s (%s) -> %s."
name
version
a
(Address_translation.translate_tid_to_assembler_address_string b tid_map)
c)
let resolve_symbols prog symbols =
Term.enum sub_t prog |>
Seq.filter ~f:(fun s -> List.exists ~f:(fun x -> x = Sub.name s) symbols)
let check_cwe prog tid_map symbols =
let subfunctions = Term.enum sub_t prog in
let cg = Program.to_graph prog in
get_calls_to_symbols cg subfunctions (resolve_symbols prog symbols)
|> print_calls ~tid_map:tid_map
(** This module implements a check for CWE-676 (Use of Potentially Dangerous Function)
Potentially dangerous functions like memcpy can lead to security issues like buffer overflows.
The functions are configurable in config.json.
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.word Bap.Std.Tid.Map.t -> string list -> unit
open Bap.Std
open Core_kernel.Std
let name = "CWE782"
let version = "0.1"
(*TODO: check if binary is setuid*)
let handle_sub sub program tid_map symbols =
if Symbol_utils.sub_calls_symbol program sub "ioctl" then
Log_utils.warn "[%s] {%s} (Exposed IOCTL with Insufficient Access Control) Program uses ioctl at %s (%s). Be sure to double check the program and the corresponding driver."
name
version
(Term.name sub)
(Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map)
else
()
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)
(** TODO
CWE-782 (Exposed IOCTL with Insufficient Access Control)
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 -> unit
{
"CWE190": {
"symbols": ["xmalloc", "malloc", "realloc"]
},
"CWE243": {
"chroot_pathes": [["chroot", "chdir"], ["chdir", "chroot", "setresuid"], ["chdir", "chroot", "seteuid"],
["chdir", "chroot", "setreuid"], ["chdir", "chroot", "setuid"]],
"_comment": "valid chroot pathes according to http://www.unixwiz.net/techtips/chroot-practices.html"
},
"CWE332": {
"init_rand_pairs": [["srand", "rand"]]
},
"CWE467": {
"symbols": ["strncmp", "malloc",
"alloca", "_alloca", "strncat", "wcsncat",
"strncpy", "wcsncpy", "stpncpy", "wcpncpy",
"memcpy", "wmemcpy", "memmove", "wmemmove", "memcmp", "wmemcmp"],
"_comment": "any function that takes something of type size_t could be a possible candidate."
},
"CWE476": {
"symbols": ["malloc", "calloc", "realloc", "getenv", "bsearch", "setlocale", "tmpfile", "tmpnam",
"fopen", "freopen", "fgets", "memchr", "strchr", "strpbrk", "strrchr", "strstr", "strtok",
"fgetws", "wcschr", "wcspbrk", "wcsrchr", "wcsstr", "wcstok", "wmemchr"],
"_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"
},
"CWE676": {
"_comment": "https://github.com/01org/safestringlib/wiki/SDL-List-of-Banned-Functions",
"symbols": ["alloca", "_alloca",
"scanf", "wscanf", "sscanf", "swscanf", "vscanf", "vsscanf",
"strlen", "wcslen", "strtok", "strtok_r", "wcstok",
"strcat", "strncat", "wcscat", "wcsncat",
"strcpy", "strncpy", "wcscpy", "wcsncpy", "stpcpy", "stpncpy", "wcpcpy", "wcpncpy",
"memcpy", "wmemcpy", "memmove", "wmemmove", "memcmp", "wmemcmp", "me​mset", "wmemset",
"gets", "sprintf​", "vsprintf", "swprintf", "vswprintf", "snprintf", "vsnprintf",
"realpath", "getwd", "wctomb", "wcrtomb", "wcstombs", "wcsrtombs", "wcsnrtombs"]
}
}
open Core_kernel.Std
open Bap.Std
open Graphlib.Std
open Format
open Yojson.Basic.Util
include Self()
let known_modules = [(Cwe_190.name, Cwe_190.version);
(Cwe_215.name, Cwe_215.version);
(Cwe_243.name, Cwe_243.version);
(Cwe_332.name, Cwe_332.version);
(Cwe_367.name, Cwe_367.version);
(Cwe_426.name, Cwe_426.version);
(Cwe_467.name, Cwe_467.version);
(Cwe_476.name, Cwe_476.version);
(Cwe_457.name, Cwe_457.version);
(Cwe_676.name, Cwe_676.version);
(Cwe_782.name, Cwe_782.version)]
let build_version_sexp () =
List.map known_modules ~f:(fun (name, version) -> Format.sprintf "(\"%s\" \"%s\")" name version)
|> String.concat ~sep:" "
let print_module_versions () =
Log_utils.info
"[cwe_checker] module_versions: (%s)"
(build_version_sexp ())
(** Extracts the symbols to check for from json document.
An example looks like this:
"CWE467": {
"symbols": ["strncmp", "malloc",
"alloca", "_alloca", "strncat", "wcsncat",
"strncpy", "wcsncpy", "stpncpy", "wcpncpy",
"memcpy", "wmemcpy", "memmove", "wmemmove", "memcmp", "wmemcmp"],
"_comment": "any function that takes something of type size_t could be a possible candidate."
}, *)
let get_symbols_from_json json cwe =
[json]
|> filter_member cwe
|> filter_member "symbols"
|> flatten
|> List.map ~f:to_string
let init_cwe_190 json project program tid_address_map =
let symbols = get_symbols_from_json json "CWE190" in
Cwe_190.check_cwe program project tid_address_map symbols
let init_cwe_215 json project program tid_address_map =
Cwe_215.check_cwe project
let init_cwe_243 json project program tid_address_map =
[json]
|> filter_member "CWE243"
|> filter_member "chroot_pathes"
|> flatten
|> List.map ~f:(fun l -> List.map (to_list l) ~f:to_string)
|> Cwe_243.check_cwe program project tid_address_map
let init_cwe_332 json project program tid_address_map =
(* TODO: read config. *)
Cwe_332.check_cwe program project tid_address_map
let init_cwe_367 json project program tid_address_map =
(* TODO: read config. *)
Cwe_367.check_cwe program project tid_address_map
let init_cwe_426 json project program tid_address_map =
(* TODO: read config. *)
let symbols = ["setresgid"; "setresuid"; "setuid"; "setgid"; "seteuid"; "setegid"] in
Cwe_426.check_cwe program project tid_address_map symbols
let init_cwe_457 json project program tid_address_map =
Cwe_457.check_cwe program project tid_address_map
let init_cwe_467 json project program tid_address_map =
let symbols = get_symbols_from_json json "CWE467" in
Cwe_467.check_cwe program project tid_address_map symbols
let init_cwe_476 json project program tid_address_map =
let symbols = get_symbols_from_json json "CWE476" in
Cwe_476.check_cwe program project tid_address_map symbols
let init_cwe_676 json project program tid_address_map =
let symbols = get_symbols_from_json json "CWE676" in
Cwe_676.check_cwe program tid_address_map symbols
let init_cwe_782 json project program tid_address_map =
(* TODO: read config and hand over symbols from man ioctl *)
let symbols = [] in
Cwe_782.check_cwe program project tid_address_map symbols
let partial_run project config modules =
(* IMPLEMENT ME: checkout how to dispatch ocaml modules dynamically *)
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 a partial update of %s." modules
let full_run project config =
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
begin
init_cwe_190 json project program tid_address_map;
init_cwe_215 json project program tid_address_map;
init_cwe_243 json project program tid_address_map;
init_cwe_332 json project program tid_address_map;
init_cwe_367 json project program tid_address_map;
init_cwe_426 json project program tid_address_map;
init_cwe_457 json project program tid_address_map;
init_cwe_467 json project program tid_address_map;
init_cwe_476 json project program tid_address_map;
init_cwe_676 json project program tid_address_map;
init_cwe_782 json project program 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 ();
if module_versions then
begin
print_module_versions ()
end
else
begin
if config = "" then
Log_utils.error "[cwe_checker] No configuration file provided! Aborting..."
else
begin
if partial_update = "" then
full_run project config
else
partial_run project config partial_update
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 partial_update = param string "partial" ~doc:"Comma separated list of modules to apply on binary."
let () = when_ready (fun ({get=(!!)}) -> Project.register_pass' ~deps:["callsites"] (main !!config !!module_versions !!partial_update))
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)"
]
end
open Core_kernel.Std
open Bap.Std
let translate_tid_to_assembler_address_string tid tid_map =
match Tid.Map.find tid_map tid with
| Some asm_addr -> Word.to_string asm_addr
| _ -> "UNKNOWN"
let generate_tid_map prog =
(object
inherit [addr Tid.Map.t] Term.visitor
method enter_term _ t addrs = match Term.get_attr t address with
| None -> addrs
| Some addr -> Map.add addrs ~key:(Term.tid t) ~data:addr
end)#run prog Tid.Map.empty
(** This module helps to translate between IR addresses and addresses found in the actual assembler code.
At first, a mapping between the two addressing schemes has to be computed with the function generate_tid_map.
Call this function once at start up.Then, we can translate IR addresses (Bap.Std.tid) to addresses
in assembler code (represented as string). *)
(** Generates a map that maps from TIDs to real addresses of the assembly code. *)
val generate_tid_map :
Bap.Std.program Bap.Std.term -> Bap.Std.word Bap.Std.Tid.Map.t
(** Translates a TID to a real address of the assembly code.
It requires a TID -> address mapping that can be generated with generate_tid_map. *)
val translate_tid_to_assembler_address_string :
Bap.Std.tid -> Bap.Std.word Bap.Std.Tid.Map.t -> string
open Core_kernel.Std
open Bap.Std
open Graphlib.Std
type path = {
start_node: Bap.Std.tid;
nodes: Bap.Std.tid array;
end_node: Bap.Std.tid;
}
let get_entry_blk_of_sub sub =
match Term.first blk_t sub with
| Some blk -> blk
| _ -> failwith "Could not determine first block of sub."
let print_path p =
Format.printf "%s\n" (Array.fold p.nodes ~init:"" ~f:(fun acc n -> acc ^ " -> " ^ (Tid.to_string n)))
let print_path_length p =
Format.printf "%d\n" (Array.length p.nodes)
(* ToDo: remove *)
let print_current_edge a b =
Format.printf "\t%s -> %s\n" (Tid.to_string a) (Tid.to_string b)
let fork_path current_path current_node =
let new_path = Array.append (Array.copy current_path.nodes) [|current_node|] in
{start_node = current_path.start_node; nodes = new_path; end_node = current_path.end_node;}
let node_already_visited_on_path node path =
node = path.start_node || Array.exists path.nodes ~f:(fun n -> n = node)
let rec get_all_paths_from_node node g current_path =
match Seq.to_list (Graphs.Tid.Node.succs node g) with
| [] -> [current_path]
| succs -> List.concat_map succs
~f:(fun succ ->
if node_already_visited_on_path succ current_path then
[]
else
get_all_paths_from_node succ g (fork_path current_path node))
(* Please mind the path explosion !!! *)
let enumerate_paths_between_blks sub blk_start_tid blk_end_tid limit =
let g = Sub.to_graph sub in
let pathes = get_all_paths_from_node blk_start_tid g {start_node = blk_start_tid; nodes = [||]; end_node = blk_end_tid} in
Format.printf "\tFound %d pathes.\n" (List.length pathes); []
(** This module implements functionality that works on graphs like the CFG.
Most of its functionality is implemented by using BAP's Graphlib.Std. *)
(* Copyright (c) 2014, INRIA.
* Copyright (c) 2013, Zhang Initiative Research Unit,
* Advance Science Institute, RIKEN
* 2-1 Hirosawa, Wako, Saitama 351-0198, Japan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *)
open Printf
(* localtime is used to date events, _not_ GMT, BEWARE SCIENTIST *)
type log_level =
| FATAL
| ERROR
| WARN
| INFO
| DEBUG
let int_of_level = function
| FATAL -> 4
| ERROR -> 3
| WARN -> 2
| INFO -> 1
| DEBUG -> 0
let string_of_level = function
| FATAL -> "FATAL"
| ERROR -> "ERROR"
| WARN -> "WARN "
| INFO -> "INFO "
| DEBUG -> "DEBUG"
let level_of_string = function
| "FATAL" | "fatal" -> FATAL
| "ERROR" | "error" -> ERROR
| "WARN" | "warn" -> WARN
| "INFO" | "info" -> INFO
| "DEBUG" | "debug" -> DEBUG
| str -> failwith ("no such log level: " ^ str)
type color = Black | Red | Green | Yellow | Blue | Magenta | Cyan | White
| Default
(* ANSI terminal colors for UNIX *)
let color_to_string = function
| Black -> "\027[30m"
| Red -> "\027[31m"
| Green -> "\027[32m"
| Yellow -> "\027[33m"
| Blue -> "\027[34m"
| Magenta -> "\027[35m"
| Cyan -> "\027[36m"
| White -> "\027[37m"
| Default -> "\027[39m"
let color_reset = "\027[0m"
(* default log levels color mapping *)
let color_of_level = function
| FATAL -> Magenta
| ERROR -> Red
| WARN -> Yellow
| INFO -> Green
| DEBUG -> Cyan
(* defaults *)
let level = ref ERROR
let output = ref stdout
let level_to_color = ref color_of_level
let use_color = ref false
let prefix = ref ""
let set_log_level l =
level := l
let get_log_level () =
!level
let set_output o =
output := o
let set_prefix p =
prefix := p
let clear_prefix () =
prefix := ""
let set_color_mapping f =
level_to_color := f
let color_on () =
use_color := true
let color_off () =
use_color := false
let level_to_string lvl =
let s = string_of_level lvl in
if !use_color then
let color = !level_to_color lvl in
(color_to_string color) ^ s ^ (color_reset)
else
s
let section_width = ref 0
module type S = sig
val log : log_level -> ('a, out_channel, unit) format -> 'a
val fatal : ('a, out_channel, unit) format -> 'a
val error : ('a, out_channel, unit) format -> 'a
val warn : ('a, out_channel, unit) format -> 'a
val info : ('a, out_channel, unit) format -> 'a
val debug : ('a, out_channel, unit) format -> 'a
end
module type SECTION = sig
val section: string
end
module Make (S: SECTION) = struct
let () =
if S.section <> "" then
section_width := max (String.length S.section) !section_width
let timestamp_str lvl =
let section =
if !section_width = 0 then ""
else sprintf "%-*s " !section_width S.section
in
let ts = Unix.gettimeofday() in
let tm = Unix.localtime ts in
let us, _s = modf ts in
(* example: "2012-01-13 18:26:52.091" *)
sprintf "%04d-%02d-%02d %02d:%02d:%02d.%03d %s%s%s: "
(1900 + tm.Unix.tm_year)
(1 + tm.Unix.tm_mon)
tm.Unix.tm_mday
tm.Unix.tm_hour
tm.Unix.tm_min
tm.Unix.tm_sec
(int_of_float (1_000. *. us))
section
(level_to_string lvl)
!prefix
(* example for a shorter timestamp string *)
let short_timestamp_str lvl =
sprintf "%.3f %s: " (Unix.gettimeofday()) (string_of_level lvl)
let log lvl fmt =
if int_of_level lvl >= int_of_level !level then
let now = timestamp_str lvl in
fprintf !output ("%s" ^^ fmt ^^ "\n%!") now
else
ifprintf !output fmt
let fatal fmt = log FATAL fmt
let error fmt = log ERROR fmt
let warn fmt = log WARN fmt
let info fmt = log INFO fmt
let debug fmt = log DEBUG fmt
end
include Make (struct
let section = ""
end)
(* Copyright (c) 2014, INRIA.
* Copyright (c) 2013, Zhang Initiative Research Unit,
* Advance Science Institute, RIKEN
* 2-1 Hirosawa, Wako, Saitama 351-0198, Japan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *)
(** {2 Logger} *)
(** {4 Log levels} *)
type log_level = FATAL | ERROR | WARN | INFO | DEBUG
val string_of_level : log_level -> string
val level_of_string : string -> log_level
(** {4 Setup} *)
val set_log_level : log_level -> unit
val get_log_level : unit -> log_level
val set_output : out_channel -> unit
val set_prefix : string -> unit
val clear_prefix : unit -> unit
(** {4 Printf-like logging primitives} *)
module type S = sig
val log: log_level -> ('a, out_channel, unit, unit) format4 -> 'a
val fatal : ('a, out_channel, unit) format -> 'a
val error : ('a, out_channel, unit) format -> 'a
val warn : ('a, out_channel, unit) format -> 'a
val info : ('a, out_channel, unit) format -> 'a
val debug : ('a, out_channel, unit) format -> 'a
end
include S
(** {4 Coloring of log levels (optional)} *)
type color = Black | Red | Green | Yellow | Blue | Magenta | Cyan | White
| Default
val color_on : unit -> unit
val color_off : unit -> unit
val set_color_mapping : (log_level -> color) -> unit
(** {4 Functor interface (optional)} *)
module type SECTION = sig
(** Signature for the functor parameters. *)
val section: string
(** Section name. *)
end
module Make (Section: SECTION): S
(**
This module aims to be used on the first line of each module:
module Log = Log.Make(struct let section = "module-name" end)
*)
open Core_kernel.Std
open Bap.Std
type symbol =
{
address : tid option;
name : string;
}
let find_symbol program name =
Term.enum sub_t program |>
Seq.find_map ~f:(fun s -> Option.some_if (Sub.name s = name) (Term.tid s))
let build_symbols symbol_names prog =
List.map symbol_names ~f:(fun symbol -> let symbol_address = find_symbol prog symbol in
{address = symbol_address; name = symbol;})
|> List.filter ~f:(fun symbol -> match symbol.address with
| Some _ -> true
| _ -> false)
let get_symbol tid symbols =
List.find symbols ~f:(
fun symbol -> match symbol.address with
| Some address -> tid = address
| None -> false)
let get_symbol_name_from_jmp jmp symbols =
match Jmp.kind jmp with
| Goto _ | Ret _ | Int (_,_) -> assert(false)
| Call destination -> begin
match Call.target destination with
| Direct addr ->
begin
let symbol = List.find symbols ~f:(fun symbol -> match symbol.address with
| Some address -> addr = address
| _ -> assert(false)) in match symbol with
| Some s -> s.name
| _ -> assert(false)
end
| _ -> assert(false)
end
let get_direct_callsites_of_sub 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 -> Some j
| _ -> None
end))
let sub_calls_symbol prog sub symbol_name =
let symbol_struct = find_symbol prog symbol_name in
match symbol_struct with
| Some s -> begin
let callsites = get_direct_callsites_of_sub sub in
Seq.exists callsites ~f:(fun callsite -> match Jmp.kind callsite with
| Goto _ | Ret _ | Int (_,_) -> false
| Call destination -> match Call.target destination with
| Direct addr -> addr = s
| _ -> false)
end
| _ -> false
type concrete_call =
{
call_site : tid;
symbol_address : tid;
name : string;
}
let call_finder = object
inherit [(tid * tid) list] Term.visitor
method! enter_jmp jmp tid_list = match Jmp.kind jmp with
| Goto _ | Ret _ | Int (_,_) -> tid_list
| Call destination -> begin
match Call.target destination with
| Direct addr -> (Term.tid jmp, addr) :: tid_list
| _ -> tid_list
end
end
let transform_call_to_concrete_call (src_tid, dst_tid) symbols =
match (get_symbol dst_tid symbols) with
| Some symbol -> {call_site = src_tid; symbol_address = dst_tid; name = symbol.name}
| None -> assert(false)
let filter_calls_to_symbols calls symbols =
List.filter calls ~f:(
fun (_, dst) -> List.exists symbols ~f:(
fun symbol -> match symbol.address with
| Some address -> address = dst
| None -> false))
|> List.map ~f:(fun call -> transform_call_to_concrete_call call symbols)
let is_interesting_callsite jmp relevant_calls =
match Jmp.kind jmp with
| Goto _ | Ret _ | Int (_,_) -> false
| Call dst -> match Call.target dst with
| Direct tid -> List.exists relevant_calls ~f:(fun c -> c.symbol_address = tid)
| _ -> false
let check_calls relevant_calls prog proj tid_map symbols check_func =
Seq.iter (Term.enum sub_t prog)
~f:(fun sub ->
begin
Seq.iter (Term.enum blk_t sub)
~f:(fun blk -> Seq.iter (Term.enum jmp_t blk)
~f:(fun jmp -> if is_interesting_callsite jmp relevant_calls then
check_func proj prog sub blk jmp tid_map symbols))
end)
let get_symbol_call_count_of_sub symbol_name sub prog =
match find_symbol prog symbol_name with
| Some s -> begin
Seq.to_list (get_direct_callsites_of_sub sub)
|> List.filter ~f:(fun callsite ->
match Jmp.kind callsite with
| Goto _ | Ret _ | Int (_,_) -> false
| Call destination -> match Call.target destination with
| Direct addr -> addr = s
| _ -> false)
|> List.length
end
| _ -> 0
(** This module implements functionality to work with symbols (e.g. malloc).*)
type concrete_call = {
call_site : Bap.Std.tid;
symbol_address : Bap.Std.tid;
name : string;
}
(** This type represents a symbol like malloc or memcpy. *)
type symbol = { address : Bap.Std.tid option; name : string; }
(** Finds a symbol string in a program and returns its IR address (tid). *)
val find_symbol : Bap.Std.program Bap.Std.term -> string -> Bap.Std.tid option
(** builds a list of symbols from a list of strings for a given program
TODO: maybe another data structure like a hashmap would be better. *)
val build_symbols : string list -> Bap.Std.program Bap.Std.term -> symbol list
(** Gets a symbol from a symbol list *)
val get_symbol : Bap.Std.tid -> symbol list -> symbol option
(** Given a JMP and symbol list, it returns its name as string.
Use only if you are sure that the JMP calls a symbol. *)
val get_symbol_name_from_jmp : Bap.Std.Jmp.t -> symbol list -> string
(** Checks if a subfunction calls a symbol. *)
val sub_calls_symbol: Bap.Std.program Bap.Std.term -> Bap.Std.sub Bap.Std.term -> string -> bool
(** This function finds all (direct) calls in a program. It returns a list of tuples of (callsite, address).*)
val call_finder : (Bap.Std.tid * Bap.Std.tid) list Bap.Std.Term.visitor
(** Transform a call (e.g. found with call_finder) to concrete_call with the symbol resolved.*)
val transform_call_to_concrete_call :
Bap.Std.tid * Bap.Std.tid -> symbol list -> concrete_call
(** Filters out all calls (callsite, target) that do not call to a known symbol.*)
val filter_calls_to_symbols :
(Bap.Std.tid * Bap.Std.tid) list ->
symbol list -> concrete_call list
(** Checks if a callsite is in a list of (interesting) concrete_calls *)
val is_interesting_callsite : Bap.Std.Jmp.t -> concrete_call list -> bool
(** TODO *)
val check_calls :
concrete_call list ->
Bap.Std.program Bap.Std.term ->
'a ->
'b ->
'c ->
('a ->
Bap.Std.program Bap.Std.term ->
Bap.Std.sub Bap.Std.term ->
Bap.Std.blk Bap.Std.term -> Bap.Std.jmp Bap.Std.term -> 'b -> 'c -> unit) ->
unit
(** Returns a sequence of all (direct) callsites in a subfunction *)
val get_direct_callsites_of_sub :
Bap.Std.sub Bap.Std.term -> Bap.Std.jmp Bap.Std.term Core_kernel.Sequence.t
(** Returns call count of symbol in function *)
val get_symbol_call_count_of_sub : string -> Bap.Std.Sub.t -> Bap.Std.Program.t -> int
#include <stdio.h>
#include <stdlib.h>
void heap_based_array(){
char* a = malloc(20);
for(int i=0; i<20;i++){
*(a + i) = 'A';
}
free(a);
}
void stack_based_array(){
char a[20];
for(int i=0; i<20;i++){
a[i] = 'A';
}
}
int main(int argc, char *argv[argc])
{
stack_based_array();
heap_based_array();
return 0;
}
#include <stdlib.h>
void if_statement(){
void* bla = malloc(89);
int a = 2;
if (a < 4){
a = 33;
}
free(bla);
}
void for_loop(){
void* bla = malloc(89);
int a = 34;
for(int i = 0;i < 100; i++){
a += i;
}
free(bla);
}
void nested_for_loop(){
void* bla = malloc(89);
int a = 34;
for(int i = 0; i < 100; i++){
for(int j = 0; j < 200; j++){
a += i + j;
}
}
free(bla);
}
void main(){
if_statement();
for_loop();
nested_for_loop();
}
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#define png_t 4242
// example taken from the book
// "The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities"
// slightly edited
char* make_table(unsigned int width, unsigned int height, char* init_row){
unsigned int n;
int i;
char* buf;
n = width * height;
buf = (char*) malloc(n);
if (!buf)
return NULL;
for(i=0; i < height; i++){
memcpy(&buf[i* width], init_row, width);
}
return buf;
}
void tassa1(int arg1, int arg2){
char init_row[] = "init";
char *res = make_table(arg1, arg2, &init_row);
printf("Table at %p\n", res);
free(res);
}
int malloc_overflow_get_num_elems(){
srand(42);
return rand() * 1000000;
}
void malloc_overflow(){
int num_elems = malloc_overflow_get_num_elems();
void* ptr_elems = malloc(sizeof(png_t) * num_elems); // overflow occurs here
printf("PNG at %p\n", ptr_elems);
free(ptr_elems);
}
int packet_get_int(){
return malloc_overflow_get_num_elems();
}
char* packet_get_string(){
return NULL;
}
// taken from https://cwe.mitre.org/data/definitions/190.html
// slightly edited to make it compile
void overflow_ssh3_1(){
char** response;
int nresp = packet_get_int();
if (nresp > 0) {
response = malloc(nresp*sizeof(char*));
for (int i = 0; i < nresp; i++)
response[i] = packet_get_string();
free(response);
}
}
int main(int argc, char *argv[argc])
{
tassa1(atoi(argv[1]), atoi(argv[2]));
malloc_overflow();
overflow_ssh3_1();
return 0;
}
#include <stdio.h>
#include <stdbool.h>
void integer_underflow_subtraction(){
int i;
i = -2147483648;
i = i - 1;
printf("[integer_overflow_subtraction] %d\n", i);
}
main (void)
{
integer_underflow_subtraction();
}
#include <stdio.h>
#include <unistd.h>
void chroot_fail(){
chdir("/tmp");
if (chroot("/tmp") != 0) {
perror("chroot /tmp");
}
}
int main(void) {
chroot_fail();
}
#include <stdio.h>
#include <unistd.h>
// this one is safe according to http://www.unixwiz.net/techtips/chroot-practices.html
void chroot_safe1(){
chdir("/tmp");
if (chroot("/tmp") != 0) {
perror("chroot /tmp");
}
setuid(1077);
}
void chroot_safe2(){
chdir("/tmp");
if (chroot("/tmp") != 0) {
perror("chroot /tmp");
}
setresuid(1077);
}
void chroot_safe3(){
chdir("/tmp");
if (chroot("/tmp") != 0) {
perror("chroot /tmp");
}
setreuid(1077, 44);
}
void chroot_safe4(){
chdir("/tmp");
if (chroot("/tmp") != 0) {
perror("chroot /tmp");
}
seteuid(1077);
}
int main(void) {
chroot_safe1();
chroot_safe2();
chroot_safe3();
chroot_safe4();
}
#include <stdlib.h>
int main ()
{
int random_number = rand();
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
void main(){
if (access("file", W_OK) != 0) {
exit(1);
}
char* buffer = malloc(6);
if(buffer == NULL){
exit(1);
}
memset(buffer, 1, 6);
int fd = open("file", O_WRONLY);
write(fd, buffer, sizeof(buffer));
close(fd);
free(buffer);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE1 512
int main(int argc, char **argv) {
char *buf1R1;
char *buf2R1;
buf1R1 = (char *) malloc(BUFSIZE1);
buf2R1 = (char *) malloc(BUFSIZE1);
free(buf1R1);
free(buf2R1);
free(buf1R1);
}
// taken from https://exploit-exercises.com/nebula/level01/
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
void vulnerable_sub(){
gid_t gid;
uid_t uid;
gid = getegid();
uid = geteuid();
setresgid(gid, gid, gid);
setresuid(uid, uid, uid);
system("/usr/bin/env echo and now what?");
}
int main(int argc, char **argv, char **envp)
{
vulnerable_sub();
}
#include <stdlib.h>
#include <stdio.h>
void uninitalized_variable(){
int a; // never initialized
int b = 7;
int c = a + b;
printf("a is %d, b is %d, c is %d\n", a , b, c);
}
int main(int argc, char *argv[argc])
{
uninitalized_variable();
return 0;
}
/* taken from https://cwe.mitre.org/data/definitions/467.html and slightly modified */
/* Ignore CWE-259 (hard-coded password) and CWE-309 (use of password system for authentication) for this example. */
#include <stdlib.h>
#include <stdio.h>
#define AUTH_SUCCESS 1
#define AUTH_FAIL 0
char *username = "admin";
char *pass = "password";
int AuthenticateUser(char *inUser, char *inPass) {
printf("Sizeof username = %d\n", sizeof(username));
printf("Sizeof pass = %d\n", sizeof(pass));
if (strncmp(username, inUser, sizeof(username))) {
printf("Auth failure of username using sizeof\n");
return AUTH_FAIL;
}
/* Because of CWE-467, the sizeof returns 4 on many platforms and architectures. */
if (! strncmp(pass, inPass, sizeof(pass))) {
printf("Auth success of password using sizeof\n");
return AUTH_SUCCESS;
}
else {
printf("Auth fail of password using sizeof\n");
return AUTH_FAIL;
}
}
int main (int argc, char **argv) {
int authResult;
if (argc < 3) {
printf("Usage: Provide a username and password\n");
exit(1);
}
authResult = AuthenticateUser(argv[1], argv[2]);
if (authResult != AUTH_SUCCESS) {
printf("Authentication failed\n");
exit(1);
}
else {
printf("Authenticated\n");
exit(0);
}
}
#include <stdlib.h>
void func1(){
void* data = malloc(20);
if (data == NULL){
exit(42);
}
free(data);
}
void func2(){
void* data = malloc(20);
free(data);
}
void main() {
func1();
func2();
}
#include <stdio.h>
#include <stdlib.h>
#define FAILED 0
#define PASSED 1
int main(int argc, char *argv[argc])
{
srand(42);
int result = rand() % 2;
switch (result) {
case FAILED:
printf("Security check failed!\n");
exit(-1);
//Break never reached because of exit()
break;
case PASSED:
printf("Security check passed.\n");
break;
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[argc])
{
int fd = open("/dev/my_driver", O_WRONLY, O_NONBLOCK);
if (fd == -1){
printf("Could not open my_driver.\n");
exit(1);
}
if (ioctl(fd, 0x42) == -1){
printf("ioctl failed.\n");
}
if (close(fd) == -1){
printf("Could not properly close my_driver.\n");
exit(1);
}
return 0;
}
#!/bin/bash
echo "Installing cross compiler for ARM architecture."
sudo apt install -y gcc-arm-linux-gnueabi
echo "Installing cross compiler for MIPS architecture."
sudo apt install -y gcc-mips-linux-gnu
echo "Installing cross compiler for PPC architecture."
sudo apt install -y gcc-powerpc-linux-gnu
echo "Done."
CC_x64=gcc
CC_X86=gcc
CC_ARM=arm-linux-gnueabi-gcc-7
CC_MIPS=mips-linux-gnu-gcc-7
CC_PPC=powerpc-linux-gnu-gcc-7
CFLAGS_X64=-O0 -g -fno-stack-protector
CFLAGS_X86=-O0 -g -m32 -fno-stack-protector
CFLAGS_ARM=-O0 -g -fno-stack-protector
CFLAGS_MIPS=-O0 -g -fno-stack-protector
CFLAGS_PPC=-O0 -g -fno-stack-protector
define compile_x64
@echo "Compiling x64 target:" $(1)
$(CC_x64) $(CFLAGS_X64) -o build/$(1)_x64.out $(1).c
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_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_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_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_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))
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,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_x64,cwe_782)
$(call compile_all,arrays)
$(call compile_all,memory_access)
clean:
rm -rf build
#include <stdlib.h>
#include <stdio.h>
void uninitalized_variable(){
int a; // never initialized
int b = 7;
int c = a + b;
printf("a is %d, b is %d, c is %d\n", a , b, c);
}
int main(int argc, char *argv[argc])
{
uninitalized_variable();
return 0;
}
#!/bin/bash
function printf_new() {
v=$(printf "%-80s" "-")
echo "${v// /-}"
}
function run_arch() {
echo
echo
echo "Running architecture:" $1
printf_new
echo "cwe_190_$1"
printf_new
bap artificial_samples/build/cwe_190_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
echo "cwe_243_$1"
printf_new
bap artificial_samples/build/cwe_243_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
echo "cwe_243_$1 (clean)"
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_323_$1"
printf_new
bap artificial_samples/build/cwe_332_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
echo "cwe_367_$1"
printf_new
bap artificial_samples/build/cwe_367_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
echo "cwe_415_$1"
printf_new
bap artificial_samples/build/cwe_415_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
echo "cwe_426_$1"
printf_new
bap artificial_samples/build/cwe_426_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
echo "cwe_457_$1"
printf_new
bap artificial_samples/build/cwe_457_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
echo "cwe_467_$1"
printf_new
bap artificial_samples/build/cwe_467_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
echo "cwe_476_$1"
printf_new
bap artificial_samples/build/cwe_476_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
if [ $1 == "x64" ]; then
echo "cwe_782_$1"
printf_new
bap artificial_samples/build/cwe_782_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
fi
echo "c_constructs_$1"
printf_new
bap artificial_samples/build/c_constructs_$1.out --pass=callsites,cwe-checker --cwe-checker-config=../src/config.json
printf_new
}
function run_all_arch() {
run_arch x86
run_arch x64
run_arch arm
run_arch mips
run_arch ppc
}
function main() {
if [ -z "$1" ]; then
run_all_arch
else
run_arch $1
fi
}
main "$@"
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