Commit b0862410 authored by 易初's avatar 易初 🖐🏻

Update server_incident_scan_v3.sh

parent 1fa6e150
......@@ -12,7 +12,7 @@ set -u
umask 077
export LC_ALL=C
VERSION="3.0.0"
VERSION="3.1.0"
SCAN_START="${SCAN_START:-2026-09-11 00:00:00}"
SCAN_END="${SCAN_END:-2026-09-12 06:00:00}"
BASE_OUT="/var/tmp"
......@@ -77,6 +77,7 @@ EVENTS="$OUT_DIR/chain_events.tsv"
PROCESSES="$OUT_DIR/processes.tsv"
CONNECTIONS="$OUT_DIR/public_remote_connections.tsv"
SSH_SUCCESS="$OUT_DIR/ssh_success.log"
SSH_159_CONTEXT="$OUT_DIR/ssh_from_159_context.log"
FILES="$OUT_DIR/recent_files.tsv"
SCRIPTS="$OUT_DIR/cloud_assist_scripts.tsv"
ENDPOINTS="$OUT_DIR/candidate_endpoints.txt"
......@@ -87,6 +88,7 @@ mkdir -p "$ARTIFACTS/processes" "$ARTIFACTS/cloud-assist-scripts" \
: > "$RAW"
: > "$SSH_SUCCESS"
: > "$SSH_159_CONTEXT"
printf 'host\tseverity\tstage\tcategory\tevidence\n' > "$FINDINGS"
printf 'host\ttime\tstage\tsource\tindicator\tevidence\n' > "$EVENTS"
printf 'host\tpid\tppid\tuser\tstart\tcomm\texe\tsha256\tcmdline\n' > "$PROCESSES"
......@@ -100,9 +102,12 @@ BAD_KEY_SHA256='SHA256:/6Ll/VlsnRWorwg3IpUSNSosKJxNNA6znrR+QTXQRaE'
CURSOR_KEY_FRAGMENT='AAAAB3NzaC1yc2EAAAADAQABAAABAQCF8tdOgKz0byyI/1wsvYH7/dAH71F7QDr4W7PQu8aCH8'
PIVOT_KEY_FRAGMENT='AAAAB3NzaC1yc2EAAAADAQABAAABAQC4evWpxa5XJ8KiL1+E32x1MB7FlijaDTHVhBOwL7xhr0GvZGLj'
KNOWN_AK_ID='LTAI4FjyvUTRZZN4QyHoXQag'
KNOWN_LATERAL_AUTH_MD5='4b:f9:2d:16:60:1e:5b:5e:68:c3:50:89:27:d9:ff:59'
KNOWN_WWW_OUTBOUND_MD5='bf:e7:3a:be:59:e6:d5:e4:3e:f1:b2:a5:aa:78:41:8a'
IOC_TEXT_REGEX='101\.201\.148\.142|8\.217\.173\.211|hdocf\.com|ddocf\.com|watch_dog_auth|hpgoc2([.-]|\.oss\.v2\.core)|cursor-agent-010|aliyun-sys-assist|AliyunSysAssist|/tmp/dbg([^A-Za-z0-9_.-]|$)|libnuma_hint\.so|url_key=hpgoc2|LTAI4FjyvUTRZZN4QyHoXQag'
IOC_EXEC_REGEX='AuthorizedKeysCommand|authorized_keys|unset[[:space:]]+HISTFILE|rpm[[:space:]]+-Uvh|/tmp/sys\.rpm|/tmp/elf\.rpm|/tmp/kd\.rpm|dbg-el6|aliyun-sys-assist'
IOC_IP_REGEX='101\.201\.148\.142|8\.217\.173\.211|47\.237\.187\.64|39\.108\.93\.196'
IOC_TEXT_REGEX='101\.201\.148\.142|8\.217\.173\.211|47\.237\.187\.64|39\.108\.93\.196|hdocf\.com|ddocf\.com|watch_dog_auth|top-sec\.oss-cn-beijing\.aliyuncs\.com|cache-node-02\.oss-cn-hongkong\.aliyuncs\.com|hpgoc2([.-]|\.oss\.v2\.core)|cursor-agent-010|aliyun-sys-assist|AliyunSysAssist|/(tmp|var/tmp|var/opt|usr/local/sbin)/dbg([^A-Za-z0-9_.-]|$)|libnuma_hint\.so|url_key=hpgoc2|LTAI4FjyvUTRZZN4QyHoXQag'
IOC_EXEC_REGEX='AuthorizedKeysCommand|PasswordAuthentication[[:space:]]+yes|PermitRootLogin[[:space:]]+yes|authorized_keys|unset[[:space:]]+HISTFILE|history[[:space:]]+-c|rpm[[:space:]]+--rebuilddb|rpm[[:space:]]+-Uvh|/tmp/sys\.rpm|/tmp/elf\.rpm|/tmp/kd\.rpm|dbg-el6|/usr/local/sbin/dbg|aliyun-sys-assist'
sanitize() {
local _value
......@@ -151,6 +156,17 @@ hash_value() {
fi
}
key_fingerprints() {
local _key_path
_key_path="$1"
have ssh-keygen || return 0
{
ssh-keygen -lf "$_key_path" 2>/dev/null || true
ssh-keygen -E sha256 -lf "$_key_path" 2>/dev/null || true
ssh-keygen -E md5 -lf "$_key_path" 2>/dev/null || true
} | sort -u
}
hash_label() {
local _digest
_digest="$1"
......@@ -165,6 +181,12 @@ hash_label() {
echo "malicious libnuma_hint.so" ;;
8ca077298ca5a46655be209bd993268e5e224f0bc10a079825e023e036d342fe)
echo "malicious AliyunSysAssist" ;;
0e1bd634d5fc8f4df1c91f0072a4556eb11f41ecc95b05f5a2ffb0e0b914613d)
echo "malicious AliyunSysAssist variant" ;;
0eb8af527d40c3a1e27293be935504b6201db991fed028c6e4b805dca7f9f15d)
echo "malicious aliyun-sys-assist setup variant" ;;
ca3c6256354a8053812c85feff0c540b6e2feddfc8fc7804ef2637628e9426e8)
echo "malicious usb_portctl helper" ;;
f5403e362abf1ac6a4d3628f305110c6f1aa8d1ddea0922b3757ba3281ff7bc3)
echo "malicious aliyun-sys-assist core.so" ;;
b174e5a7debf48004811e58be69cfaad607af379c3dcbf268772f84be88a2b8f)
......@@ -212,6 +234,9 @@ file_evidence() {
_label="$(hash_label "$_digest")"
if test -n "$_label" \
|| test "$_path" = /usr/sbin/aliyun-sys-assist-setup \
|| test "$_path" = /usr/local/sbin/dbg \
|| test "$_path" = /var/tmp/dbg \
|| test "$_path" = /var/opt/dbg \
|| printf '%s' "$_path" | grep -q '^/usr/local/share/aliyun-sys-assist/' \
|| printf '%s' "$_path" | grep -q '^/usr/lib/aliyun-sys-assist-payload/' \
|| { test "$_path" = /usr/sbin/irqbalance && test "${IRQ_RPM_BAD:-0}" -eq 1; }; then
......@@ -318,8 +343,14 @@ if have rpm; then
_build_time="$(date -d "@$_build_epoch" '+%F %T %z' 2>/dev/null || echo "$_build_epoch")"
finding HIGH execution prerequisite "Build prerequisite installed during incident window: $_build_pkg at $_build_time"
event "$_build_time" execution rpm build-prerequisite "$_build_pkg"
_build_verify="$(rpm -V "$_build_pkg" 2>&1 || true)"
printf 'package=%s install_time=%s\n%s\n' "$_build_pkg" "$_build_time" "$_build_verify" \
>> "$ARTIFACTS/package-metadata/incident-build-packages.txt"
fi
done
echo "-- RPM database metadata --" >> "$RAW"
stat /var/lib/rpm /var/lib/rpm/* >> "$RAW" 2>&1 || true
fi
section "known and campaign-related files"
......@@ -336,9 +367,29 @@ for _path in \
/usr/local/share/aliyun-sys-assist/.epcache \
/usr/local/share/aliyun-sys-assist/.helper.lock \
/usr/local/share/aliyun-sys-assist/.helper.alive \
/tmp/dbg /tmp/dbg-el6 /tmp/sys.rpm /tmp/elf.rpm /tmp/kd.rpm \
/tmp/dbg /tmp/dbg-el6 /var/tmp/dbg /var/opt/dbg /usr/local/sbin/dbg \
/tmp/sys.rpm /tmp/elf.rpm /tmp/kd.rpm \
/tmp/v2_rpm.log /tmp/v2_rpm.exit; do
file_evidence "$_path" campaign-target
case "$_path" in
/usr/local/sbin/dbg|/var/tmp/dbg|/var/opt/dbg)
if test -e "$_path" || test -L "$_path"; then
finding CRITICAL execution file "Confirmed campaign dbg path exists: $_path sha256=$(hash_value "$_path")"
fi ;;
esac
done
section "campaign temporary and cleanup artifacts"
for _path in \
/tmp/.irq-stats-* /dev/shm/.hpgoc2_skag_* /dev/shm/.ov2* \
/tmp/CVE-2021-4034* /tmp/.pwn* /tmp/.k42* /var/opt/.k42* \
/tmp/.fs_i.sh /var/tmp/.fs_i.sh /tmp/.ht_* /var/tmp/.ht_* \
/tmp/getsshpwd /tmp/.getsshpwd /var/opt/getsshpwd \
/tmp/ssh4.zip /tmp/c.tar.gz /tmp/.chlp*; do
test -e "$_path" || test -L "$_path" || continue
file_evidence "$_path" campaign-cleanup-target
finding HIGH defense_evasion artifact "Campaign cleanup/staging artifact remains: $_path"
done
section "static indicators extracted from campaign binaries"
......@@ -349,7 +400,7 @@ for _binary in \
/usr/lib/aliyun-sys-assist-payload/core.so \
/usr/local/share/aliyun-sys-assist/AliyunSysAssist \
/usr/lib64/libnuma_hint.so /usr/lib/libnuma_hint.so \
/tmp/dbg /tmp/dbg-el6; do
/tmp/dbg /tmp/dbg-el6 /var/tmp/dbg /var/opt/dbg /usr/local/sbin/dbg; do
test -r "$_binary" || continue
_binary_hash="$(hash_value "$_binary")"
_binary_label="$(hash_label "$_binary_hash")"
......@@ -358,7 +409,7 @@ for _binary in \
/usr/sbin/irqbalance)
test "${IRQ_RPM_BAD:-0}" -eq 1 || continue
_binary_label="altered irqbalance candidate" ;;
/usr/sbin/aliyun-sys-assist-setup|/usr/lib/aliyun-sys-assist-payload/*|/usr/local/share/aliyun-sys-assist/*|/tmp/dbg|/tmp/dbg-el6)
/usr/sbin/aliyun-sys-assist-setup|/usr/lib/aliyun-sys-assist-payload/*|/usr/local/share/aliyun-sys-assist/*|/tmp/dbg|/tmp/dbg-el6|/var/tmp/dbg|/var/opt/dbg|/usr/local/sbin/dbg)
_binary_label="campaign-path candidate" ;;
*) continue ;;
esac
......@@ -390,6 +441,24 @@ if test -d /usr/local/share/aliyun-sys-assist; then
fi
fi
is_expected_initramfs_plymouth() {
local _proc_path _exe_path _comm_name _cmdline _parent
_proc_path="$1"
_exe_path="$2"
_comm_name="$3"
_cmdline="$4"
_parent="$5"
test "$_comm_name" = plymouthd || return 1
case "$_exe_path" in
'/bin/plymouthd (deleted)'|'/sbin/plymouthd (deleted)') ;;
*) return 1 ;;
esac
test "$_parent" = 1 || return 1
printf '%s' "$_cmdline" | grep -q -- '--attach-to-session' || return 1
grep -Eq '[[:space:]]00:01[[:space:]].*plymouth.*\(deleted\)' "$_proc_path/maps" 2>/dev/null || return 1
return 0
}
section "running process correlation"
MATCHED_PIDS=""
CAMPAIGN_PIDS=""
......@@ -400,15 +469,19 @@ for _proc in /proc/[0-9]*; do
_exe="$(readlink "$_proc/exe" 2>/dev/null || true)"
_comm="$(cat "$_proc/comm" 2>/dev/null || true)"
_cmd="$(tr '\0' ' ' < "$_proc/cmdline" 2>/dev/null || true)"
_maps_hit="$(grep -E 'libnuma_hint\.so|aliyun-sys-assist|/tmp/dbg' "$_proc/maps" 2>/dev/null | head -20 || true)"
_maps_hit="$(grep -E 'libnuma_hint\.so|aliyun-sys-assist|/(tmp|var/tmp|var/opt|usr/local/sbin)/dbg' "$_proc/maps" 2>/dev/null | head -20 || true)"
_digest=""
test -r "$_proc/exe" && _digest="$(hash_value "$_proc/exe")"
_label="$(hash_label "$_digest")"
_ppid="$(awk '/^PPid:/ {print $2}' "$_proc/status" 2>/dev/null || true)"
_expected_initramfs_plymouth=0
is_expected_initramfs_plymouth "$_proc" "$_exe" "$_comm" "$_cmd" "$_ppid" \
&& _expected_initramfs_plymouth=1
_match=0
_campaign_match=0
case "$_comm $_exe $_cmd $_maps_hit $_label" in
*irqbalance*|*AliyunSysAssist*|*aliyun-sys-assist*|*"/tmp/dbg"*|*libnuma_hint*|*"dbg payload"*|*"malicious "*)
*irqbalance*|*AliyunSysAssist*|*aliyun-sys-assist*|*"/tmp/dbg"*|*"/var/tmp/dbg"*|*"/var/opt/dbg"*|*"/usr/local/sbin/dbg"*|*libnuma_hint*|*"dbg payload"*|*"malicious "*)
_match=1; _campaign_match=1 ;;
*"(deleted)"*) _match=1 ;;
rngd*) _match=1 ;;
......@@ -418,7 +491,6 @@ for _proc in /proc/[0-9]*; do
MATCHED_PIDS="$MATCHED_PIDS $_pid"
test "$_campaign_match" -eq 1 && CAMPAIGN_PIDS="$CAMPAIGN_PIDS $_pid"
case "$_comm $_cmd" in *irqbalance*) IRQ_PIDS="$IRQ_PIDS $_pid" ;; esac
_ppid="$(awk '/^PPid:/ {print $2}' "$_proc/status" 2>/dev/null || true)"
_user="$(ps -p "$_pid" -o user= 2>/dev/null | awk '{print $1}')"
_start="$(ps -p "$_pid" -o lstart= 2>/dev/null | sed 's/^[[:space:]]*//')"
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
......@@ -454,10 +526,16 @@ for _proc in /proc/[0-9]*; do
finding CRITICAL execution process "PID $_pid runs $_label; comm=$_comm exe=$_exe"
fi
case "$_exe" in
/tmp/dbg|/tmp/dbg-el6|"/tmp/dbg (deleted)"|"/tmp/dbg-el6 (deleted)")
/tmp/dbg|/tmp/dbg-el6|/var/tmp/dbg|/var/opt/dbg|/usr/local/sbin/dbg|\
"/tmp/dbg (deleted)"|"/tmp/dbg-el6 (deleted)"|"/var/tmp/dbg (deleted)"|\
"/var/opt/dbg (deleted)"|"/usr/local/sbin/dbg (deleted)")
finding CRITICAL execution process "PID $_pid runs campaign payload $_exe; comm=$_comm" ;;
*"(deleted)"*)
finding HIGH defense_evasion process "PID $_pid runs deleted executable $_exe; comm=$_comm" ;;
if test "$_expected_initramfs_plymouth" -eq 1; then
finding INFO execution process "PID $_pid is expected initramfs plymouth residue: $_exe; ppid=$_ppid cmd=$_cmd"
else
finding HIGH defense_evasion process "PID $_pid runs deleted executable $_exe; comm=$_comm"
fi ;;
esac
case "$_comm $_exe $_cmd" in
*AliyunSysAssist*|*aliyun-sys-assist*)
......@@ -493,8 +571,8 @@ if printf '%s\n' "$UNIX_ALL" | grep -Fq 'hpgoc2.oss.v2.core'; then
event "$(date '+%F %T %z')" command_control socket hpgoc2.oss.v2.core "$_sock_hit"
fi
if printf '%s\n' "$NET_ALL" | grep -Eq '101\.201\.148\.142|8\.217\.173\.211'; then
_net_hit="$(printf '%s\n' "$NET_ALL" | grep -E '101\.201\.148\.142|8\.217\.173\.211' | head -10)"
if printf '%s\n' "$NET_ALL" | grep -Eq "$IOC_IP_REGEX"; then
_net_hit="$(printf '%s\n' "$NET_ALL" | grep -E "$IOC_IP_REGEX" | head -10)"
finding CRITICAL command_control network "Active connection to known campaign IP: $_net_hit"
event "$(date '+%F %T %z')" command_control network known-c2 "$_net_hit"
fi
......@@ -550,8 +628,22 @@ for _ssh_dir in /root/.ssh /home/*/.ssh; do
stat "$_key_file" >> "$RAW" 2>&1
_key_copy="$(printf '%s' "$_key_file" | sed 's#/#_#g')"
copy_artifact "$_key_file" "$ARTIFACTS/authorized-keys/${_key_copy}"
_key_info="$(ssh-keygen -E sha256 -lf "$_key_file" 2>/dev/null || ssh-keygen -lf "$_key_file" 2>/dev/null || true)"
_key_info="$(key_fingerprints "$_key_file")"
printf '%s\n' "$_key_info" >> "$RAW"
_key_line_no=0
while IFS= read -r _key_line; do
_key_line_no=$((_key_line_no + 1))
case "$_key_line" in ''|'#'*) continue ;; esac
_key_temp="$OUT_DIR/.authorized-key-$$-${_key_line_no}"
printf '%s\n' "$_key_line" > "$_key_temp"
_line_info="$(key_fingerprints "$_key_temp")"
rm -f "$_key_temp"
printf 'authorized_key_file=%s line=%s fingerprint=%s\n' \
"$_key_file" "$_key_line_no" "$_line_info" >> "$RAW"
if printf '%s' "$_line_info" | grep -Fqi "$KNOWN_LATERAL_AUTH_MD5"; then
finding HIGH lateral_movement ssh_key "Known historical lateral-trust key is authorized in $_key_file line $_key_line_no: $_line_info"
fi
done < "$_key_file"
if printf '%s\n' "$_key_info" | grep -Fq "$BAD_KEY_SHA256" \
|| grep -Fq "$PIVOT_KEY_FRAGMENT" "$_key_file" 2>/dev/null; then
finding CRITICAL persistence ssh_key "Known 159-generated attacker key is authorized in $_key_file"
......@@ -571,6 +663,14 @@ for _ssh_dir in /root/.ssh /home/*/.ssh; do
_key_hash="$(hash_value "$_private_key")"
echo "private_key=$_private_key mtime_epoch=$_key_mtime sha256=$_key_hash" >> "$RAW"
stat "$_private_key" >> "$RAW" 2>&1
_public_key="${_private_key}.pub"
if test -r "$_public_key"; then
_public_info="$(key_fingerprints "$_public_key")"
echo "private_key_public_fingerprint=$_private_key $_public_info" >> "$RAW"
if printf '%s' "$_public_info" | grep -Fqi "$KNOWN_WWW_OUTBOUND_MD5"; then
finding HIGH credential_access ssh_private_key "Known campaign-exposed www outbound key is present: $_private_key fingerprint=$_public_info"
fi
fi
if test "$_key_mtime" -ge "$START_EPOCH" 2>/dev/null \
&& test "$_key_mtime" -le "$END_EPOCH" 2>/dev/null; then
_key_time="$(date -d "@$_key_mtime" '+%F %T %z' 2>/dev/null || echo "$_key_mtime")"
......@@ -580,6 +680,26 @@ for _ssh_dir in /root/.ssh /home/*/.ssh; do
done
done
section "effective SSH authentication policy"
SSH_EFFECTIVE="$ARTIFACTS/ssh-effective-config.txt"
if have sshd; then
sshd -T > "$SSH_EFFECTIVE" 2>&1 || true
grep -Ei '^(permitrootlogin|passwordauthentication|pubkeyauthentication|authorizedkeyscommand|authorizedkeysfile)' \
"$SSH_EFFECTIVE" >> "$RAW" 2>/dev/null || true
fi
grep -nE '^[[:space:]]*#?[[:space:]]*(PermitRootLogin|PasswordAuthentication|PubkeyAuthentication|AuthorizedKeysCommand|AuthorizedKeysCommandUser|AuthorizedKeysCommandRunAs)' \
/etc/ssh/sshd_config >> "$RAW" 2>/dev/null || true
_sshd_mtime="$(stat -c %Y /etc/ssh/sshd_config 2>/dev/null || echo 0)"
_risky_ssh="$(grep -Ei '^(permitrootlogin[[:space:]]+yes|passwordauthentication[[:space:]]+yes)' "$SSH_EFFECTIVE" 2>/dev/null | tr '\n' ' ')"
if test -n "$_risky_ssh"; then
if test "$_sshd_mtime" -ge "$START_EPOCH" 2>/dev/null \
&& test "$_sshd_mtime" -le "$END_EPOCH" 2>/dev/null; then
finding HIGH persistence ssh_policy "Risky SSH policy is effective and sshd_config changed during incident window: $_risky_ssh"
else
finding INFO discovery ssh_policy "Risky effective SSH policy requires manual review: $_risky_ssh"
fi
fi
section "SSH authentication timeline"
if ls /var/log/secure* >/dev/null 2>&1; then
zgrep -hE 'sshd.*Accepted (publickey|password)' /var/log/secure* 2>/dev/null \
......@@ -588,7 +708,13 @@ if ls /var/log/secure* >/dev/null 2>&1; then
_from_159="$(grep -E 'Accepted (publickey|password).* from 172\.18\.172\.159 ' "$SSH_SUCCESS" || true)"
if test -n "$_from_159"; then
finding CRITICAL lateral_movement ssh "Successful SSH authentication from compromised pivot 172.18.172.159: $(printf '%s\n' "$_from_159" | head -8)"
if test -f /home/www/.ssh/id_rsa; then
finding HIGH credential_access ssh_private_key "Host accepted SSH from compromised pivot and also contains outbound /home/www/.ssh/id_rsa; treat the downstream trust chain as exposed"
fi
fi
zgrep -hF '172.18.172.159' /var/log/secure* 2>/dev/null \
| grep -E '^Sep[[:space:]]+(10|11|12)[[:space:]].*sshd' > "$SSH_159_CONTEXT" || true
cat "$SSH_159_CONTEXT" >> "$RAW"
_bad_fp_log="$(grep -F "$BAD_KEY_SHA256" "$SSH_SUCCESS" || true)"
if test -n "$_bad_fp_log"; then
finding CRITICAL lateral_movement ssh "Known attacker key fingerprint used successfully: $(printf '%s\n' "$_bad_fp_log" | head -8)"
......@@ -741,7 +867,7 @@ record_recent() {
"$HOST_NAME" "$_time_type" "$(sanitize "$_recent_time")" "$_mode" "$_owner" \
"$_size" "$_recent_hash" "$(sanitize "$_recent_path")" >> "$FILES"
case "$_recent_path" in
*/irqbalance|*aliyun-sys-assist*|*libnuma_hint.so|*/tmp/dbg|*/tmp/dbg-el6|*/authorized_keys|*/id_rsa|*/id_rsa.pub)
*/irqbalance|*aliyun-sys-assist*|*libnuma_hint.so|*/tmp/dbg|*/tmp/dbg-el6|*/var/tmp/dbg|*/var/opt/dbg|*/usr/local/sbin/dbg|*/authorized_keys|*/id_rsa|*/id_rsa.pub|*/tmp/elf.rpm|*/tmp/kd.rpm)
event "$_recent_time" defense_evasion filesystem "${_time_type}-in-window" "$_recent_path sha256=$_recent_hash"
;;
esac
......@@ -838,6 +964,7 @@ chain_events.tsv Mergeable host timeline
processes.tsv Matched process inventory and hashes
public_remote_connections.tsv Established public peers for manual review
ssh_success.log Successful SSH/session entries around incident
ssh_from_159_context.log SSH context lines involving the compromised pivot
recent_files.tsv ctime/mtime incident-window file inventory
cloud_assist_scripts.tsv Cloud Assistant command-script inventory
candidate_endpoints.txt Strings-derived endpoint candidates from known malware
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment