Skip to content

Commit

Permalink
Merge pull request #602 from BenediktMKuehne/gpt-module
Browse files Browse the repository at this point in the history
EMBA ChatGPT integration
  • Loading branch information
m-1-k-3 authored Jul 23, 2023
2 parents 530b67c + 66c7cf0 commit c15e810
Show file tree
Hide file tree
Showing 22 changed files with 692 additions and 24 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ config/emba_updater
config/emba_updater_data
config/module_blacklist.txt
config/jtr_wordlist.txt
config/gpt_config.env
2 changes: 1 addition & 1 deletion check_project.sh
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ check_tools() {
done
if ! [[ -d ./external/semgrep-rules/bash ]]; then
echo -e "\\n""${RED}""${BOLD}""Please install semgrep-rules to directory ./external to perform all checks""${NC}""\\n"
echo -e "${ORANGE}git clone https://github.com/returntocorp/semgrep-rules.git external/semgrep-rule${NC}"
echo -e "${ORANGE}git clone https://github.com/returntocorp/semgrep-rules.git external/semgrep-rules${NC}"
exit 1
fi
}
Expand Down
3 changes: 3 additions & 0 deletions config/gpt_config.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# OPENAI_API_KEY=""
# GPT_QUESTION="For the following code I need you to tell me how an attacker could exploit it and point out all vulnerabilities."
# MAXMIUM_GPT_PRIO=3
8 changes: 8 additions & 0 deletions config/gpt_template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content":
"Testing123"
}]}
51 changes: 49 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,64 @@ services:
- /boot:/boot:ro
environment:
- USER
- CONTAINER_NUMBER=1
networks:
- emba_runs
devices:
- /dev/fuse:/dev/fuse:rwm
cap_add:
- SYS_ADMIN
networks:
- emba_runs
ulimits:
core:
hard: 0
soft: 0

