Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New capa (identify capabilities in executable files) module with ATT&CK support (S18) #1212

Merged
merged 9 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ jobs:
source "./external/emba_venv/bin/activate"
echo "GH_action:true" > ./config/gh_action
sudo docker-compose build --no-cache --pull
NO_UPDATE_CHECK=1 sudo -E sudo ./emba -d 2 -y
NO_UPDATE_CHECK=1 sudo -E ./emba -d 2 -y
7 changes: 4 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "3"
services:
# nosemgrep
emba:
image: embeddedanalyzer/emba:1.4.1d
image: embeddedanalyzer/emba:1.4.1e
container_name: emba
read_only: true
# all pre-checker mount modules need privileged mode
Expand All @@ -12,7 +12,8 @@ services:
# /root/.config is needed for cwe_checker
# /root/.local is needed for cwe_checker
tmpfs:
- /tmp
# exec on /tmp is needed for capa -> Todo: find better solution
- /tmp:exec
- /root/.config/
- /root/.local/share/composer/
- /root/.local/share/cwe_checker/
Expand Down Expand Up @@ -50,7 +51,7 @@ services:
soft: 0

emba_quest:
image: embeddedanalyzer/emba:1.4.1d
image: embeddedanalyzer/emba:1.4.1e
container_name: emba_quest
read_only: true
tmpfs:
Expand Down
1 change: 1 addition & 0 deletions helpers/helpers_emba_dependency_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,7 @@ dependency_check()
# radare2
check_dep_tool "radare2" "r2"
fi
check_dep_file "Identify capabilities in executable files" "${EXT_DIR}/capa"

