This commit is contained in:
2026-03-27 13:44:34 +01:00
commit 895b866e72
2 changed files with 937 additions and 0 deletions

768
bpfdoor.sh Executable file
View File

@@ -0,0 +1,768 @@
#!/usr/bin/env bash
#
# Rapid7 Labs / Enhanced Linux BPFDoor Detection Script
#
# Detects both “classic” and newer BPFDoor variants using:
# - Known hashes (optional, extendable)
# - Suspicious mutex/lock files in /var/run
# - Auto-start hooks in /etc/sysconfig
# - BPF filter usage via ss -0pb and /proc/net/packet
# - RAW / packet socket usage (Including SOCK_DGRAM Layer 2 Stripping)
# - Suspicious env vars (HOME=/tmp, HISTFILE=/dev/null, MYSQL_HISTFILE=/dev/null)
# - Known masqueraded process names + paths (Verified against Whitelist)
# - Process-specific Binary Signatures (66666666H, ttcompat, etc.)
# - Active C2 Reverse Shell Connections
# - Memory-resident (deleted) binary execution
# - Kernel Stack Tracing (packet_recvmsg blocking)
# - Suspicious ports (4239143390, 8000)
# - Suspicious strings & UPX packing in candidate binaries
# - Basic persistence checks (cron, systemd, rc scripts)
#
# Requires: bash, grep, awk, ps, readlink, stat, ss OR netstat, lsof (optional), strings, find, hexdump, dd, dig (optional)
#
# This script is best-effort and may produce false positives. Use results as
# triage input, not as a sole source of truth.
set -o pipefail
VERSION="1.1"
HOSTNAME="$(hostname)"
DATE="$(date +%Y-%m-%d_%H-%M-%S)"
LOGFILE="bpfdoor_report_${HOSTNAME}_${DATE}.log"
# Global script PIDs to exclude from our own checks
SCRIPT_PID=$$
SCRIPT_PPID=$PPID
# ---- Colours ---------------------------------------------------------------
#if [ -t 1 ]; then
RED=$'\033[1;31m'
GREEN=$'\033[1;32m'
YELLOW=$'\033[1;33m'
BLUE=$'\033[1;34m'
CYAN=$'\033[1;36m'
MAGENTA=$'\033[1;35m'
ORANGE=$'\033[38;5;208m'
NC=$'\033[0m'
#else
#RED=""; GREEN=""; YELLOW=""; BLUE=""; CYAN=""; MAGENTA=""; NC=""; ORANGE=""
#fi
# ---- Known malicious hashes (minimal baseline, extend as needed) -----------
declare -A MALWARE_SHA256=()
declare -A MALWARE_MD5=()
# ---- Suspicious names and patterns ----------------------------------------
SUSPICIOUS_MUTEX_FILES=(
"/var/run/aepmonend.pid" "/var/run/auditd.lock" "/var/run/cma.lock"
"/var/run/console-kit.pid" "/var/run/consolekit.pid" "/var/run/daemon.pid"
"/var/run/hald-addon.pid" "/var/run/hald-smartd.pid" "/var/run/hp-health.pid"
"/var/run/hpasmlit.lock" "/var/run/hpasmlited.pid" "/var/run/lldpad.lock"
"/var/run/mcelog.pid" "/var/run/system.pid" "/var/run/uvp-srv.pid"
"/var/run/vmtoolagt.pid" "/var/run/xinetd.lock"
)
# Command-line strings BPFDoor uses to hide
SUSPICIOUS_PROCS=(
"/sbin/agetty" "/sbin/auditd" "/sbin/mingetty" "/sbin/sgaSolAgent" "/sbin/udevd"
"/usr/bin/python -Es /usr/sbin/tuned" "/usr/bin/uvp-srv" "/usr/lib/polkit-1/polkitd"
"/usr/lib/systemd/systemd-journald" "/usr/lib/systemd/systemd-machined"
"/usr/libexec/hald-addon-volume" "/usr/libexec/postfix/master" "/usr/libexec/rtkit-daemon"
"/usr/libexec/upowerd" "/usr/sbin/NetworkManager" "/usr/sbin/abrtd" "/usr/sbin/acpid"
"/usr/sbin/atd" "/usr/sbin/chronyd" "/usr/sbin/console-kit" "/usr/sbin/console-kit-daemon"
"/usr/sbin/crond" "/usr/sbin/mcelog" "/usr/sbin/rsyslogd" "/usr/sbin/smartd"
"/usr/sbin/sshd" "[charger_manager]" "[kaluad_sync]" "[scsi_tmf_6]" "[watchdogd]"
"[cpu/0]" "avahi-daemon: chroot helper" "cmathreshd" "dbus-daemon --system"
"hald-addon-acpi" "hald-runner" "hpasmlited" "lldpad -d" "nginx: master process"
"pickup -l -t fifo -u" "/sbin/ora_ppmond" "/usr/bin/pulse-helper"
)
# The ACTUAL physical paths of legitimate daemons. If a process masquerades as one of
# the above but isn't running from one of these files, it gets flagged.
WHITELIST_EXES=(
"/sbin/agetty" "/sbin/auditd" "/sbin/mingetty" "/sbin/udevd"
"/usr/bin/python" "/usr/bin/python2" "/usr/bin/python3"
"/usr/sbin/tuned" "/usr/lib/polkit-1/polkitd" "/usr/libexec/postfix/pickup"
"/usr/libexec/postfix/master" "/usr/sbin/NetworkManager"
"/usr/sbin/console-kit-daemon" "/usr/sbin/crond" "/usr/sbin/mcelog"
"/usr/sbin/rsyslogd" "/usr/sbin/smartd" "/usr/sbin/avahi-daemon"
"/usr/bin/dbus-daemon" "/usr/libexec/hald-addon-acpi" "/usr/libexec/hald-runner"
"/usr/sbin/lldpad" "/usr/sbin/nginx" "/usr/sbin/sshd" "/usr/sbin/acpid"
"/usr/sbin/atd" "/usr/sbin/chronyd" "/usr/sbin/console-kit"
"/usr/libexec/rtkit-daemon" "/usr/libexec/upowerd" "/usr/sbin/abrtd"
)
SUSPICIOUS_STRINGS=(
"HISTFILE=/dev/null" "MYSQL_HISTFILE=/dev/null" "ttcompat" ":h:d:l:s:b:t:"
":f:wiunomc" ":f:x:wiuoc" "LibTomCrypt 1.17"
"Private key does not match the public certificate"
"I5*AYbs@LdaWbsO" "3458" "8543" "1234"
)
KNOWN_C2_HOSTS=(
"ntpupdate.ddnsgeek.com" "ntpussl.instanthq.com" "ntpd.casacam.net" "ntpupdate.ygto.com"
)
SUSPICIOUS_PORTS_RANGE_START=42391
SUSPICIOUS_PORTS_RANGE_END=43390
SUSPICIOUS_PORT_SINGLE=8000
SUSPICIOUS_FILES_TMP=()
# ---- Logging helpers -------------------------------------------------------
log() {
local level="$1"; shift
local msg="$*"
local ts
ts="$(date +'%Y-%m-%d %H:%M:%S')"
# Log cleanly to file
echo "[$ts] [$level] $msg" >> "$LOGFILE"
# Apply colors for terminal output
local color="${NC}"
case "$level" in
"CRITICAL") color="${RED}" ;;
"ALERT") color="${YELLOW}" ;;
"INFO") color="${BLUE}" ;;
"SUCCESS") color="${GREEN}" ;;
"WARN") color="${MAGENTA}" ;;
esac
# Print colored output to terminal
echo -e "${NC}[$ts] [${color}${level}${NC}] $msg"
}
banner() {
local b=$(cat << "EOF"
██████╗ █████╗ ██████╗ ██╗██████╗ ███████╗
██╔══██╗██╔══██╗██╔══██╗██║██╔══██╗╚════██║
██████╔╝███████║██████╔╝██║██║ ██║ ██╔╝
██╔══██╗██╔══██║██╔═══╝ ██║██║ ██║ ██╔╝
██║ ██║██║ ██║██║ ██║██████╔╝ ██║
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝
M A L W A R E L A B S
==========================================================
Enhanced Linux BPFDoor Detection Script
==========================================================
EOF
)
echo -e "${ORANGE}${b}${NC}"
echo "$b" >> "$LOGFILE"
echo "Host : ${HOSTNAME}" | tee -a "$LOGFILE"
echo "Date : ${DATE}" | tee -a "$LOGFILE"
echo "Version: ${VERSION}" | tee -a "$LOGFILE"
echo "==========================================================" | tee -a "$LOGFILE"
}
require_root() {
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}[!] This script requires root privileges.${NC}"
exit 1
fi
}
cmd_exists() {
command -v "$1" >/dev/null 2>&1
}
mark_suspicious_file() {
local file="$1"
SUSPICIOUS_FILES_TMP+=("$file")
}
is_self() {
# Excludes the script itself from being flagged during execution
local p="$1"
if [ "$p" = "$SCRIPT_PID" ] || [ "$p" = "$SCRIPT_PPID" ]; then
return 0
fi
return 1
}
# ---- Helper: Hash check ---------------------------------------------------
hash_matches_malware() {
local file="$1"
if cmd_exists sha256sum; then
local h="$(sha256sum "$file" 2>/dev/null | awk '{print $1}')"
[ -n "${MALWARE_SHA256[$h]}" ] && return 0
fi
if cmd_exists md5sum; then
local h="$(md5sum "$file" 2>/dev/null | awk '{print $1}')"
[ -n "${MALWARE_MD5[$h]}" ] && return 0
fi
return 1
}
# ---- Helper: Strings check ------------------------------------------------
scan_strings_for_bpfdoor() {
local file="$1"
cmd_exists strings || return 1
local found=0
local file_strings="$(strings "$file" 2>/dev/null)"
for s in "${SUSPICIOUS_STRINGS[@]}"; do
if echo "$file_strings" | grep -Fq "$s"; then
log "ALERT" "String match '$s' found in $file"
found=1
fi
done
[ "$found" -eq 1 ] && return 0
return 1
}
# ---- Helper: Hexdump Mapped Memory for Magic Bytes ------------------------
check_maps_hex() {
local pid="$1"
is_self "$pid" && return
[ -r "/proc/$pid/maps" ] || return
cmd_exists hexdump || return
local maps_paths="$(awk '{ if ($6 ~ /^\//) print $6 }' "/proc/$pid/maps" 2>/dev/null | sort -u)"
# Combined 32-bit Little-Endian hex strings of all magic bytes from BPF filters
local magic_hex_regex="55720000|93520000|39393939|6c8d0000|4f9f0000|0f270000|48200000|55110000|33540000|82310000|51100000|21330000|adde0000"
for path in $maps_paths; do
[ -r "$path" ] || continue
local size="$(stat -c '%s' "$path" 2>/dev/null || echo 0)"
[ "$size" -gt 5242880 ] && continue
# Create a single continuous string of hex characters
local hexdata="$(hexdump -ve '1/1 "%02x"' "$path" 2>/dev/null)"
if echo "$hexdata" | grep -qiE "$magic_hex_regex"; then
log "CRITICAL" "Little-Endian Magic Bytes found in mapped file: $path (PID: $pid)"
mark_suspicious_file "$path"
# Extract the magic bytes along with ~16 bytes (32 hex characters) of context on either side
local context="$(echo "$hexdata" | grep -ioE ".{0,32}($magic_hex_regex).{0,32}" | head -n 3)"
if [ -n "$context" ]; then
echo "------- HEXDUMP CONTEXT -------" >> "$LOGFILE"
echo "$context" >> "$LOGFILE"
echo "-------------------------------" >> "$LOGFILE"
echo -e "${CYAN}------- HEXDUMP CONTEXT -------${NC}"
echo "$context" | grep --color=always -iE "$magic_hex_regex"
echo -e "${CYAN}-------------------------------${NC}"
fi
fi
done
}
# ---- Check 1: Mutex / lock files ------------------------------------------
check_mutex_files() {
log "INFO" "[1/12] Checking /var/run for suspicious zero-byte mutex/lock files"
local found=0
for f in /var/run/*.pid /var/run/*.lock; do
[ -e "$f" ] || continue
local size="$(stat -c '%s' "$f" 2>/dev/null || echo "")"
local perm="$(stat -c '%a' "$f" 2>/dev/null || echo "")"
if [ "$size" = "0" ] && [ "$perm" = "644" ]; then
for known in "${SUSPICIOUS_MUTEX_FILES[@]}"; do
if [ "$f" = "$known" ]; then
log "ALERT" "Suspicious mutex/lock file: $f (size=0, perm=644)"
found=1
fi
done
fi
done
[ "$found" -eq 0 ] && log "SUCCESS" "[1/12] No known suspicious mutex/lock files found"
}
# ---- Check 2: Auto-exec / sysconfig hooks ---------------------------------
check_autostart_files() {
log "INFO" "[2/12] Checking /etc/sysconfig for suspicious auto-start entries"
local dir="/etc/sysconfig"
local pat='\[[[:space:]]*-f[[:space:]]+/[^]]+\][[:space:]]*&&[[:space:]]*/'
if [ ! -d "$dir" ]; then
log "WARN" "[2/12] /etc/sysconfig not present; skipping"
return
fi
local results="$(find "$dir" -type f -exec grep -EH "$pat" {} + 2>/dev/null || true)"
if [ -z "$results" ]; then
log "SUCCESS" "[2/12] No suspicious auto-start patterns found in /etc/sysconfig"
return
fi
log "ALERT" "[2/12] Potential suspicious auto-start entries detected:"
echo "$results" | tee -a "$LOGFILE"
while IFS= read -r line; do
local filepath="$(echo "$line" | sed -nE 's/.*\[ *-f *([^ ]+).*/\1/p')"
if [ -n "$filepath" ] && [ -e "$filepath" ]; then
log "ALERT" "Auto-start target candidate: $filepath"
mark_suspicious_file "$filepath"
fi
done <<< "$results"
}
# ---- Check 3: BPF filter usage (old + new variants) -----------------------
check_bpf_filters() {
log "INFO" "[3/12] Inspecting BPF filters via ss -0pb"
if ! cmd_exists ss; then
log "WARN" "[3/12] ss command not available; skipping BPF filter check"
return
fi
local out="$(ss -0pb 2>/dev/null || true)"
if [ -z "$out" ]; then
log "INFO" "[3/12] No packet sockets reported by ss -0pb"
return
fi
echo "$out" >> "$LOGFILE"
local magic_regex='0x5293|21139|0x7255|29269|0x39393939|960051133|0x8D6C|36204|0x9F4F|40783|12674|13089|57005|4437|21555|4177|8264|0x270f|0x2048|0x1155|0x5433|0x3182|0x1051|0x3321|0xdead'
local matches="$(echo "$out" | grep -EB1 "$magic_regex" || true)"
if [ -n "$matches" ]; then
log "CRITICAL" "[3/12] BPFDoor magic pattern found in BPF filter output!"
echo "------- MATCH CONTEXT -------" >> "$LOGFILE"
echo "$matches" >> "$LOGFILE"
echo "-----------------------------" >> "$LOGFILE"
echo -e "${CYAN}------- MATCH CONTEXT -------${NC}"
echo "$matches" | grep --color=always -E "$magic_regex"
echo -e "${CYAN}-----------------------------${NC}"
local pids="$(echo "$matches" | sed -n 's/.*pid=\([0-9]\+\).*/\1/p' | sort -u)"
if [ -n "$pids" ]; then
for pid in $pids; do
is_self "$pid" && continue
[ -d "/proc/$pid" ] || continue
local exe="$(readlink -f "/proc/$pid/exe" 2>/dev/null || echo "")"
local cmd="$(ps -p "$pid" -o user=,pid=,cmd= 2>/dev/null || echo "")"
log "ALERT" " PID $pid -> $exe :: $cmd"
[ -n "$exe" ] && mark_suspicious_file "$exe"
check_maps_hex "$pid"
done
fi
else
log "SUCCESS" "[3/12] No obvious BPFDoor-like BPF filters found"
fi
}
# ---- Check 4: RAW + packet socket usage (Enhanced) ------------------------
check_raw_and_packet_sockets() {
log "INFO" "[4/12] Checking RAW and packet socket usage (SOCK_RAW / SOCK_DGRAM)"
local pids_found=""
local flagged=0
# 1. Collect PIDs from 'ss'
if cmd_exists ss; then
pids_found+="$(ss -0 -w -n -p 2>/dev/null | grep -oP 'pid=\K[0-9]+')"
fi
# 2. Collect PIDs from /proc/net forensics
# We combine all potential inodes first, then find their owners
local all_inodes=""
for netfile in /proc/net/packet /proc/net/raw /proc/net/raw6; do
if [ -r "$netfile" ]; then
all_inodes+=" $(awk 'NR>1 && $NF ~ /^[0-9]+$/ {print $NF}' "$netfile" 2>/dev/null)"
fi
done
# Process each unique inode found
for ino in $(echo "$all_inodes" | tr ' ' '\n' | sort -u); do
[ -z "$ino" ] && continue
[ "$ino" -eq 0 ] 2>/dev/null && continue
# Find PIDs for this inode and add to our list
local p=$(grep -rlE "ino(de)?:\s*$ino" /proc/[0-9]*/fdinfo 2>/dev/null | cut -d/ -f3)
pids_found+=" $p"
done
# 3. Analyze the collected PIDs
# Unique-sort the list of PIDs to avoid duplicate alerts
local unique_pids=$(echo "$pids_found" | tr ' ' '\n' | grep -E '^[0-9]+$' | sort -u)
for pid in $unique_pids; do
# Self-exclusion
[ "$pid" -eq "$SELF_PID" ] 2>/dev/null && continue
[ -d "/proc/$pid" ] || continue
local exe_path=$(readlink -f "/proc/$pid/exe" 2>/dev/null || echo "unknown")
# Skip if it's this detection script
[ "$exe_path" == "$SELF_EXE" ] && continue
local cmd_line=$(ps -p "$pid" -o args= 2>/dev/null | head -n1)
# If we got here, we found something
log "ALERT" "Suspicious Socket detected: PID $pid ($cmd_line) -> $exe_path"
if [[ -n "$exe_path" && "$exe_path" != "unknown" && -e "$exe_path" ]]; then
mark_suspicious_file "$exe_path"
fi
check_maps_hex "$pid"
flagged=1
done
# 4. Final Status (Only Success if NO pids were ever flagged)
if [ "$flagged" -eq 0 ]; then
log "SUCCESS" "No suspicious RAW/packet socket usage detected"
fi
}
# ---- Check 5: Env vars used by BPFDoor shells -----------------------------
check_env_vars() {
log "INFO" "[5/12] Checking for suspicious environment variables"
local hits=0
for pid_dir in /proc/[0-9]*; do
[ -r "$pid_dir/environ" ] || continue
local pid="${pid_dir##*/}"
is_self "$pid" && continue
local env="$(tr '\0' '\n' < "$pid_dir/environ" 2>/dev/null || true)"
[ -z "$env" ] && continue
if echo "$env" | grep -qx "HOME=/tmp" \
&& echo "$env" | grep -qx "HISTFILE=/dev/null" \
&& echo "$env" | grep -qx "MYSQL_HISTFILE=/dev/null"; then
local exe="$(readlink -f "/proc/$pid/exe" 2>/dev/null || echo "")"
local cmd="$(ps -p "$pid" -o user=,pid=,cmd= 2>/dev/null || echo "")"
local ppid="$(ps -p "$pid" -o ppid= 2>/dev/null | tr -d ' ' || echo "")"
log "CRITICAL" "[5/12] Process with BPFDoor-like env vars: PID=$pid, PPID=$ppid, EXE=$exe, CMD=$cmd"
[ -n "$exe" ] && mark_suspicious_file "$exe"
if [ -n "$ppid" ] && [ -e "/proc/$ppid/exe" ]; then
mark_suspicious_file "$(readlink -f "/proc/$ppid/exe" 2>/dev/null || true)"
fi
hits=$((hits+1))
fi
done
[ "$hits" -eq 0 ] && log "SUCCESS" "[5/12] No processes with the full suspicious env var set found"
}
# ---- Check 6: Ports historically used by BPFDoor --------------------------
check_suspicious_ports() {
log "INFO" "[6/12] Checking TCP ports ${SUSPICIOUS_PORTS_RANGE_START}-${SUSPICIOUS_PORTS_RANGE_END} and ${SUSPICIOUS_PORT_SINGLE}"
local net_out=""
if cmd_exists netstat; then
net_out="$(netstat -antp 2>/dev/null || true)"
elif cmd_exists ss; then
net_out="$(ss -antp 2>/dev/null || true)"
else
log "WARN" "[6/12] Neither netstat nor ss available; skipping port check"
return
fi
local matches=""
while IFS= read -r line; do
[[ "$line" =~ ^tcp ]] || continue
local laddr raddr state pidprog
read -r _ _ laddr raddr state pidprog <<<"$line"
local lport="${laddr##*:}"
local rport="${raddr##*:}"
[[ "$lport" =~ ^[0-9]+$ ]] || lport=0
[[ "$rport" =~ ^[0-9]+$ ]] || rport=0
if { [ "$lport" -ge "$SUSPICIOUS_PORTS_RANGE_START" ] && [ "$lport" -le "$SUSPICIOUS_PORTS_RANGE_END" ]; } \
|| { [ "$rport" -ge "$SUSPICIOUS_PORTS_RANGE_START" ] && [ "$rport" -le "$SUSPICIOUS_PORTS_RANGE_END" ]; } \
|| [ "$lport" -eq "$SUSPICIOUS_PORT_SINGLE" ] \
|| [ "$rport" -eq "$SUSPICIOUS_PORT_SINGLE" ]; then
matches+="$line"$'\n'
fi
done <<< "$net_out"
if [ -z "$matches" ]; then
log "SUCCESS" "[6/12] No connections on known suspicious BPFDoor ports"
return
fi
log "ALERT" "[6/12] Potentially suspicious connections on historical BPFDoor ports:"
printf "%s\n" "$matches" | tee -a "$LOGFILE"
local pids="$(printf "%s\n" "$matches" | awk '{print $7}' | cut -d/ -f1 | grep -E '^[0-9]+$' | sort -u)"
for pid in $pids; do
is_self "$pid" && continue
[ -d "/proc/$pid" ] || continue
local exe="$(readlink -f "/proc/$pid/exe" 2>/dev/null || echo "")"
[ -n "$exe" ] && mark_suspicious_file "$exe"
done
}
# ---- Check 7: Process masquerading (Whitelist Integration) ----------------
check_process_masquerade() {
log "INFO" "[7/12] Checking for masqueraded processes (Verifying true execution paths)"
local found=0
# Grab all processes and their arguments
local ps_output="$(ps -eo pid=,args= 2>/dev/null || true)"
[ -z "$ps_output" ] && return
while read -r pid args; do
[ -z "$pid" ] && continue
is_self "$pid" && continue
# 1. Identify if the process claims to be a suspect
local is_suspect=0
for pat in "${SUSPICIOUS_PROCS[@]}"; do
if echo "$args" | grep -Fq "$pat"; then
is_suspect=1
break
fi
done
# 2. Unmask and Verify the suspect against the Whitelist
if [ "$is_suspect" -eq 1 ]; then
local exe="$(readlink -f "/proc/$pid/exe" 2>/dev/null || echo "")"
# Skip if no executable path can be resolved (e.g. kernel threads)
[ -z "$exe" ] && continue
local is_whitelisted=0
for wl in "${WHITELIST_EXES[@]}"; do
if [ "$exe" = "$wl" ]; then
is_whitelisted=1
break
fi
done
# 3. Trap: It's a suspect name, but not running from a whitelisted binary file
if [ "$is_whitelisted" -eq 0 ]; then
log "CRITICAL" "[7/12] Process Masquerading Detected! PID=$pid claims to be '$args' but is actually executing '$exe'"
mark_suspicious_file "$exe"
found=1
fi
fi
done <<< "$ps_output"
[ "$found" -eq 0 ] && log "SUCCESS" "[7/12] No process masquerading detected"
}
# ---- Check 8: Memory-Resident / Deleted Binaries --------------------------
check_deleted_binaries() {
log "INFO" "[8/12] Checking for processes executing deleted binaries (Fileless execution)"
local found=0
local deleted_pids="$(ls -l /proc/*/exe 2>/dev/null | grep " (deleted)" | awk -F'/proc/' '{print $2}' | cut -d'/' -f1 || true)"
if [ -n "$deleted_pids" ]; then
for d_pid in $deleted_pids; do
is_self "$d_pid" && continue
[ -d "/proc/$d_pid" ] || continue
local d_name="$(ps -p "$d_pid" -o comm= 2>/dev/null || echo "")"
local d_exe="$(readlink -f "/proc/$d_pid/exe" 2>/dev/null || echo "")"
local is_bpf_candidate=0
for pat in "${SUSPICIOUS_PROCS[@]}"; do
if [[ "$pat" == *"$d_name"* ]]; then
is_bpf_candidate=1
break
fi
done
if [ "$is_bpf_candidate" -eq 1 ]; then
log "CRITICAL" "PID: $d_pid masquerading as '$d_name' running from a deleted file: $d_exe"
mark_suspicious_file "$d_exe"
found=1
else
log "WARN" "PID: $d_pid, ProcessName: $d_name, Exec: $d_exe (Deleted Binary)"
fi
done
fi
[ "$found" -eq 0 ] && log "SUCCESS" "[8/12] No critical memory-resident/deleted binary execution found"
}
# ---- Check 9: Kernel Stack Tracing ----------------------------------------
check_kernel_stack() {
log "INFO" "[9/12] Checking kernel stacks for raw socket blocking (packet_recvmsg/wait_for_more_packets)"
local found=0
local pids="$(grep -lE "packet_recvmsg|wait_for_more_packets" /proc/*/stack 2>/dev/null | awk -F/ '{print $3}' || true)"
for pid in $pids; do
is_self "$pid" && continue
[ -d "/proc/$pid" ] || continue
local exe="$(readlink -f "/proc/$pid/exe" 2>/dev/null || echo "")"
local cmd="$(ps -p "$pid" -o user=,pid=,cmd= 2>/dev/null || echo "")"
log "CRITICAL" "Process hanging on packet_recvmsg: PID=$pid EXE=$exe CMD=$cmd"
[ -n "$exe" ] && mark_suspicious_file "$exe"
found=1
check_maps_hex "$pid"
done
[ "$found" -eq 0 ] && log "SUCCESS" "[9/12] No processes blocking on suspicious packet socket kernel functions"
}
# ---- Check 10: Deep scan suspicious files (hash, strings, UPX packer) -----
deep_scan_suspicious_files() {
log "INFO" "[10/12] Deep scanning candidate binaries (hash, strings, UPX packing)"
local uniq_files=($(printf "%s\n" "${SUSPICIOUS_FILES_TMP[@]}" | sort -u))
if [ "${#uniq_files[@]}" -eq 0 ]; then
log "INFO" "[10/12] No candidate binaries collected for deep scan"
return
fi
for f in "${uniq_files[@]}"; do
[ -e "$f" ] || continue
log "INFO" ">>> Analyzing candidate binary: $f"
if hash_matches_malware "$f"; then
log "CRITICAL" "Known BPFDoor hash match for $f"
continue
fi
if cmd_exists dd; then
local dd_out="$(dd if="$f" bs=1 count=256 2>/dev/null | grep -o 'UPX!' || true)"
if [ -n "$dd_out" ]; then
log "ALERT" "Binary is UPX packed (Common for BPFdoor): $f"
fi
fi
if scan_strings_for_bpfdoor "$f"; then
log "CRITICAL" "BPFDoor-like string pattern(s) found in $f"
fi
done
}
# ---- Check 11: C2 Connections (DNS Resolving & SS Tracking) ---------------
check_c2_connections() {
log "INFO" "[11/12] Checking for active connections to known BPFDoor C2 domains"
local found=0
if ! cmd_exists dig || ! cmd_exists ss; then
log "WARN" "[11/12] 'dig' or 'ss' missing; skipping C2 connection checks."
return
fi
for host in "${KNOWN_C2_HOSTS[@]}"; do
local ips="$(dig +short "$host" A "$host" AAAA 2>/dev/null || true)"
for ip in $ips; do
if [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || [[ "$ip" =~ ^[0-9a-fA-F:]+$ && "$ip" =~ .*[:].* ]]; then
local ss_output="$(ss -tnp state established dst "$ip" 2>/dev/null || true)"
if [ -n "$ss_output" ]; then
log "CRITICAL" "Active Reverse Shell to BPFDoor C2: $host ($ip)"
echo "$ss_output" | tee -a "$LOGFILE"
local c2_pids="$(echo "$ss_output" | grep -oP 'pid=\K[0-9]+' | sort -u)"
for c2pid in $c2_pids; do
is_self "$c2pid" && continue
[ -d "/proc/$c2pid" ] || continue
local exe="$(readlink -f "/proc/$c2pid/exe" 2>/dev/null || echo "")"
[ -n "$exe" ] && mark_suspicious_file "$exe"
done
found=1
fi
fi
done
done
[ "$found" -eq 0 ] && log "SUCCESS" "[11/12] No active connections to known C2 domains found"
}
# ---- Check 12: Process-Specific Signatures --------------------------------
check_process_signatures() {
log "INFO" "[12/12] Checking specific processes for hardcoded BPFDoor file signatures"
local found=0
local sig_checks=(
"sshd::66666666H"
"abrtd|atd|pickup:::h:d:l:s:b:t"
"sgaSolAgent|cmathreshd|udevd|agetty|hpasmlited|\.sshd::ttcompat::127.0.0.1"
)
for check in "${sig_checks[@]}"; do
local proc_pat="${check%%::*}"
local remainder="${check#*::}"
local sig1="${remainder%%::*}"
local sig2="${remainder#*::}"
local pids="$(pgrep -E "$proc_pat" 2>/dev/null || true)"
for pid in $pids; do
is_self "$pid" && continue
[ -d "/proc/$pid" ] || continue
local path="$(readlink -f "/proc/$pid/exe" 2>/dev/null || echo "")"
[ -z "$path" ] && continue
if grep -a -q "$sig1" "$path" 2>/dev/null; then
if [ -n "$sig2" ] && [ "$sig1" != "$sig2" ]; then
if ! grep -a -q "$sig2" "$path" 2>/dev/null; then
continue
fi
fi
local cmdline="$(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null || echo "")"
log "CRITICAL" "Process Signature Match: PID=$pid, Path=$path, Cmd=$cmdline matched pattern '$proc_pat'"
mark_suspicious_file "$path"
found=1
fi
done
done
[ "$found" -eq 0 ] && log "SUCCESS" "[12/12] No hardcoded process signatures detected"
}
# ---- Optional: Basic persistence checks -----------------------------------
check_persistence() {
log "INFO" "[-] Basic persistence triage (cron, systemd, rc scripts)"
for file in /etc/crontab /var/spool/cron/* /var/spool/cron/crontabs/*; do
[ -f "$file" ] || continue
if grep -E "bpf|dbus-srv|hpasmmld|smartadm|hald-addon-volume" "$file" 2>/dev/null | grep -q .; then
log "ALERT" "Suspicious entry in cron file: $file"
grep -E "bpf|dbus-srv|hpasmmld|smartadm|hald-addon-volume" "$file" 2>/dev/null | tee -a "$LOGFILE"
fi
done
for dir in /etc/systemd/system /usr/lib/systemd/system /run/systemd/system; do
[ -d "$dir" ] || continue
if grep -rE "bpf|dbus-srv|hpasmmld|smartadm|hald-addon-volume" "$dir" 2>/dev/null | grep -q .; then
log "ALERT" "Suspicious pattern in systemd units under $dir"
fi
done
for rc in /etc/rc.local /etc/init.d; do
[ -e "$rc" ] || continue
if grep -rE "bpf|dbus-srv|hpasmmld|smartadm|hald-addon-volume" "$rc" 2>/dev/null | grep -q .; then
log "ALERT" "Suspicious pattern in rc script(s) under $rc"
fi
done
}
# ---- Main ------------------------------------------------------------------
main() {
: > "$LOGFILE"
require_root
banner
echo -e "\n${CYAN}[*] Running Ultimate BPFDoor triage…${NC}"
check_mutex_files
check_autostart_files
check_bpf_filters
check_raw_and_packet_sockets
check_env_vars
check_suspicious_ports
check_process_masquerade
check_deleted_binaries
check_kernel_stack
check_c2_connections
check_process_signatures
deep_scan_suspicious_files
check_persistence
echo
echo -e "${CYAN}[*] Scan complete. Report written to: ${LOGFILE}${NC}"
echo -e "${YELLOW}[!] Any CRITICAL or ALERT entries should be investigated promptly.${NC}"
}
main "$@"