<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>文件上传漏洞 on Zero Day Notes</title>
        <link>https://blog.5772447.xyz/tags/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E/</link>
        <description>Recent content in 文件上传漏洞 on Zero Day Notes</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh</language>
        <lastBuildDate>Sat, 25 Jul 2026 07:00:00 +0800</lastBuildDate><atom:link href="https://blog.5772447.xyz/tags/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>记一次企业门户网站被植入 Webshell 的入侵溯源与清除</title>
            <link>https://blog.5772447.xyz/posts/0afe0629/</link>
            <pubDate>Sat, 25 Jul 2026 07:00:00 +0800</pubDate>
            <guid>https://blog.5772447.xyz/posts/0afe0629/</guid>
            <description>&lt;h2 id=&#34;问题背景&#34;&gt;&lt;a href=&#34;#%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;问题背景&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;我们对外的企业门户网站是一套基于 Tomcat 9 + Spring MVC 的老系统，跑在一台放在 DMZ 区的 Linux 服务器上，前面是 Nginx 反向代理，再往外是防火墙做端口映射。这套系统上线好几年，功能改动不多，一直是&amp;quot;能跑就不动&amp;quot;的状态。&lt;/p&gt;&#xA;&lt;p&gt;某天下午，全流量检测设备（NDR）弹出一条告警：这台 Web 服务器在持续向一个境外 IP 发起&lt;strong&gt;可疑加密 HTTP 通信&lt;/strong&gt;，特征命中&amp;quot;疑似哥斯拉（Godzilla）Webshell 加密流量&amp;quot;。安全同事把工单转给我时，第一反应是&amp;quot;会不会是误报&amp;quot;——毕竟这台机器只是个门户站，没什么敏感数据。但告警的置信度不低，且外联是持续性的，只能按真出事来应急处理。&lt;/p&gt;&#xA;&lt;h2 id=&#34;故障现象&#34;&gt;&lt;a href=&#34;#%e6%95%85%e9%9a%9c%e7%8e%b0%e8%b1%a1&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;故障现象&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;登上服务器先做了一圈粗看，几个现象拼在一起就不像误报了：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;CPU 间歇性飙高&lt;/strong&gt;：&lt;code&gt;top&lt;/code&gt; 里 Tomcat 的 java 进程偶尔冲到 80% 以上，但门户站访问量并不大，业务上没有理由这么忙。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;异常外联确实存在&lt;/strong&gt;：&lt;code&gt;netstat -antp&lt;/code&gt; 能看到该 java 进程与告警里那个境外 IP 建立着 ESTABLISHED 连接，端口是 443，流量是加密的，抓包也看不出明文。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;网站被挂了暗链&lt;/strong&gt;：用搜索引擎 &lt;code&gt;site:&lt;/code&gt; 检索自己的域名，翻到几个根本不存在的博彩、菠菜类页面路径，点进去会 302 跳转到外部站——典型的被挂马 / SEO 暗链。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;上传目录里多了陌生文件&lt;/strong&gt;：门户有个&amp;quot;个人头像上传&amp;quot;功能，落地目录 &lt;code&gt;/data/webapp/portal/upload/avatar/&lt;/code&gt; 下，除了正常的 jpg/png，还躺着几个 &lt;code&gt;.jsp&lt;/code&gt; 文件，修改时间集中在三天前的凌晨。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;到这一步基本可以确认：不是误报，是真被人打进来了，且已经落地了 Webshell、并在拿这台机器做外联跳板和挂暗链。&lt;/p&gt;&#xA;&lt;h2 id=&#34;排查过程&#34;&gt;&lt;a href=&#34;#%e6%8e%92%e6%9f%a5%e8%bf%87%e7%a8%8b&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;排查过程&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;第一步，先确认外联的&amp;quot;主体&amp;quot;是谁。&lt;/strong&gt; 用 &lt;code&gt;netstat -antp | grep &amp;lt;境外IP&amp;gt;&lt;/code&gt; 拿到连接对应的 PID，再 &lt;code&gt;ps -ef | grep &amp;lt;PID&amp;gt;&lt;/code&gt;，结果指向的是 Tomcat 自己的 java 进程，而不是某个独立的挖矿木马或反弹 shell 进程。这说明恶意行为是&lt;strong&gt;跑在 Web 容器进程里的&lt;/strong&gt;——要么是落地的脚本 Webshell 被调用，要么是更隐蔽的内存马。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;第二步，翻 Web 访问日志找入口。&lt;/strong&gt; 重点看 Nginx 的 &lt;code&gt;access.log&lt;/code&gt; 和 Tomcat 的 &lt;code&gt;localhost_access_log&lt;/code&gt;。Webshell 的访问有很强的特征，用几个条件一过滤就浮出来了：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;2&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;3&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;4&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;5&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 找对单个 URL 高频 POST 的可疑请求&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;$6==&amp;#34;POST&amp;#34;{print $7}&amp;#39;&lt;/span&gt; access.log | sort | uniq -c | sort -rn | head&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 结合异常 UA / 固定 Content-Length 规律进一步收敛&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;grep &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;avatar/.*\.jsp&amp;#34;&lt;/span&gt; access.log | awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{print $1,$4,$7,$9,$10}&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;结果非常清楚：&lt;code&gt;/upload/avatar/x1.jsp&lt;/code&gt; 这个路径在过去三天里被同一批源 IP 反复 POST，请求体长度呈规律性变化，User-Agent 是 Java 客户端的默认值——这正是哥斯拉客户端连接 Webshell 的典型行为。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;第三步，确认落地文件性质。&lt;/strong&gt; 打开那几个 &lt;code&gt;.jsp&lt;/code&gt;，内容不是明文，而是一段加载器：接收 &lt;code&gt;pass&lt;/code&gt; 参数，做 Base64 解码后再 AES 解密，动态编译执行——标准的哥斯拉加密马，磁盘上和流量里都是密文，普通特征匹配很难抓。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;第四步，回溯 Webshell 是怎么传上来的。&lt;/strong&gt; 门户的头像上传接口按理只允许图片。翻代码 + 翻上传接口的请求日志，还原出攻击者的绕过手法：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;服务端对文件类型的校验用的是&lt;strong&gt;黑名单&lt;/strong&gt;（只拦 &lt;code&gt;.php&lt;/code&gt;、&lt;code&gt;.asp&lt;/code&gt;），没拦 &lt;code&gt;.jsp&lt;/code&gt;；&lt;/li&gt;&#xA;&lt;li&gt;且校验只看了&lt;strong&gt;前端传来的文件后缀字符串&lt;/strong&gt;，没有做服务端 MIME / 文件头（magic number）校验；&lt;/li&gt;&#xA;&lt;li&gt;最致命的是，&lt;strong&gt;上传目录就在 Web 根目录下，且被 Tomcat 当作可执行的 JSP 目录&lt;/strong&gt;——文件传上去就能直接被当脚本解析执行。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;三个问题叠加，就是一条完整的&amp;quot;任意文件上传 → Webshell 落地 → 直接访问执行&amp;quot;的链路。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;第五步，排查有没有内存马。&lt;/strong&gt; 落地文件好清，但更怕对方在容器里注册了反射型的 Filter / Servlet 内存马（磁盘无 class、重启才消失）。对比了 Tomcat 当前已加载的 Filter 链与应用 &lt;code&gt;web.xml&lt;/code&gt; 声明的差异，又用 &lt;code&gt;arthas&lt;/code&gt; 扫了一遍可疑的动态注册组件，确认这次&lt;strong&gt;没有内存马&lt;/strong&gt;，攻击者只用了落地脚本马。&lt;/p&gt;&#xA;&lt;h2 id=&#34;解决方案&#34;&gt;&lt;a href=&#34;#%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;解决方案&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;确认范围后按应急响应流程处置，顺序很重要：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;先隔离、后动手&lt;/strong&gt;：在防火墙上只放行运维跳板的入站、掐掉那条境外外联，但&lt;strong&gt;暂不整机断网、不重启&lt;/strong&gt;，先保留现场做取证。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;固定证据&lt;/strong&gt;：打包 &lt;code&gt;access.log&lt;/code&gt;、Webshell 文件、&lt;code&gt;/tmp&lt;/code&gt; 与 upload 目录、进程与网络快照，计算哈希留存。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;清除落地马&lt;/strong&gt;：删除 upload 目录下全部 &lt;code&gt;.jsp&lt;/code&gt; 文件，全盘 &lt;code&gt;find / -name &amp;quot;*.jsp&amp;quot; -newermt &amp;quot;3 days ago&amp;quot;&lt;/code&gt; 复核有没有藏在别处的第二落点。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;清内存并重启&lt;/strong&gt;：处理完落地文件后重启 Tomcat，确保即便有残留动态组件也被清掉。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;修上传接口（根治）&lt;/strong&gt;：改为&lt;strong&gt;扩展名白名单 + 服务端 magic number 校验 + 随机重命名&lt;/strong&gt;，并把上传目录移出 Web 根、改为&lt;strong&gt;独立存储 + 禁止脚本执行&lt;/strong&gt;。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;收尾加固&lt;/strong&gt;：轮转服务器口令与相关密钥、清理被挂的暗链页面、通知搜索引擎重新收录。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;根因分析&#34;&gt;&lt;a href=&#34;#%e6%a0%b9%e5%9b%a0%e5%88%86%e6%9e%90&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;根因分析&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;技术根因是三点缺陷的叠加，缺一不可：&lt;strong&gt;文件类型校验用黑名单且不全（漏了 .jsp）→ 只信前端后缀、无服务端内容校验 → 上传目录与 Web 根同源且可执行脚本&lt;/strong&gt;。任何一环补上，这条攻击链都走不通。而管理上的根因是这套&amp;quot;能跑就不动&amp;quot;的老系统长期无人做安全基线审视，暴露面（对外的可上传接口）一直无监控，直到全流量设备兜底才发现。&lt;/p&gt;&#xA;&lt;h2 id=&#34;预防措施&#34;&gt;&lt;a href=&#34;#%e9%a2%84%e9%98%b2%e6%8e%aa%e6%96%bd&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;预防措施&#xD;&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;上传接口&lt;/strong&gt;：扩展名白名单 + 服务端 MIME / 文件头双重校验 + 上传后随机重命名，从源头堵住可控文件名。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;存储与执行分离&lt;/strong&gt;：上传目录独立存放，Nginx / Tomcat 层面&lt;strong&gt;显式禁止该目录解析 jsp/php&lt;/strong&gt;（如 location 配置移除脚本 handler），即使传上去也无法执行。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;最小权限运行&lt;/strong&gt;：Web 容器用非 root 的低权限用户跑，限制其对系统目录的写权限。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;纵深检测&lt;/strong&gt;：部署 RASP / Webshell 检测，定期用河马、D 盾等工具做落地文件基线扫描；保留全流量 NDR 兜底加密马。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;暴露面治理&lt;/strong&gt;：对外系统定期做资产梳理与漏洞扫描，把&amp;quot;能跑就不动&amp;quot;的老站纳入常态化安全巡检。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;总结&#34;&gt;&lt;a href=&#34;#%e6%80%bb%e7%bb%93&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;总结&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;这次事件的隐蔽点在于：外联主体是 Tomcat 自己、木马是加密的、入口是个不起眼的头像上传接口，任何一个单看都容易被当成&amp;quot;业务正常&amp;quot;或&amp;quot;误报&amp;quot;放过。真正把线索串起来的是&lt;strong&gt;全流量告警 + Web 访问日志的高频 POST 特征分析&lt;/strong&gt;这两把钥匙。对运维来说，最该记住的不是&amp;quot;哥斯拉长什么样&amp;quot;，而是：&lt;strong&gt;任意文件上传永远要按白名单 + 内容校验 + 存储执行分离三件套来防，而不是靠拦几个危险后缀&lt;/strong&gt;；同时对外暴露面必须有日志、有监控、有兜底，别让老系统成为攻击者最舒服的落脚点。&lt;/p&gt;&#xA;</description>
        </item></channel>
</rss>