# bandit python security tester
check_dep_tool "bandit - python vulnerability scanner" "bandit"
Expand Down
2 changes: 1 addition & 1 deletion helpers/helpers_emba_status_bar.sh
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ update_box_status_2() {
fi

while [[ "${BOX_SIZE}" -gt 0 ]]; do
[[ -f "${TMP_DIR}""/LINES.log" ]] && lLINES=$(cat "${TMP_DIR}""/LINES.log")
[[ -f "${TMP_DIR}""/LINES.log" ]] && lLINES=$(cat "${TMP_DIR}""/LINES.log" || true)

PHASE_STR=$(grep 'phase started' "${LOG_DIR}/emba.log" 2> /dev/null | tail -1 | cut -d"-" -f2 | awk '{print $1}' || true)
[[ "${PHASE_STR}" == "Pre" ]] && PHASE_STR="Extraction"
Expand Down
7 changes: 7 additions & 0 deletions installer/I13_disasm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ I13_disasm() {

if [[ "${LIST_DEP}" -eq 1 ]] || [[ "${IN_DOCKER}" -eq 1 ]] || [[ "${DOCKER_SETUP}" -eq 0 ]] ; then
print_file_info "${BINUTIL_VERSION_NAME}" "The GNU Binutils are a collection of binary tools." "https://ftp.gnu.org/gnu/binutils/${BINUTIL_VERSION_NAME}.tar.gz" "external/${BINUTIL_VERSION_NAME}.tar.gz" "external/objdump"
print_file_info "Capa" "Capa - Open-source tool to identify capabilities in executable files." "https://github.com/mandiant/capa/releases/download/v7.1.0/capa-v7.1.0-linux.zip" "external/capa-v7.1.0-linux.zip"
print_tool_info "texinfo" 1
print_tool_info "git" 1
print_tool_info "wget" 1
Expand Down Expand Up @@ -58,6 +59,12 @@ I13_disasm() {
# apt-get install "${INSTALL_APP_LIST[@]}" -y --no-install-recommends
apt-get install "${INSTALL_APP_LIST[@]}" -y

if ! [[ -f "external/capa" ]]; then
download_file "Capa" "https://github.com/mandiant/capa/releases/download/v7.1.0/capa-v7.1.0-linux.zip" "external/capa-v7.1.0-linux.zip"
unzip external/capa-v7.1.0-linux.zip -d external
rm external/capa-v7.1.0-linux.zip
fi

if ! [[ -f "external/objdump" ]] ; then
download_file "${BINUTIL_VERSION_NAME}" "https://ftp.gnu.org/gnu/binutils/${BINUTIL_VERSION_NAME}.tar.gz" "external/${BINUTIL_VERSION_NAME}.tar.gz"
if [[ -f "external/${BINUTIL_VERSION_NAME}.tar.gz" ]] ; then
Expand Down
10 changes: 4 additions & 6 deletions modules/L10_system_emulation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,9 @@ L10_system_emulation() {
if [[ -n "${D_END}" ]]; then
export TAPDEV_0="tap0_0"
local lARCH_END=""
export D_END_lower=""

D_END_lower="$(echo "${D_END}" | tr '[:upper:]' '[:lower:]')"
lARCH_END="$(echo "${ARCH}" | tr '[:upper:]' '[:lower:]')"
lARCH_END+="${D_END_lower}"
lARCH_END="${ARCH,,}"
lARCH_END+="${D_END,,}"

# default is ARM_SF -> we only need to check if it is HF
# The information is based on the results of architecture_check()
Expand Down Expand Up @@ -1520,9 +1518,9 @@ get_networking_details_emulation() {
lIP="${lIP/\.}"

IP_ADDRESS_=""
if [[ "${D_END_lower}" == "eb" ]]; then
if [[ "${D_END,,}" == "eb" ]]; then
IP_ADDRESS_="${lIP}"
elif [[ "${D_END_lower}" == "el" ]]; then
elif [[ "${D_END,,}" == "el" ]]; then
IP_ADDRESS_=$(echo "${lIP}" | tr '.' '\n' | tac | tr '\n' '.' | sed 's/\.$//')
fi

Expand Down
134 changes: 134 additions & 0 deletions modules/S18_capa_checker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/bin/bash -p

# EMBA - EMBEDDED LINUX ANALYZER
#
# Copyright 2024-2024 Siemens Energy AG
#
# EMBA comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
# welcome to redistribute it under the terms of the GNU General Public License.
# See LICENSE file for usage of this software.
#
# EMBA is licensed under GPLv3
# SPDX-License-Identifier: GPL-3.0-only
#
# Author(s): Michael Messner

# Description: This module uses capa (https://github.com/mandiant/capa) for detecting binary behavior
# Currently capa only supports x86 architecture

S18_capa_checker() {
module_log_init "${FUNCNAME[0]}"
module_title "Analyse binary behavior with capa"
pre_module_reporter "${FUNCNAME[0]}"

if [[ ! -e "${EXT_DIR}"/capa ]]; then
print_output "[-] Missing capa installation ... exit module"
module_end_log "${FUNCNAME[0]}" 0
return
fi
if [[ ${BINARY_EXTENDED} -ne 1 ]] ; then
print_output "[-] ${FUNCNAME[0]} - BINARY_EXTENDED not set to 1. You can set it up via a scan-profile."
module_end_log "${FUNCNAME[0]}" 0
return
fi
if [[ "${FULL_TEST}" -ne 1 ]]; then
# we only need to wait if we are not using the full_scan profile
module_wait "S13_weak_func_check"
fi
if [[ -s "${CSV_DIR}"/s13_weak_func_check.csv ]]; then
local BINARIES=()
# usually binaries with strcpy or system calls are more interesting for further analysis
# to keep analysis time low we only check these bins
mapfile -t BINARIES < <(grep "strcpy\|system" "${CSV_DIR}"/s13_weak_func_check.csv | sort -k 3 -t ';' -n -r | awk '{print $1}' || true)
fi

local lBINARY=""
local lBIN_TO_CHECK=""
local lWAIT_PIDS_S18=()
local lBIN_TO_CHECK_ARR=()
local lBINS_CHECKED_CNT=0

for lBINARY in "${BINARIES[@]}"; do
mapfile -t lBIN_TO_CHECK_ARR < <(find "${LOG_DIR}/firmware" -name "$(basename "${lBINARY}")" | sort -u || true)
for lBIN_TO_CHECK in "${lBIN_TO_CHECK_ARR[@]}"; do
if [[ -f "${BASE_LINUX_FILES}" && "${FULL_TEST}" -eq 0 ]]; then
# if we have the base linux config file we only test non known Linux binaries
# with this we do not waste too much time on open source Linux stuff
lNAME=$(basename "${lBIN_TO_CHECK}" 2> /dev/null)
if grep -E -q "^${lNAME}$" "${BASE_LINUX_FILES}" 2>/dev/null; then
continue 2
fi
fi

if ( file "${lBIN_TO_CHECK}" | grep -q "ELF.*Intel" ); then
# ensure we have not tested this binary
local lBIN_MD5=""
lBIN_MD5="$(md5sum "${lBIN_TO_CHECK}" | awk '{print $1}')"
if ( grep -q "${lBIN_MD5}" "${TMP_DIR}"/s18_checked.tmp 2>/dev/null); then
# print_output "[*] ${ORANGE}${lBIN_TO_CHECK}${NC} already tested with capa" "no_log"
continue
fi

if [[ "${THREADED}" -eq 1 ]]; then
capa_runner_fct "${lBIN_TO_CHECK}" &
local lTMP_PID="$!"
store_kill_pids "${lTMP_PID}"
lWAIT_PIDS_S18+=( "${lTMP_PID}" )
max_pids_protection "${MAX_MOD_THREADS}" "${lWAIT_PIDS_S18[@]}"
else
capa_runner_fct "${lBIN_TO_CHECK}"
fi

# in normal operation we stop checking after the first 20 binaries
# if FULL_TEST is activated we are testing all binaries -> this takes a long time
lBINS_CHECKED_CNT=$(wc -l "${TMP_DIR}"/s18_checked.tmp 2>/dev/null || true)
if [[ "${lBINS_CHECKED_CNT/\ *}" -gt 20 ]] && [[ "${FULL_TEST}" -ne 1 ]]; then
print_output "[*] 20 binaries already analysed - ending capa binary analysis now." "no_log"
print_output "[*] For complete analysis enable FULL_TEST." "no_log"
break 2
fi
else
print_output "[-] Binary behavior testing with capa for $(print_path "${lBIN_TO_CHECK}") not possible ... unsupported architecture" "no_log"
fi
done
done

[[ "${THREADED}" -eq 1 ]] && wait_for_pid "${lWAIT_PIDS_S18[@]}"

print_ln
lBINS_CHECKED_CNT=$(wc -l "${TMP_DIR}"/s18_checked.tmp 2>/dev/null || true)
print_output "[*] Found ${ORANGE}${lBINS_CHECKED_CNT/\ *}${NC} capa results in ${ORANGE}${#BINARIES[@]}${NC} binaries"
rm "${TMP_DIR}"/s18_checked.tmp 2>/dev/null

module_end_log "${FUNCNAME[0]}" "${lBINS_CHECKED_CNT/\ *}"
}

capa_runner_fct() {
local lBINARY="${1:-}"

local lATTACK_CODES_ARR=()
local lATTACK_CODE=""
local lBIN_NAME=""
lBIN_NAME="$(basename "${lBINARY}")"
local lBIN_MD5=""

print_output "[*] Testing binary behavior with capa for $(print_path "${lBINARY}")" "no_log"
"${EXT_DIR}"/capa "${lBINARY}" > "${LOG_PATH_MODULE}/capa_${lBIN_NAME}".log || print_output "[-] Capa analysis failed for ${lBINARY}" "no_log"

if [[ -s "${LOG_PATH_MODULE}/capa_${lBIN_NAME}.log" ]]; then
print_output "[+] Capa results for ${ORANGE}$(print_path "${lBINARY}")${NC}" "" "${LOG_PATH_MODULE}/capa_${lBIN_NAME}.log"
mapfile -t lATTACK_CODES_ARR < <(grep -o "T[0-9]\{4\}\(\.[0-9]\{3\}\)\?" "${LOG_PATH_MODULE}/capa_${lBIN_NAME}.log" || true)
for lATTACK_CODE in "${lATTACK_CODES_ARR[@]}"; do
# check for ATT&CK framework codes and insert the correct links
sed -i "/\ ${lATTACK_CODE}\ /a\[REF\] https://attack.mitre.org/techniques/${lATTACK_CODE/\./\/}" "${LOG_PATH_MODULE}/capa_${lBIN_NAME}.log" || true
done
sed -i '/\ MBC Objective/a \[REF\] https://github.com/MBCProject/mbc-markdown' "${LOG_PATH_MODULE}/capa_${lBIN_NAME}.log" || true
lBIN_MD5="$(md5sum "${lBIN_TO_CHECK}" | awk '{print $1}')"
if ( ! grep -q "${lBIN_MD5}" "${TMP_DIR}"/s18_checked.tmp 2>/dev/null); then
echo "${lBIN_MD5}" >> "${TMP_DIR}"/s18_checked.tmp
fi
else
print_output "[*] No capa results for $(print_path "${lBINARY}")" "no_log"
rm "${LOG_PATH_MODULE}/capa_${lBIN_NAME}.log" || true
fi
}
Loading