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

Firmware diffing preparation #804

Merged
merged 26 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ RUN apt-get update && \
apt-get -y install wget kmod procps sudo dialog apt curl git

# install EMBA, disable coredumps and final cleanup
RUN yes | sudo /installer.sh -D && \
RUN yes | sudo /installer.sh -s -D && \
ulimit -c 0 && rm -rf /var/lib/apt/lists/*

WORKDIR /emba
Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@ Author(s): Michael Messner, Pascal Eckmann, Benedikt Kühne
# EMBA
## The security analyzer for firmware of embedded devices

*EMBA* is designed as the central firmware analysis tool for penetration testers. It supports the complete security analysis process starting with the *firmware extraction* process, doing *static analysis* and *dynamic analysis* via emulation and finally generating a web report. *EMBA* automatically discovers possible weak spots and vulnerabilities in firmware. Examples are insecure binaries, old and outdated software components, potentially vulnerable scripts or hard-coded passwords. *EMBA* is a command line tool with the option to generate an easy to use web report for further analysis.
*EMBA* is designed as the central firmware analysis tool for penetration testers and product security teams. It supports the complete security analysis process starting with *firmware extraction*, doing *static analysis* and *dynamic analysis* via emulation and finally generating a web report. *EMBA* automatically discovers possible weak spots and vulnerabilities in firmware. Examples are insecure binaries, old and outdated software components, potentially vulnerable scripts, or hard-coded passwords. *EMBA* is a command line tool with the possibility to generate an easy-to-use web report for further analysis.

*EMBA* combines multiple established analysis tools and can be started with one simple command. Afterwards it tests the firmware for possible security risks and interesting areas for further investigation. No manual installation of all helpers, once the integrated installation script has been executed, you are ready to test your firmware.

*EMBA* is designed to assist penetration testers and not as a standalone tool without human interaction. *EMBA* should provide as much information as possible about the firmware, that the tester can decide on focus areas and is responsible for verifying and interpreting the results.
*EMBA* assists the penetration testers and product security teams in the identification of weak spots and vulnerabilities in the firmware image. *EMBA* provides as much information as possible about the firmware, that the tester can decide on focus areas and is responsible for verifying and interpreting the results.

[![Watch EMBA](https://raw.githubusercontent.com/wiki/e-m-b-a/emba/images/youtube-emba.png)](https://youtu.be/_dvdy3klFFY "Watch EMBA")

Expand Down
4 changes: 2 additions & 2 deletions config/report_templates/F20_vul_aggregator-post.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ print_output "[*] Exploitability notes:"
print_output "$(indent "${ORANGE}R$NC - remote exploits")"
print_output "$(indent "${ORANGE}L$NC - local exploits")"
print_output "$(indent "${ORANGE}D$NC - DoS exploits")"
print_output "$(indent "${ORANGE}G$NC - PoC code found on Github (unknown exploit vector)")"
write_link "https://github.com/trickest/cve"
# print_output "$(indent "${ORANGE}G$NC - PoC code found on Github (unknown exploit vector)")"
# write_link "https://github.com/trickest/cve"
print_output "$(indent "${ORANGE}P$NC - PoC code found on Packetstormsecurity (unknown exploit vector)")"
write_link "https://packetstormsecurity.com/files/tags/exploit/"
print_output "$(indent "${ORANGE}S$NC - PoC code found on Snyk vulnerability database (unknown exploit vector)")"
Expand Down
4 changes: 2 additions & 2 deletions config/report_templates/S26_kernel_vuln_verifier-post.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ print_output "$(indent "${ORANGE}EDB$NC - Exploit code found in the Exploit data
write_link "https://exploit-db.com"
print_output "$(indent "${ORANGE}MSF$NC - Exploit code found in the Metasploit framework")"
write_link "https://github.com/rapid7/metasploit-framework"
print_output "$(indent "${ORANGE}GH$NC - PoC code found on Github (via trickest)")"
write_link "https://github.com/trickest/cve"
# print_output "$(indent "${ORANGE}GH$NC - PoC code found on Github (via trickest)")"
# write_link "https://github.com/trickest/cve"
print_output "$(indent "${ORANGE}PS$NC - PoC code found on Packetstormsecurity")"
write_link "https://packetstormsecurity.com/files/tags/exploit/"
print_output "$(indent "${ORANGE}SNYK$NC - PoC code found on Snyk vulnerability database")"
Expand Down
12 changes: 1 addition & 11 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,9 @@ services:
- /root
- /root/.cargo/bin:exec
- /run/lock
- /var
- /var/run
- /var/tmp
- /var/lock
- /var/lib/nikto:exec
- /external/arachni/arachni-1.6.1.3-0.6.1.1/bin/../.system/arachni-ui-web/config/component_cache
- /external/arachni/arachni-1.6.1.3-0.6.1.1/bin/../.system/arachni-ui-web/db
- /external/arachni/arachni-1.6.1.3-0.6.1.1/bin/../.system/arachni-ui-web/tmp
- /external/arachni/arachni-1.6.1.3-0.6.1.1/bin/../.system/../logs
- /external/arachni/arachni-1.6.1.3-0.6.1.1/bin/../.system/home
# build: .
# /dev is needed for the system emulator (L10)
# /lib/modules is needed for modules which are loading kernel modules (e.g. extractors)
# /boot is needed for modules which are loading kernel modules (e.g. extractors like P10)
Expand All @@ -93,8 +85,6 @@ services:
- ${EMBA}/external/linux_kernel_sources/:/external/linux_kernel_sources
- /etc/localtime:/etc/localtime:ro
- /dev:/dev
# - /lib/modules:/lib/modules:ro
# - /boot:/boot:ro
environment:
- USER
- CONTAINER_NUMBER=2
Expand All @@ -104,7 +94,7 @@ services:
hard: 0
soft: 0
security_opt:
- no-new-privileges:true
- no-new-privileges:true

networks:
emba_runs:
Expand Down
79 changes: 70 additions & 9 deletions emba
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,11 @@ main() {
print_output "[*] Original user: ${ORANGE}${SUDO_USER:-${USER}}${NC}" "no_log"
print_output "[*] Notification process started with PID ${ORANGE}${NOTIFICATION_PID}${NC}" "no_log"
echo "${SUDO_USER:-${USER}}" > "${LOG_DIR}"/orig_user.log
echo "UID: $(id -u "${SUDO_USER:-${USER}}")" >> "${LOG_DIR}"/orig_user.log
echo "GID: $(id -g "${SUDO_USER:-${USER}}")" >> "${LOG_DIR}"/orig_user.log
{
echo "UID: $(id -u "${SUDO_USER:-${USER}}")"
echo "GID: $(id -g "${SUDO_USER:-${USER}}")"
echo "PROXY: $(sudo -E -u "${SUDO_USER:-${USER}}" env | grep -E "http(s)_proxy" | cut -d = -f2)"
} >> "${LOG_DIR}"/orig_user.log
fi

if [[ "${IN_DOCKER}" -eq 0 ]]; then
Expand Down Expand Up @@ -547,11 +550,15 @@ main() {
export FIRMWARE_PATH="${LOG_DIR}"/firmware/firmware_docker_extracted.tar
export OUTPUT_DIR="${FIRMWARE_PATH}"
export FIRMWARE=1
elif [[ -f "${FIRMWARE_PATH}" ]]; then
elif [[ -f "${FIRMWARE_PATH}" ]] && [[ -z "${FIRMWARE_PATH1}" ]]; then
PRE_CHECK=1
print_output "[*] Firmware binary detected." "no_log"
print_output " EMBA starts with the pre-testing phase." "no_log"
export OUTPUT_DIR="${FIRMWARE_PATH}"
elif [[ -f "${FIRMWARE_PATH}" ]] && [[ -f "${FIRMWARE_PATH1}" ]]; then
DIFF_MODE=1
print_output "[*] Multiple firmware binarie detected." "no_log"
print_output " EMBA starts in firmware diff mode ." "no_log"
elif [[ -f "${KERNEL_CONFIG}" && "${KERNEL}" -eq 1 ]]; then
print_output "[*] Kernel configuration file detected." "no_log"
else
Expand Down Expand Up @@ -675,9 +682,9 @@ main() {

OPTIND=1
ARGUMENTS=()
while getopts a:A:BcC:d:De:Ef:Fghijk:l:m:N:op:P:QrsStT:UX:yY:WxzZ: OPT ; do
while getopts a:A:BcC:d:De:Ef:Fghijk:l:m:N:o:p:P:QrsStT:UX:yY:WxzZ: OPT ; do
case ${OPT} in
D|f|i|l)
D|f|i|l|o)
;;
*)
if [[ -v OPTARG[@] ]] ; then
Expand Down Expand Up @@ -712,6 +719,9 @@ main() {
if [[ "${ONLY_DEP}" -eq 0 ]]; then
# store some details that we do not have in the docker container:
echo "${FIRMWARE_PATH}" >> "${TMP_DIR}"/fw_name.log
if [[ "${DIFF_MODE}" -gt 0 ]]; then
echo "${FIRMWARE_PATH1}" >> "${TMP_DIR}"/fw_name.log
fi
echo "${LOG_DIR}" >> "${TMP_DIR}"/emba_log_dir.log
echo "${EMBA_COMMAND}" >> "${TMP_DIR}"/emba_command.log
fi
Expand All @@ -722,13 +732,20 @@ main() {

disable_strict_mode "${STRICT_MODE}" 0
if [[ "${ONLY_DEP}" -gt 0 ]]; then
# check dependencies mode:
EMBA="${INVOCATION_PATH}" FIRMWARE="${FIRMWARE_PATH}" docker-compose run --rm emba -c './emba -f /firmware -i "$@"' _ "${ARGUMENTS[@]}"
D_RETURN=$?
EMBA="${INVOCATION_PATH}" FIRMWARE="${FIRMWARE_PATH}" docker-compose run --rm emba_quest -c './emba -f /firmware -i "$@"' _ "${ARGUMENTS[@]}"
if [[ $? -ne ${D_RETURN} ]]; then
D_RETURN=1
fi
elif [[ "${DIFF_MODE}" -gt 0 ]]; then
FIRMWARE_PATH1="$(abs_path "${FIRMWARE_PATH1}")"
# firmware diff mode - needs two firmware files:
EMBA="${INVOCATION_PATH}" FIRMWARE="${FIRMWARE_PATH}" LOG="${LOG_DIR}" docker-compose run -v "${FIRMWARE_PATH1}":/firmware2 --rm emba -c './emba -l /logs -f /firmware -o /firmware2 -i "$@"' _ "${ARGUMENTS[@]}"
D_RETURN=$?
else
# default EMBA analysis mode:
local QUEST_CONTAINER_=""
QUEST_CONTAINER_="$(EMBA="${INVOCATION_PATH}" FIRMWARE="${FIRMWARE_PATH}" LOG="${LOG_DIR}" docker-compose run --detach --rm emba_quest -c './emba -l /logs -f /firmware -i "$@"' _ "${ARGUMENTS[@]}")"
export QUEST_CONTAINER="${QUEST_CONTAINER_}"
Expand Down Expand Up @@ -784,7 +801,7 @@ main() {
# Quests (Q-modules)
#######################################################################################

if [[ -v CONTAINER_NUMBER ]]; then
if [[ -v CONTAINER_NUMBER ]] && [[ "${DIFF_MODE}" -ne 1 ]]; then
if [[ "${CONTAINER_NUMBER}" -eq 2 ]] ; then
while ! grep -q "Pre-checking phase started" "${LOG_DIR}"/"${MAIN_LOG_FILE}"; do
sleep 1
Expand All @@ -809,7 +826,7 @@ main() {
#######################################################################################
# Pre-Check (P-modules)
#######################################################################################
if [[ "${PRE_CHECK}" -eq 1 ]] ; then
if [[ "${PRE_CHECK}" -eq 1 ]] && [[ "${DIFF_MODE}" -ne 1 ]]; then

print_ln "no_log"
if [[ -d "${LOG_DIR}" ]]; then
Expand Down Expand Up @@ -844,11 +861,55 @@ main() {
# print_output "[!] LINUX_PATH_ARRAY: ${#ROOT_PATH[@]}"
fi

#######################################################################################
# Diff mode (D-modules - currently not public)
#######################################################################################
if [[ "${DIFF_MODE}" -eq 1 ]] ; then

# Diff mode is unthreaded
export THREADED=0

print_ln "no_log"
if [[ -d "${LOG_DIR}" ]]; then
print_output "[!] Diff mode started on ""$(date)" "main"
print_output "$(indent "${NC}""1st Firmware binary path: ""${FIRMWARE_PATH}")" "main"
print_output "$(indent "${NC}""2nd Firmware binary path: ""${FIRMWARE_PATH1}")" "main"
else
print_output "[!] Diff mode started on ""$(date)"
print_output "$(indent "${NC}""1st Firmware binary path: ""${FIRMWARE_PATH}")" "no_log"
print_output "$(indent "${NC}""2nd Firmware binary path: ""${FIRMWARE_PATH1}")" "no_log"
fi
write_notification "Diff mode started"

if [[ "$(find "${MOD_DIR_LOCAL}" -name "D[0-9][0-9]_*.sh" | wc -l)" -gt 0 ]] || [[ "$(find "${MOD_DIR}" -name "D[0-9][0-9]_*.sh" | wc -l)" -gt 0 ]]; then
run_modules "D" "${THREADED}" "0"
else
if [[ -d "${LOG_DIR}" ]]; then
print_output "[!] Diff mode currently not supported in this EMBA installation." "main"
print_output "[!] EMBA exits now." "main"
else
print_output "[!] Diff mode currently not supported in this EMBA installation." "no_log"
print_output "[!] EMBA exits now." "no_log"
fi
exit 1
fi

if [[ -d "${LOG_DIR}" ]]; then
print_output "[!] Diff mode ended on ""$(date)"" and took about ""$(show_runtime)"" \\n" "main"
else
print_output "[!] Diff mode ended on ""$(date)"" and took about ""$(show_runtime)"" \\n" "no_log"
fi
write_notification "Diff mode finished"

TESTING_DONE=1

fi

#######################################################################################
# Firmware-Check (S modules)
#######################################################################################
WAIT_PIDS=()
if [[ ${FIRMWARE} -eq 1 ]] ; then
if [[ ${FIRMWARE} -eq 1 ]] && [[ "${DIFF_MODE}" -ne 1 ]]; then
print_output "\n=================================================================\n" "no_log"

if [[ -d "${LOG_DIR}" ]]; then
Expand Down Expand Up @@ -878,7 +939,7 @@ main() {
#######################################################################################
# Live Emulation - Check (L-modules)
#######################################################################################
if [[ "${FULL_EMULATION}" -eq 1 ]] ; then
if [[ "${FULL_EMULATION}" -eq 1 ]] && [[ "${DIFF_MODE}" -ne 1 ]]; then
print_output "\n=================================================================\n" "no_log"
if [[ -d "${LOG_DIR}" ]]; then
print_output "[!] System emulation phase started on ""$(date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "main"
Expand Down
8 changes: 5 additions & 3 deletions helpers/helpers_emba_defaults.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@

set_defaults() {
# if this is a release version set RELEASE to 1, add a banner to config/banner and name the banner with the version details
export RELEASE=1
export EMBA_VERSION="1.3.0"
export RELEASE=0
export EMBA_VERSION="1.3.x"

export CLEANED=0 # used for the final cleaner function for not running it multiple times
export STRICT_MODE=0
Expand All @@ -40,6 +40,8 @@ set_defaults() {
export KERNEL=0
export KERNEL_CONFIG=""
export FIRMWARE_PATH=""
export FIRMWARE_PATH1=""
export DIFF_MODE=0
export FW_VENDOR=""
export FW_VERSION=""
export FW_DEVICE=""
Expand Down Expand Up @@ -89,7 +91,7 @@ set_defaults() {
export EXT_DIR="$INVOCATION_PATH""/external"
export HELP_DIR="$INVOCATION_PATH""/helpers"
export MOD_DIR="$INVOCATION_PATH""/modules"
export MOD_DIR_LOCAL="$INVOCATION_PATH""/modules_local"
export MOD_DIR_LOCAL="$INVOCATION_PATH""/EMBA-Non-free/modules_local"
export PID_LOGGING=0
# this will be in TMP_DIR/pid_notes.log
export PID_LOG_FILE="pid_notes.log"
Expand Down
49 changes: 29 additions & 20 deletions helpers/helpers_emba_dependency_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,19 @@ dependency_check()

print_ln "no_log"

USER_PROXY="$(sudo -E -u "${SUDO_USER:-${USER}}" env | grep -E "http(s)_proxy" | cut -d = -f2 | sort -u || true)"
export CURL_OPTS=""
if [[ -n "${USER_PROXY}" ]]; then
CURL_OPTS="--proxy ${USER_PROXY}"
fi

#######################################################################################
## Quest Container
#######################################################################################
print_output "[*] Network connection:" "no_log"
if [[ "${CONTAINER_NUMBER}" -ne 1 ]]; then
print_output " Internet connection - \\c" "no_log"
# if ! ping 8.8.8.8 -q -c 1 -W 1 &>/dev/null ; then
if ! curl -Is https://www.google.com &>/dev/null ; then
if ! curl "${CURL_OPTS}" -Is https://www.google.com &>/dev/null ; then
echo -e "$RED""not ok""$NC"
print_output "[-] Warning: Quest container has no internet connection!" "no_log"
else
Expand Down Expand Up @@ -241,35 +246,39 @@ dependency_check()
while true; do
local HTTP_CODE_=400
print_output " OpenAI-API key - \\c" "no_log"
HTTP_CODE_=$(curl https://api.openai.com/v1/chat/completions -H "Content-Type: application/json" \
HTTP_CODE_=$(curl -sS https://api.openai.com/v1/chat/completions -H "Content-Type: application/json" \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-d @"${CONFIG_DIR}/gpt_template.json" --write-out "%{http_code}" -o /tmp/chatgpt-test.json -sS)
-d @"${CONFIG_DIR}/gpt_template.json" --write-out "%{http_code}" -o /tmp/chatgpt-test.json)

if [[ "${HTTP_CODE_}" -eq 200 ]] ; then
echo -e "$GREEN""ok""$NC"
rm /tmp/chatgpt-test.json
break
else
if jq '.error.code' /tmp/chatgpt-test.json | grep -q "rate_limit_exceeded" ; then
# rate limit handling - if we got a response like:
# Please try again in 20s
echo -e "$RED""not ok (rate limit issues)""$NC"
if jq '.error.message' /tmp/chatgpt-test.json | grep -q "Please try again in " ; then
# print_output "GPT API test #${RETRIES_} - \\c" "no_log"
sleep "${SLEEPTIME}"s
# sleeptime gets adjusted on every failure
SLEEPTIME=$((SLEEPTIME+5))
((RETRIES_+=1))
[[ "${RETRIES_}" -lt "${MAX_RETRIES}" ]] && continue
if [[ -f /tmp/chatgpt-test.json ]]; then
if jq '.error.code' /tmp/chatgpt-test.json | grep -q "rate_limit_exceeded" ; then
# rate limit handling - if we got a response like:
# Please try again in 20s
echo -e "$RED""not ok (rate limit issues)""$NC"
if jq '.error.message' /tmp/chatgpt-test.json | grep -q "Please try again in " ; then
# print_output "GPT API test #${RETRIES_} - \\c" "no_log"
sleep "${SLEEPTIME}"s
# sleeptime gets adjusted on every failure
SLEEPTIME=$((SLEEPTIME+5))
((RETRIES_+=1))
[[ "${RETRIES_}" -lt "${MAX_RETRIES}" ]] && continue
fi
fi
if jq '.error.code' /tmp/chatgpt-test.json | grep -q "insufficient_quota" ; then
echo -e "$RED""not ok (quota limit issues)""$NC"
break
fi
fi
if jq '.error.code' /tmp/chatgpt-test.json | grep -q "insufficient_quota" ; then
echo -e "$RED""not ok (quota limit issues)""$NC"
break
fi
echo -e "$RED""not ok""$NC"
print_output "[-] ChatGPT error while testing the API-Key: ${OPENAI_API_KEY}" "no_log"
print_output "[-] ERROR response: $(cat /tmp/chatgpt-test.json)" "no_log"
if [[ -f /tmp/chatgpt-test.json ]]; then
print_output "[-] ERROR response: $(cat /tmp/chatgpt-test.json)" "no_log"
fi
# Note: we are running into issues in the case where the key can't be verified, but GPT is not enabled at all
# In such a case we will fail the check without the need of GPT
DEP_ERROR=1
Expand Down