
最近在整理一批应急响应记录时我把过去大半年用过的WebShell分析手段重新梳理了一遍最终攒下了30个高频技巧。这里说的WebShell分析围绕的是一个非常具体的问题当一台Web服务器可能被上传了恶意脚本时如何通过文件、日志、行为和流量四条线索尽快把“有没有、是不是、干了什么”这三个问题查清楚。标题写“懂一半就能算高手”我觉得真不算夸张——这30个技巧不是让你背下来的知识点而是按“定位、研判、验证、收尾”四个阶段排好的操作手册。这套东西适合谁用安全运营、应急响应工程师、开发兼运维的同学甚至是被临时拉去救火的后端都适用。能熟练用上三分之一日常应急响应基本不抓瞎能熟练用上一半已经可以独立带队做一轮完整的WebShell专项排查了。下面我就把这30个技巧按使用场景拆开讲每个技巧都尽量说明原理和适用场景最后再带你走一遍完整案例看完就能直接上手。1. 先定调WebShell分析到底在分析什么很多人第一次接触WebShell分析上来就翻日志、查文件折腾半天却没有结论原因是没有把目标拆清楚。WebShell分析的最终目标其实只有三句话判断服务器上有没有异常脚本、这个脚本是不是恶意的、它进入系统后到底做了什么。围绕这三个诉求分析工作可以拆成两条主线静态分析和动态分析。1.1 回答三个问题有没有、是不是、干什么“有没有”解决的是定位问题。在没有明确告警的情况下怎么从成百上千个文件里把可疑对象筛出来。这个阶段依赖文件系统层面的观察比如文件修改时间、目录变化、扩展名异常、与历史基线对比。很多人上来就用关键词搜整个磁盘其实效率很低正确做法是先通过时间和目录缩小范围。“是不是”解决的是定性问题也就是在找到可疑文件之后判断它是真的WebShell还是正常业务代码、第三方组件、开发留下的调试脚本。这个阶段依赖代码层静态特征包括风险函数、编码方式、混淆程度、代码结构和上下文语义。“干什么”解决的是行为问题确定样本确实有问题之后要进一步还原它的行为链路访问了哪些接口、读取了哪些敏感配置、有没有尝试连接外部主机、有没有添加自启动任务。这三个问题有严格的先后顺序。没有完成定位就急着做行为分析容易在无关文件上浪费时间没有完成定性就直接按WebShell处置又容易误伤业务。实际项目中最常见的翻车案例都是因为跨越阶段直接在待定文件上执行了命令既污染了现场又丢失了判断依据。1.2 三种分析对象落地文件、混淆文件、内存样例WebShell在服务器上的存在形态决定了分析手段的选择。第一种是落地文件型也是最常见的形态通常是PHP、JSP、ASPX脚本文件直接被写进web目录、上传目录或临时目录。这类样本特征相对明显静态扫描命中率高。第二种是加密混淆型脚本经过编码、拼接、压缩函数组合表面上看起来是一堆乱码字符串但会在运行到关键阶段时还原出真正的执行逻辑。这类样本需要结合字符串解码、熵值分析和动态模拟来识别。第三种是内存驻留型文件系统里不一定能找到独立的脚本文件代码可能通过类加载器注入、扩展模块加载或运行期生成的方式存在于内存中。处理这类样本要转向进程层面、网络连接层面和日志层面的综合判断。分清形态的意义在于不同形态对应不同的分析优先级。文件落地型优先做目录遍历和静态扫描加密混淆型优先做解码尝试和沙箱验证内存驻留型优先做进程快照和连接追踪。顺序搞反了会漏掉真正的风险点。2. 30个技巧分组速查从定位到清除一次讲透下面这30个技巧我按使用阶段分成四组每一组解决一个阶段的核心问题。每个技巧单独拎出来都不复杂但组合在一起就是一套完整的分析流程。2.1 定位可疑文件信息收集阶段的8个技巧在没有告警、没有明确线索的情况下第一步永远是缩小范围而不是大海捞针。这个阶段的目标是快速找出最值得检查的文件再用后续手段验证。按时间窗口优先排查WebShell落地后一定会留下时间痕迹文件创建时间、修改时间、访问时间都会集中在攻击者操作的时间段。最有效的一招是先用最近7天或最近24小时修改过的脚本文件圈定范围再结合可疑请求日志反向验证。时间属性可以被人为伪造所以不要把时间作为唯一依据但它一定能帮助你砍掉90%的无关文件。按扩展名白名单反查不要只搜.php、.jsp这类常见脚本后缀而是要列出业务运行必须的扩展名白名单在web目录里找出不在白名单中的异常文件。很多攻击者会把脚本后缀伪装成.png、.jpg、.txt甚至无后缀文件名看着正常但放在不合理的目录里。反查思路比正向搜后缀更能发现伪装样本。重点排查上传目录和临时目录从攻击者的角度看上传入口是最便捷的投递通道。检查/home/upload、/tmp、/var/tmp、静态资源目录、头像目录这些本不该出现脚本文件的位置。如果业务系统根本没有上传功能却在uploads目录里出现了PHP文件这本身就是一个强信号。关注文件命名异常恶意文件通常会避开人工审查但也会暴露出命名上的慌乱。比如出现随机字符串文件名类似a3f9k2.php、双扩展名文件x.php.jpg、与正常文件高度相似但多了一个字符的副本名、或者突然出现大量连续编号文件。看起来越“普通”的命名越要确认它是否在版本库或备份中有对应记录。检查文件数量和目录体积突变一个原本只有几十个文件的目录突然多出上百个文件一个正常业务目录的体积在几天内翻了数倍——这些突变往往伴随着批量投毒或备用WebShell的存放。用du和ls配合历史快照对比几分钟就能定位异常。寻找隐藏文件和高权限文件隐藏文件以点开头在Windows和Linux下的意义不同但攻击者都爱用。检查web目录下的隐藏文件、设置了setuid/setgid权限的脚本文件、属主突然变成root或www的特殊文件。正常业务文件很少刻意隐藏出现即可疑。与备份或Git仓库做差异对比这是效率极高的定位手段。对比当前web目录和最近一次备份的差异凡是新增、修改的文件全部提取出来单独排查。前提是备份本身没有被污染所以在做对比前要先确认备份的时间点是否早于可疑事件发生时间。建立文件指纹基线把全站文件路径、大小、内容哈希固化到一张表里之后每次排查只做差异比对。这个技巧适合长期维护的系统初期建立基线的成本不高但后续每次应急都能把排查时间从小时级压缩到分钟级。2.2 辨别样本真伪静态特征研判的12个技巧定位阶段筛出来的可疑文件不一定都是WebShell。这一步要做的是静态特征研判通过代码特征、结构异常和语义判断来确定样本性质。这12个技巧覆盖了当前主流的恶意脚本特征面。标记高危函数调用脚本执行命令、读取配置、访问网络、操作文件系统的函数都是关注重点。真实业务代码也可能调用这些函数所以这里只能作为初筛条件不能作为最终定性依据关键是看函数调用的上下文。一个正常业务的图片上传接口不太可能同时出现命令执行和请求外部地址的逻辑。识别动态执行代码结构动态执行函数、可变函数名、字符串拼接后执行等结构是WebShell最典型的代码组织方式。看到代码把一段字符串拆成多个部分拼接、通过变量动态调用函数基本就值得进一步深挖。正常的业务逻辑也会用动态调用做插件化设计但要怀疑那些看不出业务含义的动态执行代码。警惕编码字符串Base64、十六进制、URL编码、自定义字符替换都是隐藏代码内容的常用手段。静态扫描时可以自动检测超长编码串用解码工具做一轮批量还原。编码串本身不可怕可怕的是解码后出现了命令、文件读写、配置读取等敏感操作。关注压缩与解码函数组合gzdecode、str_rot13、pack、unpack等函数单独看可能只是数据处理但组合使用时往往是代码还原链。攻击者会把真正的执行代码压缩编码后存储运行时再解码执行静态扫描时看到的只有一段无意义的乱码。这类组合出现时需要把解码后的内容拉出来看而不是停留在表面。检查文件操作函数链WebShell落地后常伴随一系列文件系统操作例如将上传的临时文件移动到web目录、修改文件权限、写入自启动脚本、删除自身等。静态代码里如果同时出现文件写入、目录切换、权限修改、原文件删除这几个动作基本可以判断不是正常业务。提取外部地址特征在脚本代码里搜索可疑的外部地址包括硬编码的URL、IP和域名。攻击者的C2地址有时会直接写在脚本里但更隐蔽的做法是编码后存放所以要把解码后的字符串也纳入检查范围。这里要注意不要只搜http协议开头的字符串很多脚本用自定义协议或者只写域名不写协议。识别图片头伪装最常见的伪装手法是在文件头部加上GIF89a或PNG头让file命令识别成图片但代码内容仍然是PHP。检查方法是直接读取文件内容而不是依赖file命令通过文本特征和实际执行环境判断。实际操作中可以用strings或十六进制查看工具跳过文件头直接看中部内容。检查业务注释伪装攻击者也懂“大隐隐于市”会在一堆正常业务注释中间夹带核心载荷或者把恶意逻辑放在看似人畜无害的类名、函数名里。应对办法是不要只看关键字要完整阅读关键函数的上下文逻辑尤其是那些名称正常但实现奇怪的方法。识别混淆变量名与垃圾代码正常开发者会尽量让变量名和函数名有意义而混淆代码的常见特征是无意义变量名如$a、$b、$c但不能一概而论、大量冗余变量赋值、无实际作用的算术表达式、重复调用的空函数。这些垃圾代码的目的不是实现功能而是提高人类阅读成本、绕过静态规则。计算内容熵值中文业务代码的熵值通常比较低英文代码略高但经过加密或压缩的代码数据分布接近随机熵值会显著偏高。写一个小脚本遍历可疑文件计算香农熵可以快速找出那些看起来“过于随机化”的脚本。这个方法特别适合识别加密混淆型WebShell。观察字符串长度分布正常代码里字符串长度分布相对均匀短字符串多、超长字符串少。如果某个文件中频繁出现几十KB的连续字符串、大量重复的长串文本说明内容很可能被编码或压缩过。这条配合熵值分析能覆盖多数“看起来不寻常”的样本。搜索敏感信息获取函数WebShell的核心目的之一是通过读取敏感信息扩大战果涉及数据库配置、环境变量、服务账号等内容的操作函数都在此列。静态扫描时对这些函数做加权打分结合出现频率和上下文可以量化判断文件的可疑程度而不是单纯看是否命中某一个特征。2.3 确认行为意图动态行为验证的6个技巧静态分析只能告诉我们“这个文件像什么东西”想要确认“它到底能干什么”还得靠动态行为验证。这一阶段的红线是绝对不能直接在受害服务器上执行可疑样本一切验证必须在隔离环境中完成。离线隔离复现准备一台断网的虚拟机恢复与受害服务器相同版本的系统环境和应用版本再把可疑文件完整拷贝进去复现。没有现成环境时用容器临时搭建也可以但必须断开外部网络连接。联网环境下的动态分析可能导致恶意行为外联让风险扩大。触发前先做快照在触发样本之前先记录系统状态包括进程列表、网络连接、文件列表、注册表或crontab内容然后执行疑似触发请求再对比前后差异。快照隔离了从“干净状态”到“感染后”的变化能让行为链路清晰呈现避免被大量无关数据干扰。分析进程树行为WebShell触发执行后通常会产生子进程例如调用命令执行功能、启动物理新进程。观察父子进程关系能快速确认脚本是否具备命令执行能力。一个正常的图片展示页面不应该衍生出解析命令的子进程。监控网络连接变化在隔离环境里用抓包工具监控样本执行前后所有对外连接请求重点关注连接外部地址的行为特别是非标准端口的TCP连接、DNS解析异常域名、HTTP回调请求。不能只看IP要看连接方向和数据内容有的样本会先做DNS查询再连接目标DNS日志里也有痕迹。检查持久化行为WebShell如果仅仅是单次执行危害可控一旦添加了计划任务、自启动项、服务注册、启动脚本等手段就具备持久化能力。动态分析时要特意检查这些位置在样本执行后有没有新增内容发现异常就要立即提取并记录。评估数据读取范围判断样本是否读取了数据库配置文件、口令文件、密钥文件、环境变量等敏感数据。可以在隔离环境里放置诱饵配置文件记录有哪些文件被读取。这一步的输出直接决定后续影响的定级和清理范围。2.4 收尾闭环溯源取证与清理加固的4个技巧分析工作做到行为确认还不够真正的闭环是把攻击入口、影响范围和清理措施全部落地。最后4个技巧解决的是收尾问题。日志关联定位攻击入口拿到样本哈希和特征后回到受害服务器的访问日志、应用日志、认证日志里反向搜索找出样本文件第一次出现的时间点、对应请求、来源IP、使用的参数。一个样本可能被多次访问但只有第一次上传时的请求能揭示攻击入口。这一步不能省否则下一次还会以同样方式被突破。重建攻击时间线把文件创建时间、日志请求时间、入侵告警时间、恶意行为外联时间放在一条时间轴上对比。时间线能让整个事件从“孤立样本”变成“完整故事”进而帮助确认攻击者还做了哪些操作、还有没有问题没有被发现。重建时间线时注意时区问题日志与文件时间可能用了不同时区先统一再对齐。证据留痕规范化发现恶意样本后不要直接删除文件而是先计算MD5/SHA256哈希、复制原文件到隔离目录、记录文件属性和路径再把样本打包加密归档。很多团队在清理阶段手太快把样本删了导致后续溯源和追踪完全没有依据。证据保存的优先级高于清理。清理后复查与基线固化删除恶意文件后立刻做一次全盘复查检查临时目录里有没有残留、计划任务里有没有被新写入口、其他主机有没有同类样本。确认干净后把样本特征哈希、文件名、特征码、外联地址整理进威胁情报表并更新文件基线。一次清理不是终点基线和IOC的沉淀才是下一次应急提速的关键。3. 实操演示从一条疑似上传的日志到完整清理复盘技巧讲再多不如走一遍完整的实操。下面用一个模拟项目作为案例把“日志发现—文件定位—静态验证—动态确认—影响评估—清理加固”的完整链路呈现出来。为了叙述方便我把目标服务器称为某业务系统系统基于PHP开发部署在一台Linux服务器上web根目录为/webroot访问日志位于/var/log/nginx/access.log。3.1 现场线索一条异常上传请求某天在进行日志例行检查时我发现了一条POST请求非常扎眼请求路径是/upload/image.php但请求体不是正常的图片数据而是几个表单字段其中一个字段的键名是cmd。正常的图片上传接口不会提交这类字段更不会让图片文件路径直接指向PHP脚本。第一反应是记录请求时间、来源IP和完整请求行把它们作为后续排查时间窗口的锚点。这里要提醒一点发现异常请求后不要立刻去web目录翻文件而是先固定日志证据。把请求时间前后10分钟的访问日志单独导出重点看同一来源IP还访问了哪些路径。很多时候攻击者在成功上传WebShell前后会先探测目标站点结构这些探测请求会为后续定位攻击入口提供线索。3.2 按图索骥定位文件并采集静态证据以那条请求时间为起点我在本地对/webroot目录执行了按修改时间筛选的操作命令大致是这样的find /webroot -type f \( -name *.php -o -name *.jsp -o -name *.asp -o -name *.aspx \) -mtime -7 -printf %TY-%Tm-%Td %TH:%TM %s %p\n | sort -r | head -100这行命令的意思是在/webroot下找最近7天内修改过的常见脚本文件按修改时间倒序输出前100条。执行结果中/webroot/upload/images/2024/11/18/u_98421.php排在最前面修改时间与那条异常请求完全吻合。注意这个文件位于图片上传目录扩展名却是.php这已经是一个明显的异常信号。接下来做三件事固定证据计算文件哈希、复制原始文件到取证目录、用只读方式查看文件内容。用stat命令查看文件属性用file命令识别文件类型用strings提取文件中可读字符串。这一步我没有在原始目录里执行任何可能修改文件的操作保证现场不被破坏。sha256sum /webroot/upload/images/2024/11/18/u_98421.php /evidence/sample_1.sha256 cp -p /webroot/upload/images/2024/11/18/u_98421.php /evidence/sample_1.php stat /webroot/upload/images/2024/11/18/u_98421.php file /webroot/upload/images/2024/11/18/u_98421.php strings /webroot/upload/images/2024/11/18/u_98421.php | head -50静态分析结果很清晰这个文件不是图片而是一个纯粹的PHP脚本没有图片头伪装代码只有16行其中包含了几个非常敏感的关键字命令执行函数、配置读取、文件上传临时路径检测。文件命名方式无意义字符串、出现在upload/images下和代码内容互相印证初步判断高度疑似WebShell。3.3 谨慎复现隔离环境动态确认静态分析只能给出高置信度的猜测不能直接下结论。我把样本文件拷贝进一台断网的虚拟机虚拟机里用相同版本的PHP运行时搭建了一个最小化环境并在执行前用抓包工具和文件监控工具打了快照。然后构造了一个无害的测试请求观察访问该页面时系统的行为变化。结果很清楚第一次请求后监控工具记录到进程派生出一个子进程执行系统命令同时抓包工具记录到一次对预设外部地址的DNS查询请求。正常图片展示逻辑绝不会产生这两类行为到此可以判定这不是误报。动态确认后我对样本的功能有了明确认知这是一个集命令执行、文件读取、外联通信于一体的恶意脚本攻击者在成功上传后大概率已经通过它执行过探测命令。3.4 清理加固复查、恢复与基线化确认恶意样本后处置顺序很重要。我没急着删除文件而是先做了一次影响面排查把所有web目录和临时目录的文件按时间再筛一遍确认只有一个可疑文件把access.log里该样本路径的所有访问记录导出发现从上传到今天共有4次访问全部来自同一个来源IP检查该IP的登录记录和错误日志确认没有其他账号被爆破成功最后检查计划任务和启动目录没有发现新的持久化配置。清理动作分三步走。第一步在保存证据后删除恶意文件并更新页面缓存与临时文件缓存。第二步对上传接口增加文件内容校验禁止脚本类型文件落地到图片目录同时限制该来源IP的访问权限。第三步把样本哈希、文件名规则、外联地址、来源IP整理成IOC记录加入检测规则和文件基线表。处置完成后继续观察了三天确认没有复发后关闭事件。4. 新手高发9个实战中容易踩的坑有了这30个技巧是不是就能顺畅完成一次应急分析了未必。下面这9个坑是我在实际项目里亲眼见过、也亲自踩过的按阶段分成三组每一组都值得拿笔记住。4.1 现场操作类最容易破坏证据的三个误区第一个误区在受害服务器上直接执行可疑样本。这是所有坑里最严重的。一旦在真实环境里执行样本攻击者可能立刻收到外联信号要么触发二次破坏要么销毁现场痕迹。正确做法是样本先哈希、复制、再移入隔离环境一切动态验证都在断网的虚拟机里完成。第二个误区只查web目录不查备份、缓存和日志轮转目录。攻击者比你想的更了解服务器目录结构备份目录里的旧版本WebShell、日志目录里被混淆脚本写入的隐藏文件、缓存目录里生成的可执行文件都容易成为漏网之鱼。排查范围至少覆盖web目录、临时目录、备份目录、日志目录四个位置。第三个误区删文件不留原始样本和证据。很多同学看到恶意文件第一反应是恨不能马上删掉。但删除动作本身会破坏溯源链条。应该先计算哈希、保留副本、记录文件属性和发现时间再执行清理。没有样本后续的溯源分析、IOC提取和同类扫描都无从谈起。4.2 判断分析类容易误报漏报的三个盲区第四个误区看到eval就把文件判死刑。动态执行函数在很多正常框架和模板引擎里都有使用看到eval/assert就直接定性会造成大量误报。正确的做法是看上下文eval的参数是固定字符串还是用户可控输入函数调用周围有没有明显的混淆代码整体逻辑是否符合业务场景。上下文决定性质单点关键字只能作为线索。第五个误区忽略业务方对代码的确认。静态分析出现灰色地带时最快的确定办法是找业务负责人确认这段代码是否存在、由谁编写。有些接口代码写得确实很绕但也确实是业务逻辑的一部分。跳过了业务确认这个环节很容易把一个老旧的第三方组件误判成WebShell浪费排查时间的同时还影响业务信心。第六个误区只做静态不做动态放过了混淆样本。静态特征能看出“可疑”但识别不了加密压缩后的真实行为。有些混淆型样本静态扫描时几乎没有警告运行后才还原出完整恶意逻辑。只要条件允许高嫌疑样本都要进隔离环境做一次动态行为确认彻底解码后再下结论。4.3 收尾固化类做一半留一半的三个问题第七个误区清理完成后不做复核。删除文件不等于处置完成。攻击者可能已经在同一目录或临时目录里备用了多份脚本清理后做一次快速的全盘复核按样本哈希再扫一遍全站文件确认没有相同内容的副本才是真正安全。我见过不少案例是在第一次清理后第三天又发现了同款样本原因就是没有做哈希全盘扫描。第八个误区不关联日志就草草结束。如果只删文件不查日志等于没有回答“攻击者从哪里进来的”这个问题。正确收尾至少要从日志里还原攻击入口、攻击时间、攻击次数、影响路径四个维度。缺少日志关联下次攻击者换个文件名进来你还是防不住。第九个误区没有将结果沉淀成IOC和基线。一次完整的应急分析应该输出样本哈希、攻击IP、攻击路径、URL特征、外联域名、检测规则建议。这些信息录入威胁情报库并更新到监控规则里才是这次排查真正的长期价值。否则每次碰到同类样本都要从头分析一遍效率低还容易漏。5. 把技巧沉淀成工具命令组合与体检脚本技巧要变成日常可用的能力最好落成一组命令和一个轻量脚本。下面放出的三个工具可以直接用于日常巡检和应急初筛。5.1 一条find命令圈定近期改动文件快速定位最近几天被修改过的脚本文件是WebShell分析最常用的起手式find /webroot -type f \( -name *.php -o -name *.jsp -o -name *.asp -o -name *.aspx \) -mtime -7 -printf %TY-%Tm-%Td %TH:%TM %s %p\n | sort -r | head -200注意几个细节-mtime -7表示最近7天内修改过的文件printf参数里的%s是文件大小%p是完整路径sort -r按时间倒序排列让最新修改的文件排在最前面。你可以把这个命令设置成每周自动执行一次输出结果作为疑似文件清单再结合备份差异进一步筛选。如果服务器上文件量大还可以追加 -size 10k 过滤掉太小的文件减少干扰。5.2 一条awk命令聚合异常请求日志分析阶段最常做的是按来源IP聚合请求数量再结合时间线定位异常访问awk {print $1} /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20这行命令按访问频率统计出TOP 20来源IP正常情况下访问量最高的IP往往是搜索引擎、监控系统或办公出口。如果看到一个陌生IP请求量异常高、集中在短时间内且大量访问上传接口就需要进一步拉出该IP的完整请求列表。更精准的做法是把请求路径里包含upload、image、attach等敏感路径的行再过滤一次grep -E upload|image|attach /var/log/nginx/access.log | grep -E \.php|\.jsp|\.asp5.3 一个Python体检脚本做批量初筛单文件人工分析没法覆盖大量文件我日常会用一个小脚本做批量初筛。它的逻辑不复杂遍历指定目录下的脚本文件计算文件大小、修改时间、内容熵值再命中几个高危关键字权重打分最后输出一个CSV报告。下面是简化版实现import os import re import math import csv import sys from collections import Counter RISK_KEYWORDS [ (reval\s*\(, 10), (rassert\s*\(, 8), (rbase64_decode\s*\(, 8), (rgzdecode\s*\(, 6), (rstr_rot13\s*\(, 4), (rshell_exec\s*\(, 10), (rsystem\s*\(, 8), (rexec\s*\(, 6), (rpassthru\s*\(, 8), (r\$_REQUEST|\$_POST|\$_GET|\$_COOKIE, 4), (r(file_put_contents|fwrite)\s*\(, 6), (rmove_uploaded_file\s*\(, 4), ] def calc_entropy(data: bytes) - float: if not data: return 0.0 length len(data) counter Counter(data) return -sum((count / length) * math.log2(count / length) for count in counter.values()) def scan_file(path: str): try: with open(path, rb) as f: data f.read() except Exception: return None score 0 matched [] try: text data.decode(utf-8, errorsignore) except Exception: text for pattern, weight in RISK_KEYWORDS: if re.search(pattern, text, re.IGNORECASE): score weight matched.append(pattern) return { path: path, size: len(data), entropy: round(calc_entropy(data), 2), score: score, keywords: ;.join(matched[:5]), mtime: os.path.getmtime(path), } def main(root_dir: str, output_csv: str): script_exts (.php, .jsp, .asp, .aspx) results [] for base, _, files in os.walk(root_dir): for name in files: if name.lower().endswith(script_exts): item scan_file(os.path.join(base, name)) if item and item[score] 0: results.append(item) results.sort(keylambda x: x[score], reverseTrue) with open(output_csv, w, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnames[path, size, entropy, score, keywords, mtime]) writer.writeheader() writer.writerows(results) print(freport saved: {output_csv}, matched {len(results)} files) if __name__ __main__: if len(sys.argv) ! 3: print(fusage: {sys.argv[0]} root_dir output.csv) sys.exit(1) main(sys.argv[1], sys.argv[2])这个脚本的定位是“初筛器”不是“判定器”。分数高的文件不代表一定是WebShell分数低也不代表绝对干净。它存在的意义是把上百个文件压缩成几行高优先级报告让你把有限的时间花在最值得研究的对象上。脚本本身没有任何执行恶意代码的能力可以放心跑在生产服务器上。使用的时候建议先跑在web根目录再把数据库配置文件、日志目录、备份目录一起纳入。输出的CSV按分数倒序排列优先处理score大于20的文件score介于10到20之间的文件结合静态特征人工过一遍score小于10的基本可以先放一放。如果某次扫描突然出现一个分数特别高的文件先不要动它直接进入“证据固定——哈希记录——隔离复现”的流程用第3章的链路走完全程再下结论。我个人的习惯是每个月初把这条巡检脚本跑一遍输出结果和上个月的基线做对比。新增的高分文件永远是排查重点因为攻击者一旦上传了WebShell文件变动轨迹会自然暴露在时间线上。稳扎稳打地把这套流程用熟比我在这里写再多技巧都更有用毕竟WebShell分析这门手艺最终是靠在实战中一次次复盘练出来的。