ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Webshell流量分析实战:从pcap包到冰蝎加密通信的完整溯源

2026/9/15 2:21:05 拓冰建站 浏览量
Webshell流量分析实战:从pcap包到冰蝎加密通信的完整溯源 这几周一直在处理一批全流量镜像包起因是内部OA系统有一台服务器被安全设备报了“Webshell通信行为”的告警。这类告警隔三差五就会出现有时候是误报但这次不一样后来又叠加了一个页面被篡改的工单基本可以确定是真实攻击。我把整个分析过程整理了一下从流量包定位到Webshell的通信会话再到解开加密的交互内容最后往回溯源到文件落地的入口算是比较完整的一次Webshell流量分析。先说结论Webshell的检测和排查不能只盯主机侧。主机侧文件查杀可以删掉已知样本但面对加密流量、免杀样本、无字母数字Webshell这类东西静态特征很容易失效。流量侧反而是最后一道比较可靠的防线——只要攻击者还要把命令执行结果传出去就一定会在网络包上留下痕迹。这篇文章我就按当时的操作顺序说说我是怎么一步步从pcap包把Webshell挖出来的顺手把踩过的坑和常用命令也一起整理出来。1. 事件现场从告警到流量包1.1 这次分析开始的起点我当时拿到的信息其实很少一台跑着Nginx PHP的OA业务服务器内网IP记作10.10.16.11告警时间是某个周四凌晨3点17分告警类型是“Webshell通信”完了就是首页被换成了攻击者的页面运维那边应急把服务先停了。老实讲这种信息量等于没有。告警只说有通信没说哪个进程、哪条连接、执行了什么命令。如果直接上服务器翻Web目录很可能什么都找不到——攻击者上传的Webshell文件名完全可以伪装成正常静态资源或者藏在上传目录某个随机命名的文件里。所以我的第一步不是登录服务器而是先去取流量包。我们这边因为平时就有全流量审计的需求核心服务器的出口交换机端口做了镜像流量会分流到抓包服务器上落盘成pcap。我直接取了一个从告警前48小时到告警后24小时的包大概3.2GB。这个时间范围是有讲究的往前留48小时是为了覆盖Webshell落地前的攻击链往后留24小时是为了确认攻击者有没有后续动作。太短了容易漏上游太长了Wireshark根本打不开。1.2 拿到流量包后我先给自己定了三个问题在开始动手前我习惯先把分析目标和思路列清楚不然三五个G的包随便点几个小时就没了。当时我给自己定了三个必须回答的问题第一Webshell通信的会话在哪服务端IP和客户端IP分别是谁第二通信内容是什么攻击者执行了哪些命令有没有拖数据出去第三这个Webshell是怎么上传到服务器上的对应的漏洞入口在哪。这三个问题对应的其实就是三个分析阶段先筛选定位再解密还原最后交叉溯源。后面的操作完全是按照这个思路推进的。很多人做流量分析容易一头扎进Wireshark里瞎翻我觉得不对流量包分析本质上是一个“减噪”的过程先把大量正常业务流量扔掉剩下的可疑内容才值得花时间深入研究。2. 把可疑会话“捞”出来流量筛选方法论2.1 先过滤POST请求为什么要从这里下手传统Webshell通信不管是菜刀、蚁剑还是手工POST命令绝大多数走HTTP POST因为要给服务端传递执行参数和数据。GET请求即使有也多半是探测性的真正的命令交互不会靠URL参数去传大段内容。所以我拿到pcap后的第一件事就是把HTTP POST请求全部捞出来看分布。我习惯用tshark做第一轮粗筛因为纯命令行在大文件处理上比Wireshark图形界面稳很多。大概是这样tshark -r traffic.pcap -Y http.request.method POST -T fields -e frame.time -e ip.src -e ip.dst -e http.host -e http.uri -e http.content_length | head -n 100实际输出会有大量字段所以我一般会先做一个统计确认POST请求的源IP和URL分布tshark -r traffic.pcap -Y http.request.method POST -T fields -e ip.src -e http.host -e http.uri | awk -F\t {print $1 - $2$3} | sort | uniq -c | sort -nr | head -n 30这一步帮我把范围缩小得非常快。统计结果里前十名基本都是正常业务接口像 /api/login、/api/notice/list 等等但排在中间有一个很扎眼的条目客户端 10.10.32.7 在凌晨时段向 /api/upload/temp.php 发了多次POST请求请求体大小在1KB到3KB之间。正常用户不会在凌晨3点调用上传接口而且我记得这个路径从来不在OA应用的路由表里。到这里重点嫌疑人基本就浮出来了。2.2 基线比对先知道“正常”长什么样筛选异常光靠看数量还不够我通常会再做一个基线比对否则很容易误判。基线的意思是在正常时间段比如工作日白天和正常源IP段里同路径的请求特征是什么样。我当时把凌晨的所有POST请求单独导出来看了一遍时间段集中、客户端IP集中、URL集中、请求体大小波动不大。而白天同路径几乎没有请求。这说明它不像正常业务调用——正常业务调用的源IP是分散的数据大小是随业务变化的不会保持这么整齐的“节奏感”。另外我还对比了响应包的状态码和大小这个可疑路径的响应码是200而且响应体大小和请求体大小存在强关联请求大一点响应就大一点。Webshell把命令执行结果回显出来的时候就是这样服务端返回的内容往往就是你刚传进去的参数的执行结果所以响应大小会跟着请求走。这个特征在自动化检测里也很好用不需要解密也能判断个七八成。2.3 把可疑会话锁死在具体TCP流编号上确认了源IP和URL后我从Wireshark里直接对 ip.addr 10.10.32.7 ip.addr 10.10.16.11 http 过滤然后把POST到 /api/upload/temp.php 的包一条条选出来追踪TCP流。这里有个小技巧Wireshark的Follow TCP Stream功能会直接把整个会话的请求和响应拼在一起非常适合查看交互全过程。我当时连续追踪了三条流每条流的模式几乎一样客户端POST一串看起来像Base64的数据服务端立刻返回另一串Base64。其中第一条流里请求体Base64解码后能隐约看到几个PHP函数名这几乎就是实锤了。到这一步Webshell通信会话已经完整锁定。我把这几条TCP流的编号记下来后面所有深入分析全部围绕这些流展开。这里也提醒一句如果流量包特别大建议直接用小包范围导出不要带着全流量做深入分析。我当时用tshark先做了一个二次切片tshark -r traffic.pcap -Y tcp.stream 0 -w temp.pcap实际用的时候是把确认的流号拿来做筛选比如editcap traffic.pcap suspicious.pcap tcp.stream128这样后面打开的就只是一个几百KB的干净文件操作流畅度完全不一样。3. 拆解Webshell通信细节3.1 识别管理工具这次遇到的是冰蝎3.x的行为指纹Webshell管理工具不少常见的有蚁剑、冰蝎、哥斯拉等它们通信特征差异很大。能识别出具体哪款工具后面的解密和分析会轻松很多。我这次遇到的流量有几个非常明显的特征第一POST请求的Content-Type是 application/x-www-form-urlencoded但请求体内容完全不是表单结构而是一大段Base64。第二请求头里的Accept是 application/octet-stream正常浏览器发表单请求不会要二进制流。第三User-Agent虽然是Mozilla/5.0但里面语言字段和操作系统字段存在矛盾——一个声称Windows中文版的UA后面跟着一个Linux的Sec-Fetch参数组合明显是伪造的。第四请求头里没有Referer和Cookie正常用户访问内部系统不可能什么上下文字段都没有。这几个特征叠加起来基本指向冰蝎Behinder3.x。冰蝎3.x默认使用AES加密通信密钥通过密钥交换或预置在脚本里每次请求和响应的payload都是密文所以我在第一条流里看到的Base64解码后“像PHP函数名”的内容其实是冰蝎3.x早期的特征。到了3.x后期版本AES加密会把请求体整个加密单看Base64解不出来内容只能看到密文。我还顺手整理了一下常见工具的流量特征对比方便以后遇到同类问题时快速判断工具请求特征响应特征备注中国菜刀固定UA“百度蜘蛛”或空UAPOST内容有eval明文返回执行结果特征最明显静态规则好拦蚁剑User-Agent带蚁剑标识响应压缩或取随机字符串有固定PHP代码片段冰蝎3.xAES加密Accept为application/octet-stream无Cookie响应也是AES密文包大小随命令输出变化必须解密才能还原命令哥斯拉加密流量带JAVA反序列化特征或PHP类型标记响应带固定分隔符各版本加密方式不同3.2 追踪流还原交互加密流量也一样能解锁定了冰蝎3.x接下来就是解密。冰蝎3.x的密钥要么是预设在服务端脚本里要么是通过请求头里的X-Requested-With字段做密钥协商version 3.0.3。我去服务器的Web目录下找到了对应的temp.php文件里面果然有一段AES加解密的逻辑密钥硬编码在文件里。拿到密钥后解密就变成了一道纯粹的体力活。我用的是一段简单的Python通过CyberChef或OpenSSL做AES解密都可以但批量解密几十条流时写脚本更省事。核心解密思路是从流量包提取出POST请求体Base64Base64解码后用AES-128-ECB冰蝎默认用ECB模式解密再解压获取明文后通常还有一层gzip解压。完整的解密脚本逻辑大概长这样密钥和模式视样本调整import base64 from Crypto.Cipher import AES def decrypt_behinder(payload_b64, key): key_bytes key.encode(utf-8) cipher AES.new(key_bytes, AES.MODE_ECB) data base64.b64decode(payload_b64) plain cipher.decrypt(data) return plain.rstrip(b\x00)实际解出来的内容里有几次是命令执行请求明文结构大概是函数名加参数的形式还有一些是文件管理操作比如读取 /www/wwwroot/config.php 的内容。攻击者把数据库密码文件也读出来了但流量里没看到大批量导出数据的痕迹说明他还在信息收集阶段就被告警打断了。这里有一个很关键的实操心得不要只解密单个请求要把整条TCP流中的请求和响应成对解开。因为Webshell管理工具通常是一问一答请求是命令响应是执行结果。单独看请求你只知道他发了什么命令单独看响应你只看到结果只有配对还原才能完整还原攻击者做了什么。我当时把7条流的请求和响应全部解出来按时间排序后整个攻击行为就非常清晰了。3.3 无字母数字Webshell这类样本为什么难找这次分析过程中还有一个插曲我拿temp.php文件去WEB目录里做静态检查时发现这个文件本身就是一个典型的无字母数字Webshell。所谓无字母数字Webshell就是文件内容里几乎看不到英文字母和数字一般只有 $、_、.、[]、() 这类符号。它通过PHP的异或、自增、字符串解析等特性在运行时动态生成函数名和字符串再调用执行函数。比如用两个字符串变量的异或结果拼出“assert”这个词。这种写法可以轻松绕过一大票基于正则匹配的WAF和静态查杀因为它们匹配的是eval、assert、system这些明文字符串而异或生成的内容在文件里根本不出现这些词。我当时打开那个文件整页内容就是一堆 $__ 和常量字符在互相异或最后一行才有一个花括号加括号的动作。如果只靠安全狗或D盾去扫这个目录确实很容易漏掉。但流量侧不一样无论你文件写得再隐蔽只要你向服务器发请求或者服务器向外部回显数据就一定会产生TCP连接和HTTP交互。我在流量分析过程中根本不需要看文件内容是什么只凭通信行为就把它揪出来了这也就是为什么我一直强调流量分析在Webshell排查里是不可替代的一道工序。4. 回到根因Webshell是怎么落地的4.1 日志交叉验证找到上传入口Webshell通信分析完了但应急响应还没结束。如果不找到上传入口攻击者随时可以从同一条路再进来一次。所以我拿着 /api/upload/temp.php 这个路径去翻Web服务器的访问日志。访问日志我翻的是nginx的access.log按天切分的。我查到 temp.php 文件首次出现的时间再往前回溯了3天搜索同源IP 10.10.32.7 访问过的所有URL。很快就发现了一条完整的时间线攻击者先访问了 /api/file/upload 接口上传了一个 .jpg 文件文件内容其实是一段PHP代码随后访问了 /api/upload/temp.php 配合参数进行包含执行。这个攻击链的原理其实不复杂上传接口对文件类型校验不严把带PHP代码的图片存到了 /api/upload/ 目录下而 /api/upload/temp.php 本身是一个存在文件包含漏洞的残留脚本攻击者可以通过参数包含本地文件让图片里的PHP代码以PHP脚本方式执行。上传一个图片马 一个本地包含点就形成了一条完整的Webshell攻击链。4.2 修复与清理两端一起堵排查到这里修复方案已经很明确了。我当时处理时是分主机侧和流量侧两步走。主机侧删除 temp.php 和同目录下的图片马杀掉Webshell落地的关联进程检查是否有其他会话还挂着检查 /tmp、/var/tmp、上传目录的近期新增文件修改数据库密码和管理后台密码升级或下线那个有文件包含漏洞的旧脚本。流量侧在WAF上临时封禁源IP 10.10.32.7增加针对 /api/upload/ 目录的POST请求体审计规则对 /api/file/upload 接口增加返回值和文件内容双重校验规则把全流量镜像的保留周期从3天调整到7天防止下次回溯时流量已经被覆盖掉把无字母数字Webshell的检测规则加到流量审计设备上。这里要特别强调一个容易被忽视的动作修复完成后要再抓48小时流量做一次复查确认源IP没有换其他路径继续进来同时确认业务正常流量没有因为新加的WAF规则被误杀。我见过太多团队清理完就收工结果第二天攻击者换了UA和路径又进来了前面的工作全部白费。5. 常见问题与排查速查5.1 流量包分析的五个典型坑第一个坑是流量包太大导致Wireshark卡死。我习惯先用 tshark editcap 切片把确认的可疑TCP流单独导出再分析。第二个坑是只盯HTTP明文忽略HTTPS流量。现在很多Webshell通信也用HTTPS碰到这种情况优先看TLS握手和证书指纹或者配合代理、SSL日志解密还原。第三个坑是不做时间基线只看单条请求。很多Webshell通信频率不高一天只有几次单看不显眼但放到时间轴上凌晨两三点主机有规律的对外请求跟业务高峰期完全不同。第四个坑是重响应不重请求。有些加密Webshell的响应内容比请求更能体现问题比如响应熵值偏高、包长分布异常这些都是加密通信的指纹。第五个坑是只分析不取证。分析过程中所有关键流、解密后的明文、时间戳、IP五元组都要记录下来固定成报告后面应急追溯和复盘都靠这些证据。5.2 常用过滤表达式与命令速查很多朋友在Webshell流量分析时问我有没有一套可以直接抄的过滤命令我把这次用到的和平时常用的整理成了一张速查表。目的Wireshark/tshark过滤表达式过滤HTTP POST请求http.request.method POST过滤访问指定路径http.request.uri contains temp.php过滤指定IP交互ip.addr 10.10.32.7 ip.addr 10.10.16.11筛选大包体请求frame.len 800 http.request.method POST按数据包长度排序找异常直接点length列排序观察是否有规则的大包追踪TCP流右键Follow TCP Stream或 tshark -z follow,http,ascii分离指定流号editcap input.pcap output.pcap tcp.stream128提取请求体输出到文件tshark -r x.pcap -Y http.request -T fields -e http.file_data另外关于抓包层面的建议做全流量审计时抓包服务器的网卡建议用多队列和PF_RING或DPDK这类高性能方案否则流量一大就容易丢包。丢了包之后Webshell的请求和响应不完整解密和还原命令会出现截断有时候还会导致会话分析完全失败。我们这次3.2GB的流量包用的就是双万兆网卡分流抓包实测没丢包这也是后面能完整解密的基础。5.3 排查结束后的处置顺序建议最后把这次事件中我最终执行的处置流程整理出来供有同样需求的朋友参考。顺序很重要顺序错了容易破坏证据或者让攻击者有二次渗透的机会。在防火墙上临时封禁可疑源IP但不要重启服务保留现场。抓取并备份全流量至少覆盖告警前48小时到处置前的时间段。分析流量包定位Webshell通信会话锁定关联IP和TCP流。查看服务器上的Webshell文件记录文件路径、修改时间、hash值。检查该文件创建时间前后的日志找到上传入口和攻击链。删除Webshell文件清理可疑进程和计划任务修改相关口令。修复漏洞调整WAF规则和文件上传校验策略。重新抓包48小时验证恶意通信已完全消失。这套流程不适合所有环境但思路是可以通用的先控传播、再留证据、后做分析、最后才修复。先修复后分析是最忌讳的因为一旦文件被删、日志被清很多线索就永远消失了。我个人在几次实战中最深的体会是Webshell流量分析的关键不在于你会用多少工具而在于你有没有一套稳定的“减噪—特征识别—解密还原—交叉验证”的思考路径。流量包本身不会说谎它把所有行为都记录下来只看你愿不愿意花时间去捞。这次从3.2GB的包里筛出7条可疑TCP流再把这些流解密成攻击者完整的操作记录整个过程下来比单纯在服务器上翻马要踏实得多。希望这篇文章能帮你少走一些弯路。