emba_quest:
image: embeddedanalyzer/emba:latest
read_only: true
tmpfs:
- /tmp
- /root/.config/
- /root/.local/
- /root/.cache/
- /root/.cache/pypoetry/virtualenvs:exec
- /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)
volumes:
- ${FIRMWARE}/:/firmware:ro
- ${LOG}/:/logs
- ${EMBA}/:/emba:ro
- ${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
network_mode: host
ulimits:
core:
hard: 0
soft: 0
security_opt:
- no-new-privileges:true

networks:
emba_runs:
name: emba_runs
Expand Down
68 changes: 67 additions & 1 deletion emba
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
INVOCATION_PATH="."
MODULE_COUNT=0


import_helper()
{
local HELPERS=()
Expand Down Expand Up @@ -108,7 +109,26 @@ check_cve_search_job() {
done
}

# $1: module group letter [P, S, L, F]
check_quest_container() {
print_ln "no_log"
print_output "[*] Checking Quest container ${QUEST_CONTAINER} dependencies \\n" "no_log"
# wait for quest container to finish network checks
while [[ "$(docker container inspect -f '{{.State.Running}}' "${QUEST_CONTAINER}")" == "true" ]]; do
if docker logs "${QUEST_CONTAINER}" | grep -q 'Elementary'; then
break
fi
sleep 0.1
done
reset
print_ln "no_log"
print_output "[*] Quest container ${QUEST_CONTAINER} dependency checks: \\n" "no_log"
docker logs "${QUEST_CONTAINER}"
print_output "[*] Finished Quest container ${QUEST_CONTAINER} dependency checks \\n" "no_log"
print_ln "no_log"
reset
}

# $1: module group letter [P, S, L, F, Q]
# $2: 0=single thread 1=multithread
# $3: HTML=1 - generate html file
run_modules()
Expand Down Expand Up @@ -702,9 +722,28 @@ main() {
if [[ "${ONLY_DEP}" -gt 0 ]]; then
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
else
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_}"
print_output "[*] Quest container ${QUEST_CONTAINER_} started and detached.\\n" "main"
print_ln "no_log"

check_quest_container "${QUEST_CONTAINER}" &
QUEST_PID="$!"
store_kill_pids "${QUEST_PID}"
disown "${QUEST_PID}" 2> /dev/null || true

EMBA="${INVOCATION_PATH}" FIRMWARE="${FIRMWARE_PATH}" LOG="${LOG_DIR}" docker-compose run --rm emba -c './emba -l /logs -f /firmware -i "$@"' _ "${ARGUMENTS[@]}"
D_RETURN=$?
# stop quest container
if [[ "$( docker container inspect -f '{{.State.Status}}' "${QUEST_CONTAINER_}" )" == "running" ]]; then
docker kill "${QUEST_CONTAINER_}"
fi
fi
enable_strict_mode "${STRICT_MODE}" 0

Expand Down Expand Up @@ -739,6 +778,29 @@ main() {
trap 'initial_status_bar' WINCH
fi

#######################################################################################
# Quests (Q-modules)
#######################################################################################

if [[ "${CONTAINER_NUMBER}" -eq 2 ]] ; then
while ! grep -q "Pre-checking phase started" "${LOG_DIR}"/"${MAIN_LOG_FILE}"; do
sleep 1
done
run_modules "Q" "${THREADED}" "0"
[[ "${THREADED}" -eq 1 ]] && wait_for_pid "${WAIT_PIDS[@]}"
print_output "[*] Quest container done" "main"
exit 0
elif [[ "${USE_DOCKER}" -eq 0 && "${IN_DOCKER}" -eq 0 ]] ; then
while ! grep -q "Pre-checking phase started" "${LOG_DIR}"/"${MAIN_LOG_FILE}"; do
sleep 1
done
# dev-mode
run_modules "Q" "${THREADED}" "0" &
export Q_MOD_PID="$!"
elif [[ "${CONTAINER_NUMBER}" -ne 1 ]]; then
docker logs "${QUEST_CONTAINER}" --follow | grep "ok" && print_output "[*] Quest container ${QUEST_CONTAINER_} failed the dependancy check" "main"
fi

#######################################################################################
# Pre-Check (P-modules)
#######################################################################################
Expand Down Expand Up @@ -849,6 +911,10 @@ main() {

write_notification "Reporting phase ended"

if [[ "${USE_DOCKER}" -eq 0 && "${THREADED}" -eq 1 && "${IN_DOCKER}" -eq 0 ]] && [[ -n "${Q_MOD_PID}" ]]; then
wait_for_pid "${Q_MOD_PID}"
fi

if [[ "${TESTING_DONE}" -eq 1 ]]; then
if [[ "${FINAL_FW_RM}" -eq 1 && -d "${LOG_DIR}"/firmware ]]; then
print_output "[*] Removing temp firmware directory\\n" "no_log"
Expand Down
6 changes: 6 additions & 0 deletions helpers/helpers_emba_defaults.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ set_defaults() {
export QRUNTIME="20s"

export SHELLCHECK=1

export QUEST_CONTAINER=""
export GPT_OPTION=0 # 0 -> off 1-> unpayed plan 2 -> no rate-limit
export GPT_QUESTION="For the following code I need you to tell me how an attacker could exploit it and point out all vulnerabilities:"
export MINIMUM_GPT_PRIO=2 # [3 downto 0] 3 -> everything gets checked; 0 -> nothing gets checked

export SHORT_PATH=0 # short paths in cli output
export THREADED=0 # 0 -> single thread
# 1 -> multi threaded
Expand Down
89 changes: 88 additions & 1 deletion helpers/helpers_emba_dependency_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,94 @@ dependency_check()
{
module_title "Dependency check" "no_log"

print_ln "no_log"

#######################################################################################
## 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
echo -e "$RED""not ok""$NC"
print_output "[-] ERROR: Quest container has no internet connection!" "no_log"
exit 1
else
echo -e "$GREEN""ok""$NC"
fi
if [[ -f "${CONFIG_DIR}/gpt_config.env" ]]; then
if grep -v -q "#" "${CONFIG_DIR}/gpt_config.env"; then
# readin gpt_config.env
while read -r LINE; do
if [[ "${LINE}" == *'='* ]] && [[ "${LINE}" != '#'* ]]; then
export "$(echo "${LINE}" | xargs)"
fi
done < "${CONFIG_DIR}/gpt_config.env"
fi
fi
if [[ -z "${OPENAI_API_KEY}" ]]; then
print_output "$(indent "ChatGPT-API key not set - ${ORANGE}see https://github.com/e-m-b-a/emba/wiki/AI-supported-firmware-analysis for more information${NC}")" "no_log"
# The following if clause is currently not working! We have not loaded the profile in this stage
# TODO: Find a workaround!
if [[ "${GPT_OPTION}" -eq 1 ]]; then
DEP_ERROR=1
fi
else
local RETRIES_=0
# on the host we try it only 10 times:
local MAX_RETRIES=10
if [[ "$IN_DOCKER" -eq 1 ]]; then
# within the Quest container we can keep trying it as it does not matter if the container starts up later
MAX_RETRIES=200
fi
local SLEEPTIME=30
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" \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-d @"${CONFIG_DIR}/gpt_template.json" --write-out "%{http_code}" -o /tmp/chatgpt-test.json -sS)

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
fi
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"
# 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
fi
if grep -q "Testing phase ended" "${LOG_DIR}"/"${MAIN_LOG_FILE}"; then
print_output " Testing phase ended - \\c" "no_log"
echo -e "$RED""exit now""$NC"
DEP_ERROR=1
fi
done
fi
else
print_output " Isolation - ${GREEN}""ok""${NC}" "no_log"
fi
if [[ "${CONTAINER_NUMBER}" -eq 2 ]] ; then
if [[ $ONLY_DEP -gt 0 ]] && [[ $FORCE -ne 0 ]]; then
exit 0
fi
fi
print_ln "no_log"
#######################################################################################
# Elementary checks
Expand All @@ -207,7 +295,6 @@ dependency_check()
# As the container runs as root we should not run into issues within the container.
# Outside the container we can run mostly without root privs - this is currently under evaluation
# Some other nice features like restarting the mongod will not work without root privs.
print_output " user permissions" "no_log"
if [[ $QEMULATION -eq 1 && $EUID -ne 0 ]] || [[ $USE_DOCKER -eq 1 && $EUID -ne 0 ]] || [[ $FULL_EMULATION -eq 1 && $EUID -ne 0 ]]; then
if [[ $QEMULATION -eq 1 && $USE_DOCKER -eq 0 ]] || [[ $FULL_EMULATION -eq 1 && $USE_DOCKER -eq 0 ]]; then
print_output " user permission - emulation mode - \\c" "no_log"
Expand Down
7 changes: 6 additions & 1 deletion helpers/helpers_emba_helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,12 @@ cleaner() {
print_output "[*] Interrupt detected!" "no_log"
fi
print_output "[*] Final cleanup started." "no_log"

if [[ "$IN_DOCKER" -eq 0 ]] && [[ -n "${QUEST_CONTAINER}" ]]; then
if [[ "$( docker container inspect -f '{{.State.Status}}' "${QUEST_CONTAINER}" )" == "running" ]]; then
print_output "[*] Stopping Quest Container ..." "no_log"
docker kill "${QUEST_CONTAINER}"
fi
fi
# stop inotifywait on host
if [[ "$IN_DOCKER" -eq 0 ]] && pgrep -f "inotifywait.*$LOG_DIR.*" &> /dev/null 2>&1; then
print_output "[*] Stopping inotify ..."
Expand Down
8 changes: 5 additions & 3 deletions helpers/helpers_emba_html_generator.sh
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ add_link_tags() {
HTML_LINK="$(echo "$IMAGE_LINK" | sed -e 's@PICTURE@'"$(basename "$REF_LINK")"'@' || true)"
LINK_COMMAND_ARR+=( "$LINE_NUMBER_INFO_PREV"'s@$@'"$HTML_LINK"'@' )
fi
elif [[ ("$REF_LINK" =~ ^(p|l|s|f){1}[0-9]{2,3}$ ) || ("$REF_LINK" =~ ^(p|l|s|f){1}[0-9]{2,3}\#.*$ ) ]] ; then
elif [[ ("$REF_LINK" =~ ^(p|l|s|q|f){1}[0-9]{2,3}$ ) || ("$REF_LINK" =~ ^(p|l|s|q|f){1}[0-9]{2,3}\#.*$ ) ]] ; then
REF_ANCHOR=""
if [[ "$REF_LINK" =~ ^(p|l|s|f){1}[0-9]{2,3}\#.*$ ]] ; then
if [[ "$REF_LINK" =~ ^(p|l|s|q|f){1}[0-9]{2,3}\#.*$ ]] ; then
REF_ANCHOR="$(echo "$REF_LINK" | cut -d"#" -f2 || true)"
REF_LINK="$(echo "$REF_LINK" | cut -d"#" -f1 || true)"
fi
Expand Down Expand Up @@ -662,8 +662,10 @@ add_arrows()
readarray -t L_MODULE_ARR < <(find "$ABS_HTML_PATH" -maxdepth 1 -name "*.html" | grep -a -E "./l[0-9]*.*" | sort -V || true)
local F_MODULE_ARR
readarray -t F_MODULE_ARR < <(find "$ABS_HTML_PATH" -maxdepth 1 -name "*.html" | grep -a -E "./f[0-9]*.*" | sort -V || true)
local Q_MODULE_ARR
readarray -t Q_MODULE_ARR < <(find "$ABS_HTML_PATH" -maxdepth 1 -name "*.html" | grep -a -E "./q[0-9]*.*" | sort -V || true)
local ALL_MODULE_ARR
ALL_MODULE_ARR=( "$ABS_HTML_PATH""/""$INDEX_FILE" "${P_MODULE_ARR[@]}" "${S_MODULE_ARR[@]}" "${L_MODULE_ARR[@]}" "${F_MODULE_ARR[@]}" )
ALL_MODULE_ARR=( "$ABS_HTML_PATH""/""$INDEX_FILE" "${P_MODULE_ARR[@]}" "${S_MODULE_ARR[@]}" "${Q_MODULE_ARR[@]}" "${L_MODULE_ARR[@]}" "${F_MODULE_ARR[@]}")
for M_NUM in "${!ALL_MODULE_ARR[@]}"; do
if [[ "$M_NUM" -gt 0 ]] ; then
FIRST_LINK="${ALL_MODULE_ARR[$(( M_NUM - 1 ))]}"
Expand Down
3 changes: 1 addition & 2 deletions helpers/helpers_emba_internet_access.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# EMBA is licensed under GPLv3
#
# Author(s): Michael Messner
# Author(s): Michael Messner, Benedikt Kuehne

# Description: Multiple useful helpers used to access online resources

Expand Down Expand Up @@ -132,4 +132,3 @@ kernel_downloader() {
write_log "$OUTPUTTER" "$LOG_DIR/kernel_downloader.log"
done
}

Loading

0 comments on commit c15e810

Please sign in to comment.