做安全的同行应该都清楚,Webshell管理工具是攻防对抗里出现频率最高的一类基础设施。最近一次应急排查里,客户环境的Web服务器被上传了一个伪装成图片的脚本,静态扫描没报,最后是翻流量发现POST请求体base64解码后有一串蚁剑的典型初始化代码,才确认服务器已经被控了两周。这类场景每天都在发生。本文基于我做过的一些流量分析排查,把菜刀、蚁剑、冰蝎、哥斯拉这四款工具的通信特征、识别思路和抓包分析方法梳理一遍,适合WAF规则工程师、蓝队值班人员、应急响应分析师参考。
1. 四款主流Webshell管理工具流量分析的价值与思路
1.1 为什么流量分析是Webshell检测体系里绕不开的一环
先说个很典型的场景。业务服务器上被放了一个Webshell,文件可能做了加密、混淆,或者干脆直接落在内存里不写盘,这时候静态查杀基本等于瞎子。但无论后门写得多隐蔽,攻击者只要还在用管理工具连接,就一定会在网络上留下通信记录。菜刀、蚁剑、冰蝎、哥斯拉是当前出现频率最高的四款Webshell管理工具,它们的协议设计、加密方式、请求特征差异很大,流量分析的价值正在于此:不依赖文件系统,不依赖告警规则是否更新,只要网络侧有镜像流量,就有机会通过特征识别出恶意通信。
从攻击链路的视角看,Webshell管理工具处于"上传后门之后"的关键阶段。攻击者上传一个脚本只是第一步,真正的大动作——提权、内网扫描、数据外带——都要靠管理工具建立稳定的命令通道来完成。所以流量分析一旦识别出工具的通信行为,基本就等于抓住了攻击者当前正在使用的通道,可以直接定位到受害主机和后门路径。这个价值比单纯发现一个恶意文件要高得多。
1.2 工具通信模式的差异,决定了检测思路的分层
这四款工具虽然是同类,但通信设计差别非常大。菜刀是最老牌的,流量基本是明文,POST参数里直接就是PHP代码,关键词一大堆,老一代WAF规则都能拦。蚁剑是半明文,默认编码器会在服务端拼一段特征明显的PHP初始化代码,但用户可以自定义编码器、自定义UA,所以纯特征匹配会漏。冰蝎和哥斯拉走的是全加密路线,请求体是密文,关键词根本看不见,必须从加密协议本身、TLS指纹、连接行为这些维度去识别。
所以流量分析不能一套规则打天下。我的经验是分三层:第一层是明文关键词规则,用来打菜刀和默认配置的蚁剑;第二层是加密流量行为分析,用来覆盖冰蝎、哥斯拉这类不产生明文特征的通信;第三层是对端侧补充检测,比如JA3指纹、响应体结构、连接时序的统计分析。三层叠加,误报和漏报才能压到可接受范围。下面这个表是我平时做研判时快速对标的概览:
| 工具 | 通信模式 | 请求体特征 | 主要识别维度 |
|---|---|---|---|
| 菜刀 | 明文 | 直接含PHP代码、eval、base64_decode | 关键词正则、明显UA |
| 蚁剑 | 半明文 | 明文带@ini_set、"-> | "标记(默认) |
| 冰蝎 | 全加密 | AES密文,高熵 | 握手行为、密文熵、JA3、请求时序 |
| 哥斯拉 | 全加密 | 自定义加密密文,参数名固定 | 响应结构、参数名、JA3、行为模式 |
1.3 流量分析不要只盯着Payload
很多刚接触这块的人容易犯一个毛病:只盯着请求体里的关键词,恨不得一条正则吃遍所有工具。实际上,真正有效的检测维度比这多得多。请求头里的User-Agent、Content-Type、Accept、Connection,某些工具默认值是固定的;响应体里的分隔符、格式标记,有些工具每次都会带上;请求的时序特征,比如攻击者连上后先发一条探活请求、间隔几百毫秒再发指令,这个节奏跟正常用户浏览网页完全不同;如果走HTTPS,客户端和服务端的TLS指纹也会因为工具使用的库比较固定而暴露。
把这些维度组合起来,才是完整的流量分析思路。单个特征可能是模糊的,但多个特征同时命中,基本就可以定性了。实战里我见过一些加密流量,单看body熵值也不算特别高,可它的请求目标是一个完全陌生的新脚本路径,而且源IP在其他时间段从没访问过这个路径,这个组合就非常可疑了。这也是为什么我一直强调,做流量分析不能只停留在"匹配关键词"这个层面。
2. 菜刀与蚁剑:明文与半明文流的识别要点
2.1 菜刀的流量特征
菜刀,也就是常说的Chopper,是很多入门安全测试人员最早接触的工具。它本身是一个Java图形化客户端,工作原理很简单:服务端放一个一句话木马,最常见的PHP版本就是<?php @eval($_POST['pass']);?>这种,客户端把所有要执行的操作封装成PHP代码,通过POST参数传过去。
这个设计决定了它的流量特征极其明显。第一,POST参数名往往就是一句话木马里定义的那个变量名,常见的是pass、cmd、z0这类;第二,参数值里必然出现@eval、assert、$_POST、base64_decode这类PHP函数调用,而且大部分情况是明文,WAF直接正则匹配就能拦住;第三,菜刀默认的User-Agent是老旧的Mozilla/4.0,配合Content-Type: application/x-www-form-urlencoded,在流量侧看就是一个很老派的浏览器发出的表单请求。
我在分析抓包文件时,最常用的筛法就是先过滤POST请求,再对请求体字段做关键词匹配。如果看到请求体里有@eval嵌套base64_decode这种调用,基本可以断定是菜刀或者同类工具。记得有一次在客户环境里排查,请求体里甚至直接明文写了执行系统命令的代码,连解码都不用,一眼就看穿了。菜刀这类明文工具,现在更多是打在老系统或者自动化扫描场景里,但确实还是有攻击者在用,而且经常配合弱口令一起出现,不能放松。
2.2 蚁剑的流量特征
蚁剑是开源的,使用量非常大。它的灵活度比菜刀高很多,支持自定义编码器、自定义UA,所以网上那些只匹配antSword字符串的旧规则,早就拦不住新版本了。
不过蚁剑在默认配置下仍然有很强的指纹。我观察到的默认PHP编码器生成的payload里,开头经常会带一串固定的初始化代码,大致是关闭错误显示、设置超时时间、输出一个类似->|的分隔标记,再继续执行指令。这一串特征字符串在流量里非常扎眼。另外,旧版蚁剑的默认User-Agent是antSword/v2.1这种格式,虽然新版默认改成和Chrome一样了,但在翻老日志的时候还是很好用。
蚁剑还有一个容易忽略的点:它的默认通信方式是在POST参数里放一串PHP代码,这串代码本身是明文(取决于是否使用编码器),所以如果你在流量里看到一段以@ini_set开头的PHP代码,夹杂着set_time_limit、display_errors、max_execution_time这些配置函数,后面再接一个echo "->|",那差不多就是蚁剑在活动。实际分析中,我会同时对请求体和响应体做匹配,因为蚁剑的部分版本会在响应尾部回显|<-这类结束标记。
具体到检测规则,我建议不要单用UA匹配,而是把"UA可疑+请求体含PHP初始化函数序列+响应体含分隔标记"三个条件叠加,能有效降低误报。之前在一个内部测试环境里,我见过一个正常业务的健康检查脚本,UA也是自造的,请求体里也带了一段PHP代码片段,差点误报成蚁剑,最后是靠响应体没有分隔标记排掉的。这种交叉验证的思路,比只看一个字段靠谱得多。
2.3 明文类Webshell工具的通用拦截规则
到这里可以总结几条通用规则,适合放在WAF或者流量检测系统里。
第一是请求体内容检测,正则匹配@eval、assert、base64_decode和$_POST这类关键字的组合。但要小心正常业务里也有eval和assert,必须加前置条件,比如请求指向的是脚本文件、参数值里同时出现多个敏感函数。第二是响应体分隔符检测,针对蚁剑这类固定输出标记的特征。第三是连接行为检测,菜刀和蚁剑的交互都是"短频快"的,攻击者执行命令时往往在几秒内连续发出多个POST请求,每个请求之间间隔非常短,这个序列特征和正常浏览的节奏差异很大。
注意:明文类规则最大的敌人是误报。我见过最夸张的一次,是一个CMS后台自带的插件更新功能,POST数据里天然包含
eval和base64_decode,导致WAF频繁告警。所以明文规则不要裸用,尽量配合目录、文件类型、Referer、Cookie等上下文条件一起判断。
3. 冰蝎与哥斯拉:加密流对抗思路
3.1 冰蝎的加密通信机制与识别点
冰蝎是加密型Webshell管理工具的代表。它的PHP版本默认使用AES对称加密,整个通信过程基本看不到任何明文PHP代码。冰蝎3.0的流程大致是这样的:客户端第一次连接时会发送一个握手请求,服务端在响应里返回一个随机密钥,这个密钥以base64形式存在;之后的每一次命令请求,客户端都用这个密钥对待执行的代码做加密,再通过POST发送,服务端解密执行后把结果加密返回。
这个机制导致冰蝎的流量在文本特征上几乎等于零,老式的关键词规则全部失效。但换个角度想,加密流量反而是它最大的识别点。首先看请求体,冰蝎POST的body是一段高熵密文,可能是十六进制字符串也可能是base64,里面没有任何可读的关键词。如果你发现一个业务接口的POST请求,body字符分布乱七八糟、熵值明显偏高,就是危险信号。其次看请求头,冰蝎3.0的一些字段组合相对固定,比如Content-Type: application/x-www-form-urlencoded搭配一个固定的Accept头,再加上看似正常的Chrome UA,这个组合出现在同一个源IP上而且频次突然变高,很可疑。第三看时序,冰蝎在命令执行前有时会先发握手请求来获取密钥,所以流量里会存在"两个请求一组"的规律模式。
3.2 哥斯拉的加密通信机制与识别点
哥斯拉出现得比冰蝎晚,功能更全面,支持PHP、JSP、ASP.NET多种平台,通信同样走加密路线,但协议和冰蝎有明显差别。
哥斯拉生成shell的时候,会把一组密钥和加密算法配置写在服务端代码里,客户端连接时把要执行的代码用这个密钥加密,通过POST参数发送。这里有个很实用的识别点:哥斯拉默认配置下,POST参数名是固定的,常见的是pass,请求体是一长串base64或自定义编码后的密文。响应体也不是随便返回的,往往带有固定的格式标记或固定开头,比如有些版本的响应每次都先输出回车换行,再拼密文数据,最后以特定字符收尾。这种"响应结构恒定"的现象在正常业务接口里很少出现,可以作为重要线索。
哥斯拉和冰蝎还有一个不同点:它不像冰蝎那样每次连接都重新协商密钥,而是把密钥固化在shell里,所以同一个后门的流量结构比冰蝎更稳定。在蜜罐或者高交互蜜网上,如果看到一个路径被反复POST、响应结构几乎不变,就要重点怀疑是哥斯拉在通信。
3.3 加密流量识别不能只靠特征库
加密型工具的检测,我最想强调的一点是不要迷信规则库。冰蝎4.0、哥斯拉新版本一直在调整协议细节,老的"特征字符串"规则很快会失效。真正能长期稳定起作用的,是下面几个思路。
第一个是JA3/JA3S指纹。冰蝎和哥斯拉的客户端都是Java写的,Java客户端发起TLS连接时,ClientHello里的密码套件、扩展列表、椭圆曲线参数组合相对固定,换算成JA3指纹后,在一段时间内基本不变。HTTPS流量里,如果看到大量来自同一源IP的请求JA3指纹高度一致,而且指向的都是同一个小路径,可以先拉出来看看。第二个是高熵请求体检测。通过数学方式计算POST body的信息熵,超过阈值就标记为加密,这个方法不依赖具体工具版本。比如一段正常的JSON数据熵值通常在4到5之间,而随机十六进制密文可以到6以上。第三是结合主机侧的证据,比如同时期的Web访问日志里出现了一个以前没见过的脚本文件被频繁POST,配合流量侧的加密请求,两者互相印证,比单靠网络特征可靠得多。
提醒一下:JA3指纹不是万能的,新版本工具已经出现TLS参数随机化的迹象。真正的纵深防御,是流量侧做"加密异常+行为模式"的双重判断,主机侧再做文件和行为监控,两边交叉验证,而不是指望某一条规则一次搞定。
3.4 工具版本演进带来的检测挑战
工具版本迭代是流量检测头疼的事情。冰蝎3.0到4.0之间,加密算法、请求头、密钥协商方式都调整过,很多老规则直接失效。哥斯拉不同版本对参数名、默认密钥、加密算法的可配置性也一直在变。蚁剑更是从发布起就支持自定义编码器,甚至能加载别人写的插件。这意味着流量检测规则天然有时效性,不是写完就能躺平。
应对思路只有一个:规则库要跟着工具新版本迭代,定期在新版本工具上做流量采集和特征比对。同时,规则设计尽量选择那些短期不会变的维度,比如加密body的高熵特性、请求时序的机器化特征、服务端响应结构的稳定性。工具版本再怎么更新,通信还是要走HTTP协议,还是要发到Web服务上,必然有固定的"请求-响应"周期,这些底层特性不会变,也是检测规则设计时要抓住的锚点。
4. 流量分析实操:从抓包到特征提取
4.1 搭建一个可复现的抓包环境
如果是自己研究,不建议直接对着生产环境抓包。更稳的做法是搭一套隔离的测试环境。我用的方案是拿一台虚拟机跑模拟业务站点,装好PHP和Nginx,放一个带后门的模拟脚本作为靶标(自己搭的靶场,不要在生产上用)。然后在宿主机上跑Burp或者Wireshark做流量的中间人或者端口镜像,分别连接菜刀、蚁剑、冰蝎、哥斯拉,把每种工具的通信流量各抓一份存成pcap。
抓包环境有几个细节值得注意。第一,网络拓扑越简单越好,最好就是靶机一条链路,避免夹杂其他通信干扰。第二,如果是测试HTTPS流量,可以让工具直接连HTTP服务抓明文,也可以配一个本地CA让工具信任,专门抓加密连接看JA3指纹。第三,抓包时长要覆盖"连接握手—执行命令—断开连接"的完整过程,有些工具的关键特征只在握手阶段出现,漏了就得重抓。
4.2 用Wireshark和tshark提取关键请求
拿到pcap文件后,Wireshark图形界面适合单包细看,批量分析我还是习惯用tshark。筛选HTTP POST请求可以直接这样:
tshark -r webshell.pcap -Y "http.request.method == POST" \ -T fields -e frame.time -e ip.src -e ip.dst \ -e http.host -e http.user_agent -e http.file_data这样能把每条POST请求的时间、来源、UA、请求体都打出来,直接存成CSV或者过一遍grep看关键词。如果需要提取JA3指纹,tshark也支持:
tshark -r webshell.pcap -Y "tls.handshake.type == 1" \ -T fields -e ip.src -e tls.handshake.ja3输出里同一个源IP如果反复出现同一个JA3值,而且正好对应那个可疑的POST目标,就很值得深挖。另外,Wireshark里右键一条HTTP请求,选择"追踪HTTP流",可以直接看到完整的请求和响应内容,加密流量里的密文结构、明文流量里的PHP代码,在流视图里会非常清楚。
4.3 用Python脚本做批量特征比对
特征字段提取出来后,手动一条条看不是办法,写个简单脚本批量比对更高效。下面是一个简化示例,基于tshark导出的CSV做几个维度的判断:
import csv import re patterns = { "chopper": re.compile(r"@eval|assert\s*\(|base64_decode\s*\(.*\$_"), "antsword": re.compile(r"@ini_set|set_time_limit|display_errors|->\|"), "encrypted_body": re.compile(r"^[a-f0-9]{32,}$", re.IGNORECASE), } def analyze_row(row): body = row.get("http_file_data", "") score = 0 hits = [] for name, pat in patterns.items(): if pat.search(body): score += 1 hits.append(name) return score, hits # 伪代码:读取tshark导出的CSV,逐行分析这个脚本只是个框架,真实场景里要对body做解码、去重、熵计算,还要结合UA和JA3字段一起打分。核心思路是一样的:把多个弱信号叠加成一个可操作的评分,分数超过阈值再告警。如果你在流量分析平台里有现成的大数据能力,把这种评分逻辑写成UDF,效果会更好。
4.4 检测规则落地的经验与常见误报排查
落地检测规则的时候,最头疼的问题永远是误报。我踩过几个坑,列出来供参考。
第一个坑是高熵检测的误报。有些正常的API接口,比如加密JSON传输、文件上传的multipart数据,body的信息熵本来就高,如果拿熵值一刀切,会把这些接口全打成加密Webshell。解决办法是加基线:对该路径、该参数在一段时间内的熵值做统计,偏离基线到一定程度才算异常。
第二个坑是UA规则的时效性。蚁剑新版默认UA已经和Chrome一致,冰蝎4.0也开始支持UA随机化,一条UA规则撑不过三个月。所以UA只能算弱特征,别单独用它定位。
第三个坑是漏报。流量侧的特征可以定制,攻击者如果对工具做了二开,改了参数名和UA,特征就会漂移。遇到这种情况,更要靠行为侧兜底:一个平时几乎没人访问的脚本路径,突然开始被同一个源IP高频POST,这个行为本身就够可疑了,不管内容是不是加密。
排查的时候,我常用的流程是:告警出来后,先看告警对象是不是CDN、健康检查、爬虫这类已知来源,排除掉;然后拉同一个源IP在相近时间窗口内的完整会话,看请求序列是否和人交互一致;最后到对应主机上查Web访问日志,确认那个被请求的脚本是什么时候出现的、内容是否带后门特征。流量和主机两边一交叉,绝大部分告警都能定性。
5. 最后:一点个人体会
5.1 一个让我印象深刻的真实案例
有一次分析一批加密流量,怎么看都像正常API调用,请求有来有回,内容也是乱码,没有太多可疑点。后来发现所有请求都精确地指向同一个新出现的脚本文件,而且响应报文的大小高度一致,每次差异不超过几个字节。打开那个文件才发现,是哥斯拉的加密shell,密钥就藏在文件里,流量侧因为加了自定义参数名,常规规则根本没覆盖到。
这个案例让我印象很深。从那以后我就习惯先把"访问对象"的特征列出来,再回头看流量。如果某个路径是过去一个月都没出现过的,却在某天突然被大量POST,这个"目标突变"本身就是一个强信号,比任何加密识别规则都可靠。
5.2 我最想分享的几点经验
做了这么久的Webshell流量分析,我最大的感受是:工具特征的时效性比想象中短得多,几年前好用的特征今天可能已经失效。真正稳定的东西,反而是攻击者在交互过程中留下的行为痕迹——加密流量不可读,但它的形态、节奏、目标文件路径都是很难彻底抹掉的。
所以做流量分析,别把自己绑死在几条规则上,多花时间理解工具的运行逻辑,多看原始流量长什么样,比什么都有用。如果你刚开始接触这块,建议先从菜刀和蚁剑的明文流量看起,把常见特征记得滚瓜烂熟,再去看冰蝎、哥斯拉的加密通信,这时候你会更容易理解加密流量背后那些不变的东西。