Skip to content

Commit

Permalink
security check on binary updates and refactors
Browse files Browse the repository at this point in the history
  • Loading branch information
marirs committed Mar 26, 2024
1 parent 2de703f commit 060ece8
Show file tree
Hide file tree
Showing 13 changed files with 312 additions and 303 deletions.
7 changes: 2 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[package]
name = "capa"
version = "0.3.14"
version = "0.3.15"
description = "File capability extractor."
authors = ["Marirs <[email protected]>", "Andrey Mnatsakanov <[email protected]>", "Jorge Alejandro Durán Royo<[email protected]>"]
keywords = ["capa", "fce", "capability", "file"]
keywords = ["capa", "fce", "capability", "aslr", "reverse"]
readme = "README.md"
license-file = "LICENSE"
repository = "https://github.com/marirs/capa-rs"
Expand All @@ -29,14 +29,11 @@ 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
83 changes: 49 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The example contains a `CLI` to output the extracted capabilities to `stdout`.
- Rust 1.56+ (edition 2021)

### Running the example cli
```bash
```
./capa_cli --rules-path capa-rules data/Demo64.dll
+--------------+-------------+
| File Properties |
Expand All @@ -33,52 +33,67 @@ The example contains a `CLI` to output the extracted capabilities to `stdout`.
| os | WINDOWS |
+--------------+-------------+
+---------------+------------------------+
| ATT&CK Tactic | ATT&CK Technique |
+===============+========================+
| Execution | Shared Modules [T1129] |
+---------------+------------------------+

+---------------+---------------------------+
| MBC Objective | MBC Behavior |
+===============+===========================+
| Process | Terminate Process [C0018] |
+---------------+---------------------------+

