VPS优惠推送
分享好玩教程

服务商发来预警(PHP2Shell),我中招了?我把 VPS 从头查了一遍(附 8 条自查命令)

本文于 2026-10-22 17:04 更新,部分内容具有时效性,如有失效,请留言

昨天收到服务商的一封邮件,说最近有大量机器被 PHP2Shell 打进来,附了一段被注入的 crontab:

@reboot # fontconfig-uuid-cache /var/cache/fontconfig/.uuid-cache/fc-uuid # rtkit-prio-cache
@reboot systemctl start rtkit-prio-cache.service 2>/dev/null || exec /var/lib/rtkit/.prio-cache/rtkitctl
# unix-service-upd
@reboot systemctl start unix-service-upd.service 2>/dev/null || exec /var/lib/unix-service/.upd-cache/usvc-updated

看着像什么系统服务在开机自启。实际上,这是一套后门。

我照着邮件里给的特征,把自己这台 VPS 从头查了一遍。下面这份清单可以直接抄去用,命令都能跑。

先说这段 crontab 阴在哪


三行拆开看:

  • 行尾用 # 接一段"注释" —— 伪装成被注释掉的旧配置,扫一眼很容易跳过;
  • systemctl start 一个不存在的服务 —— 比如 rtkit-prio-cache.service,你系统里根本没有这个 unit;
  • 核心在 || exec —— 服务启动失败(必然失败),就 直接执行后面的二进制。所以它不依赖任何服务真的存在。

@reboot 意味着每次重启都会重新拉起。服务器一重启,后门自动复活。

八条自查命令

先说一句:命令本身不告诉你答案。所以每条后面我都写了「正常长什么样」和「异常长什么样」——你对着自己的屏幕看就行。

另外,看到 没有输出 别以为是命令没跑 —— grep / find 没找到东西就是不输出,这通常是好事。

1. 所有 crontab(用户级 + 系统级)

crontab -l
cat /etc/crontab
ls -la /etc/cron.d/ && cat /etc/cron.d/*
for f in /var/spool/cron/crontabs/*; do echo "== $f"; cat "$f"; done

正常:每一行你都能认出来。比如 php2shell-autopatch、webshell-scan、wp-cron 这种自己加的任务。

异常:出现 @reboot;或者行尾挂着一段看着像注释的 # xxx;或者命令里出现 /var/lib/、/var/cache/ 下的隐藏目录。

2. 按 IOC 关键词全局搜

grep -rIn -E 'fontconfig-uuid-cache|rtkit-prio-cache|unix-service-upd|uuid-cache|prio-cache|upd-cache' 
  /etc /var/spool /root /tmp /dev/shm /opt 2>/dev/null

正常:什么都不输出,直接回到提示符。

异常:刷出几行「文件路径:行号:内容」。先别删 —— 把路径记下来,确认是不是自己的东西。

3. 假服务在不在

ls /etc/systemd/system/ | grep -iE 'rtkit|unix-service|fontconfig|uuid|prio|upd'
systemctl list-units --type=service --all | grep -iE 'rtkit-prio|unix-service-upd'

正常:两条都是空输出。

异常:出现 rtkit-prio-cache.service、unix-service-upd.service 这类你从没建过的 unit。

4. 载荷路径在不在

ls -la /var/cache/fontconfig/.uuid-cache 
       /var/lib/rtkit/.prio-cache 
       /var/lib/unix-service/.upd-cache 2>&1

正常:三条都是 No such file or directory。

异常:某条目录真的存在,而且里面还有可执行文件。

这三个路径的共同点值得记一下:都藏在正常软件的缓存目录下,且以 . 开头(隐藏目录)—— ls 不加 -a 根本看不见。

5. 进程里有没有

ps aux | grep -iE 'uuid-cache|prio-cache|upd-cache' | grep -v grep

正常:空输出。

末尾那个 grep -v grep 是为了把「你这条 grep 命令自己」过滤掉。不加它,你会看到一行假阳性,然后虚惊一场。

异常:有进程在跑。记下它的 PID 和启动时间。

6. 最近 7 天新增的 unit 和可执行文件

find /etc/systemd/system -maxdepth 1 -newermt '-7 days' -type f
find /usr/local/bin /usr/local/sbin -maxdepth 1 -newermt '-7 days'
find /tmp /dev/shm -maxdepth 2 -type f -perm -u+x

正常:列出来的文件你都认识,比如自己放的脚本、wp 命令、nexttrace。

异常:/tmp 或 /dev/shm 里有陌生的可执行文件。这两个目录是恶意程序最爱待的地方 —— 重启就没、又几乎总能写。

7. SSH 后门(这一步最容易被忽略)

cat /root/.ssh/authorized_keys
for h in /home/*/.ssh/authorized_keys; do [ -s "$h" ] && echo "== $h" && cat "$h"; done
journalctl -u ssh --since '-7 days' | grep Accepted

