Skip to content

Commit

Permalink
Added the Binary Security checks (#11)
Browse files Browse the repository at this point in the history
* Add binary security checks.

* Some Refactors

* Use the Function pointer syntax to map errors

* Check if we need the libc and handle it gracefully

* Update the `readme.md`.

* Provide security checks options as cmdline arguments

* Remove the `cmdline` module
  • Loading branch information
clementwanjau authored Mar 26, 2024
1 parent 1ecad8e commit 2de703f
Show file tree
Hide file tree
Showing 14 changed files with 2,098 additions and 23 deletions.
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ dnfile = { git = "https://github.com/marirs/dnfile-rs.git", branch = "master" }
lazy_static = "1.4.0"
parking_lot = "0.12.1"
serde_json = "1.0.113"
log = "0.4.21"
memoffset = "0.9.0"
memmap2 = "0.9.4"
termcolor = "1.4.1"
scroll = "0.12.0"
once_cell = "1.19.0"
dynamic-loader-cache = "0.1"
rayon = "1.10.0"

[dev-dependencies]
clap = { version = "4.0.27", features = ["cargo", "derive"] }
Expand Down
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
Test it online: https://www.analyze.rs/

capa detects capabilities in executable files. You run it against a PE, ELF, or shellcode file and it tells you what it thinks the program can do.
For example, it might suggest that the file is a backdoor, is capable of installing services, or relies on HTTP to communicate.
For example, it might suggest that the file is a backdoor, is capable of installing services, or relies on HTTP to communicate. It also performs a binary security check to see if the binary is compiled with security features enabled.

It is a port from https://github.com/mandiant/capa without IDA plugins, etc. Its just a capa library that gives out capability information.
The Library itself can be used in other applications. The rules are available here: `https://github.com/mandiant/capa-rules`
Expand Down Expand Up @@ -45,6 +45,29 @@ The example contains a `CLI` to output the extracted capabilities to `stdout`.
| Process | Terminate Process [C0018] |
+---------------+---------------------------+

+-----------------------+-------------+
| Binary Security Checks |
+=======================+=============+
| ASLR | supported |
+-----------------------+-------------+
| CHECKSUM | failed |
+-----------------------+-------------+
| CONSIDER-MANIFEST | passed |
+-----------------------+-------------+
| CONTROL-FLOW-GUARD | unsupported |
+-----------------------+-------------+
| DATA-EXEC-PREVENT | passed |
+-----------------------+-------------+
| HANDLES-ADDR-GT-2GB | passed |
+-----------------------+-------------+
| RUNS-IN-APP-CONTAINER | failed |
+-----------------------+-------------+
| SAFE-SEH | failed |
+-----------------------+-------------+
| VERIFY-DIGITAL-CERT | failed |
+-----------------------+-------------+


+-----------------------------------------------+------------------------------------+
| Capability | Namespace |
+===============================================+====================================+
Expand Down
65 changes: 60 additions & 5 deletions examples/capa_cli.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use capa::FileCapabilities;
use clap::Parser;
use prettytable::{color, format::Alignment, Attr, Cell, Row, Table};
use serde_json::{to_value, Map, Value};
use std::fs;
use std::time::Instant;

use clap::Parser;
use prettytable::{Attr, Cell, color, format::Alignment, Row, Table};
use serde_json::{Map, to_value, Value};

use capa::{BinarySecurityCheckOptions, FileCapabilities};

#[derive(Parser)]
#[clap(
author,
Expand Down Expand Up @@ -36,6 +38,22 @@ struct CliOpts {
/// filter map_features
#[clap(short = 'f', long, value_name = "FILTER_MAP_FEATURES")]
filter_map_features: Option<String>,

/// Path of the C runtime library file.
#[clap(long, value_name = "LIBC")]
libc: Option<String>,

/// Path of the system root for finding the corresponding C runtime library.
#[clap(long, value_name = "SYSROOT")]
sysroot: Option<String>,

/// Use an internal list of checked functions as specified by a specification. Provide the version of the specification. eg 3.2.0
#[clap(long, value_name = "LIBC_SPEC")]
libc_spec: Option<String>,

/// Assume that input files do not use any C runtime libraries.
#[clap(long, default_value = "false", value_name = "NO_LIBC")]
no_libc: bool
}

fn main() {
Expand All @@ -45,9 +63,15 @@ fn main() {
let verbose = cli.verbose;
let map_features = cli.map_features;
let json_path = cli.output;
let libc = cli.libc.map(|s| s.into());
let sysroot = cli.sysroot.map(|s| s.into());
let libc_spec = cli.libc_spec.map(|s| s.into());
let no_libc = cli.no_libc;
let security_check_opts = BinarySecurityCheckOptions::new(libc, sysroot, libc_spec, no_libc);


let start = Instant::now();
match FileCapabilities::from_file(&filename, &rules_path, true, true, &|_s| {}, map_features) {
match FileCapabilities::from_file(&filename, &rules_path, true, true, &|_s| {}, map_features, Some(security_check_opts)) {
Err(e) => println!("{:?}", e),
Ok(mut s) => {
match to_value(&s) {
Expand Down Expand Up @@ -82,6 +106,14 @@ fn main() {
}
}
println!();

// print the Security Checks
if let Some(security_checks) = data.get("security_checks") {
let tbl = get_security_checks(security_checks);
tbl.printstd();
}

println!();

// print the Capability/Namespace
if let Some(namespace) = data.get("capability_namespaces") {
Expand Down Expand Up @@ -225,6 +257,29 @@ fn get_mbc(mbc: &Map<String, Value>) -> Table {
tbl
}

fn get_security_checks(security_checks: &Value) -> Table {
let security_checks = security_checks.as_array().unwrap();
let mut tbl = Table::new();
tbl.set_titles(Row::new(vec![
Cell::new_align("Binary Security Checks", Alignment::CENTER).with_hspan(2),
]));
for check in security_checks {
let check = check.as_object().unwrap();
let check_name = check.get("name").unwrap().as_str().unwrap();
let v = check.get("status").unwrap();
let status = v.as_str().unwrap().to_string();

tbl.add_row(Row::new(vec![
Cell::new(check_name)
.with_style(Attr::ForegroundColor(color::YELLOW))
.with_style(Attr::Bold),
Cell::new(&status),
]));
}

tbl
}

/// Gets the Capability & Namespace information and returns as a TABLE for stdout
fn get_namespace(namespace: &Map<String, Value>) -> Table {
let mut tbl = Table::new();
Expand Down
35 changes: 35 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::path::PathBuf;

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("{0}")]
Expand All @@ -15,6 +17,39 @@ pub enum Error {
#[error("{0}")]
DnFileError(#[from] dnfile::error::Error),

#[error("binary format of file '{0}' is not recognized")]
UnknownBinaryFormat(PathBuf),

#[error("binary format of '{name}' is not {expected}")]
UnexpectedBinaryFormat {
expected: &'static str,
name: PathBuf,
},

#[error("architecture of '{0}' is unexpected")]
UnexpectedBinaryArchitecture(PathBuf),

#[error("binary format '{format}' of file '{path}' is recognized but unsupported")]
UnsupportedBinaryFormat { format: String, path: PathBuf },

#[error("dependent C runtime library is not recognized. Consider specifying --sysroot, --libc, --libc-spec or --no-libc")]
UnrecognizedNeededLibC,

#[error("dependent C runtime library '{0}' was not found")]
NotFoundNeededLibC(PathBuf),

#[error(transparent)]
FromBytesWithNul(#[from] core::ffi::FromBytesWithNulError),

#[error(transparent)]
FromBytesUntilNul(#[from] core::ffi::FromBytesUntilNulError),

#[error(transparent)]
Scroll(#[from] scroll::Error),

#[error(transparent)]
DynamicLoaderCache(#[from] dynamic_loader_cache::Error),

#[error("{0}")]
IoError(#[from] std::io::Error),
#[error("{0}")]
Expand Down
2 changes: 1 addition & 1 deletion src/extractor/smda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use smda::{
report::DisassemblyReport,
Disassembler,
};
use std::{collections::HashMap, convert::TryInto};
use std::{collections::HashMap};

#[derive(Debug, Clone)]
struct InstructionS {
Expand Down
Loading

0 comments on commit 2de703f

Please sign in to comment.