+-----------------------+-------------+
| Binary Security Checks |
| Security Checks |
+=======================+=============+
| ASLR | supported |
| ASLR | Supported |
+-----------------------+-------------+
| CHECKSUM | failed |
| CHECKSUM | Fail |
+-----------------------+-------------+
| CONSIDER-MANIFEST | passed |
| CONSIDER-MANIFEST | Pass |
+-----------------------+-------------+
| CONTROL-FLOW-GUARD | unsupported |
| CONTROL-FLOW-GUARD | Unsupported |
+-----------------------+-------------+
| DATA-EXEC-PREVENT | passed |
| DATA-EXEC-PREVENT | Pass |
+-----------------------+-------------+
| HANDLES-ADDR-GT-2GB | passed |
| HANDLES-ADDR-GT-2GB | Pass |
+-----------------------+-------------+
| RUNS-IN-APP-CONTAINER | failed |
| RUNS-IN-APP-CONTAINER | Fail |
+-----------------------+-------------+
| SAFE-SEH | failed |
| SAFE-SEH | Pass |
+-----------------------+-------------+
| VERIFY-DIGITAL-CERT | failed |
| VERIFY-DIGITAL-CERT | Fail |
+-----------------------+-------------+
+---------------+------------------------+
| ATT&CK Tactic | ATT&CK Technique |
+===============+========================+
| Execution | Shared Modules [T1129] |
+---------------+------------------------+
+-----------------------------------------------+------------------------------------+
| Capability | Namespace |
+===============================================+====================================+
| contain a resource (.rsrc) section | executable/pe/section/rsrc |
+-----------------------------------------------+------------------------------------+
| contain a thread local storage (.tls) section | executable/pe/section/tls |
+-----------------------------------------------+------------------------------------+
| parse PE header | load-code/pe |
+-----------------------------------------------+------------------------------------+
| terminate process via fastfail | host-interaction/process/terminate |
+-----------------------------------------------+------------------------------------+
+--------------------------+------------------------------------------------------+
| MBC Objective | MBC Behavior |
+==========================+======================================================+
| Anti-Behavioral Analysis | Debugger Detection::Software Breakpoints [B0001.025] |
+--------------------------+------------------------------------------------------+
| Data | Non-Cryptographic Hash::MurmurHash [C0030.001] |
| | Non-Cryptographic Hash::djb2 [C0030.006] |
+--------------------------+------------------------------------------------------+
+-----------------------------------------------+-------------------------------------------------+
| Capability | Namespace |
+===============================================+=================================================+
| check for software breakpoints | anti-analysis/anti-debugging/debugger-detection |
+-----------------------------------------------+-------------------------------------------------+
| contain a thread local storage (.tls) section | executable/pe/section/tls |
+-----------------------------------------------+-------------------------------------------------+
| contains PDB path | executable/pe/pdb |
+-----------------------------------------------+-------------------------------------------------+
| hash data using djb2 | data-manipulation/hashing/djb2 |
+-----------------------------------------------+-------------------------------------------------+
| hash data using murmur3 | data-manipulation/hashing/murmur |
+-----------------------------------------------+-------------------------------------------------+
| match known PlugX module | malware-family/plugx |
+-----------------------------------------------+-------------------------------------------------+
| parse PE header | load-code/pe |
+-----------------------------------------------+-------------------------------------------------+
| reference Cloudflare DNS server | communication/dns |
+-----------------------------------------------+-------------------------------------------------+
TAGS: [B0001.025, C0030.001, C0030.006, T1129]
Time taken (seconds): 1.227743833s
```

- With verbose output use
Expand Down
50 changes: 33 additions & 17 deletions examples/capa_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ 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 prettytable::{color, format::Alignment, Attr, Cell, Row, Table};
use serde_json::{to_value, Map, Value};

use capa::{BinarySecurityCheckOptions, FileCapabilities};

Expand Down Expand Up @@ -53,7 +53,7 @@ struct CliOpts {

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

fn main() {
Expand All @@ -69,9 +69,16 @@ fn main() {
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, Some(security_check_opts)) {
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 All @@ -87,6 +94,13 @@ 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 Mitre ATT&CK information
if let Some(attacks) = data.get("attacks") {
let attacks = attacks.as_object().unwrap();
Expand All @@ -106,14 +120,6 @@ 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 @@ -260,9 +266,11 @@ fn get_mbc(mbc: &Map<String, Value>) -> Table {
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),
]));
tbl.set_titles(Row::new(vec![Cell::new_align(
"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();
Expand All @@ -273,7 +281,15 @@ fn get_security_checks(security_checks: &Value) -> Table {
Cell::new(check_name)
.with_style(Attr::ForegroundColor(color::YELLOW))
.with_style(Attr::Bold),
Cell::new(&status),
if status.eq_ignore_ascii_case("fail") || status.eq_ignore_ascii_case("unsupported") {
Cell::new(&status).with_style(Attr::ForegroundColor(color::RED))
} else if status.eq_ignore_ascii_case("Pass")
|| status.eq_ignore_ascii_case("Supported")
{
Cell::new(&status).with_style(Attr::ForegroundColor(color::GREEN))
} else {
Cell::new(&status)
},
]));
}

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};
use std::collections::HashMap;

#[derive(Debug, Clone)]
struct InstructionS {
Expand Down
45 changes: 23 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,33 @@

extern crate core;

use crate::security::options::status::SecurityCheckStatus;
use consts::{FileFormat, Os};
use core::fmt;
use std::{
collections::{BTreeMap, BTreeSet, HashMap},
thread::spawn,
};
use std::collections::HashSet;
use std::path::PathBuf;

use sede::{from_hex, to_hex};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use smda::FileArchitecture;
use std::{
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
path::PathBuf,
thread::spawn,
};
use yaml_rust::Yaml;

use consts::{FileFormat, Os};
use sede::{from_hex, to_hex};

pub use crate::error::Error;
use crate::security::options::status::SecurityCheckStatus;

pub(crate) mod consts;
mod error;
mod extractor;
pub mod rules;
mod sede;
mod error;
mod security;
mod sede;

pub type Result<T> = std::result::Result<T, Error>;


// If this changes, then update the command line reference.
// Used for options for binary security checks.
#[derive(Debug, Copy, Clone)]
pub enum LibCSpec {
LSB1,
Expand All @@ -49,6 +46,7 @@ pub enum LibCSpec {
LSB5,
}

// Used for options for binary security checks.
impl fmt::Display for LibCSpec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let spec_name = match *self {
Expand Down Expand Up @@ -87,6 +85,7 @@ impl fmt::Display for LibCSpec {
}
}

// Used for options for binary security checks.
impl LibCSpec {
pub(crate) fn get_functions_with_checked_versions(self) -> &'static [&'static str] {
match self {
Expand All @@ -108,6 +107,7 @@ impl LibCSpec {
}
}

// Used for options for binary security checks.
impl From<String> for LibCSpec {
fn from(value: String) -> Self {
match value.as_str() {
Expand Down Expand Up @@ -148,10 +148,12 @@ pub struct BinarySecurityCheckOptions {
}

impl BinarySecurityCheckOptions {
pub fn new(libc: Option<PathBuf>,
sysroot: Option<PathBuf>,
libc_spec: Option<LibCSpec>,
no_libc: bool) -> Self {
pub fn new(
libc: Option<PathBuf>,
sysroot: Option<PathBuf>,
libc_spec: Option<LibCSpec>,
no_libc: bool,
) -> Self {
//!
//! Create some options to configure binary security checks.
//! - libc: This is the path of the C runtime library file.
Expand All @@ -164,7 +166,6 @@ impl BinarySecurityCheckOptions {
libc_spec,
no_libc,
input_files: Vec::new(),

}
}
}
Expand All @@ -175,7 +176,6 @@ impl Default for BinarySecurityCheckOptions {
}
}


impl FileCapabilities {
pub fn from_file(
file_name: &str,
Expand All @@ -184,7 +184,7 @@ impl FileCapabilities {
resolve_tailcalls: bool,
logger: &dyn Fn(&str),
features_dump: bool,
security_checks_opts: Option<BinarySecurityCheckOptions>
security_checks_opts: Option<BinarySecurityCheckOptions>,
) -> Result<Self> {
//! Loads a binary from a given file for capability analysis using the default binary security check options:
//! ## Example
Expand All @@ -206,7 +206,8 @@ impl FileCapabilities {
// Fetch security checks on a separate thread
let mut security_opts = security_checks_opts.unwrap_or_default();
security_opts.input_files = vec![PathBuf::from(&f)];
let security_checks_thread_handle = spawn(move || security::get_security_checks(&f, &security_opts));
let security_checks_thread_handle =
spawn(move || security::get_security_checks(&f, &security_opts));
let security_checks = security_checks_thread_handle.join().unwrap()?;

let mut file_capabilities;
Expand Down
1 change: 0 additions & 1 deletion src/security/elf/checked_functions.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#[derive(Debug, Eq, PartialEq, Hash)]
pub(crate) struct CheckedFunction {
checked_name: String,
Expand Down
Loading

0 comments on commit 060ece8

Please sign in to comment.