正常:每把 key 你都知道是哪台机器、哪个人在用;登录日志里的 IP 你都认识。

异常:出现不认识的公钥,或者日志里有没见过的 IP 成功登录。

⚠️ 但有一点要说清楚:带 command="..." 的 key 不等于后门。这是 SSH 的「受限密钥」功能 —— 可以限制这把 key 只能跑某个脚本、不许开 shell、不许转发端口,很多运维是故意这么配的。判断标准只有一个:它指向的那个脚本,是不是你放的。

8. PHP 版本

php -v

正常:8.4.27 / 8.3.36 / 8.2.35 / 8.5.12 或更高。

异常:低于这几个数字,说明补丁没打上(怎么升见我另一篇,末尾有链接)。

我这边跑完的结果

邮件里那套 IOC,一条都没中。假服务不存在、载荷路径不存在、crontab 里全是自己的任务、PHP 已经是 8.4.27。

但第 7 条查出了点东西。

一个"以为中招了"的故事

authorized_keys 里有四个 key:

ssh-rsa ... OpenSSH-rsa-import-091223
command="/usr/local/sbin/fleet-dump.sh",no-agent-forwarding,no-port-forwarding,no-pty ... ED25519
command="/usr/local/sbin/fleet-archive.sh",no-agent-forwarding,no-port-forwarding,no-pty ... ED25519
ssh-ed25519 ... vpscat-ops@Cat-AQXN5W

当时第一反应:后两个带 command= 的,这不就是标准后门写法吗?

再看登录日志,心跳差点停:

Oct 22 00:39  Accepted publickey for root from 179.255.xxx.xxx
Oct 22 01:39  Accepted publickey for root from 179.255.xxx.xxx
Oct 22 02:39  Accepted publickey for root from 179.255.xxx.xxx
... 每小时一次,一直到 15:43 ...

一个巴西的 IP,每小时用一把不属于我的 ED25519 密钥登录 root。

然后我去看了那两个脚本。

# fleet-dump.sh
# 导出本机 24 个国内目标的 RRD(供跨机房对比 / 画晚高峰图)
...
docker exec smokeping rrdtool fetch ...

# fleet-archive.sh
# 归档用:把本机 SmokePing 的 RRD 目录打包输出到 stdout
# ⚠️ 这是受限密钥专用脚本(authorized_keys 里 command="..." 指定)
exec tar czf - -C /home/data smokeping

是我自己三个月前写的。

那台"巴西 IP"是我自己的监控汇总节点,每小时来拉一次 SmokePing 的数据画延迟图。那两个带 command= 的 key,是我故意做成受限的 —— 只能跑固定脚本,不能拿 shell,不能转发端口。

虚惊一场。不过这一查也值:如果你分不清哪些 key 是自己放的,那你就已经输了一半。

顺手要修的三件事

真正的问题不在入侵,在配置:

PermitRootLogin yes
PasswordAuthentication yes
Port 22

日志里随时能看到有人在试:

15:38 Failed password for root from 123.58.215.196
15:43 Failed password for invalid user ftpuser from 123.58.215.196
15:44 Failed password for invalid user hr from 123.58.215.196

修法(改之前务必确认密钥能登,改错了会把自己锁在外面):

sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/; 
        s/^PermitRootLogin yes/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
sshd -t && systemctl reload ssh
# 先别关当前会话,新开一个窗口验证能登录,再关旧的

最后

这篇笔记就是自己服务器自检的过程。如果你也收到类似预警,或者说想自检自己的服务器有没有中招,直接从头跑一遍就行。

这段 crontab 是用"注释伪装 + 假服务兜底 + 隐藏目录"的持久化手法,换哪个漏洞都能用。

赞(0)
未经允许不得转载:F12笔记 » 服务商发来预警(PHP2Shell),我中招了?我把 VPS 从头查了一遍(附 8 条自查命令)

评论 抢沙发