0. 实验目标与结论摘要
本实验针对哥斯拉(Godzilla)PHP 动态载荷完成以下工作:
- 搭建 phpStudy、Pikachu、本地 HTTP 代理和 Wireshark 回环抓包环境。
- 从完整 PCAP 中定位文件上传、资源访问、测试连接、会话初始化、基础信息获取和命令执行。
- 从 URI、参数、正文、响应边界、报文长度和时序中提取入侵检测特征。
- 对服务端 PHP 样本进行代码审计,确认参数、密钥、Session 载荷及响应封装方式。
- 完成请求和响应的 XOR/Base64/GZip 离线还原,解析控制过程。
- 将实验结果转化为 Wireshark 过滤器、Suricata 规则、YARA 规则和蓝队处置要点。
本次抓包形成的关键证据链是:
|
|
需要区分三类证据:
| 类别 | 示例 | 适用范围 |
|---|---|---|
| HTTP 外层可见特征 | URI、pass123=、正文长度、固定响应边界 |
无需解密即可检测 |
| 解密后协议语义 | test、g_close、getBasicsInfo、execCommand |
用于调查与控制过程还原 |
| 可修改的样本 IOC | 文件名、密码参数、密钥、MD5边界 | 仅适用于相同配置样本 |
1. 搭建测试环境
1.1 实验拓扑
|
|
网站、客户端和抓包程序位于同一台 Windows 主机,通信不会经过 WLAN 网卡,因此 Wireshark 应选择 Npcap 提供的:
|
|
抓包开始时不设置过窄的捕获过滤器,先保留完整回环流量,再通过显示过滤器定位目标,避免遗漏代理转发、TCP 重组或非标准端口数据。
1.2 软件与用途
| 组件 | 用途 |
|---|---|
| Windows + phpStudy | PHP Web 运行环境 |
| Pikachu | 本地文件上传靶场 |
| Wireshark / TShark | 抓包、TCP 重组、字段提取 |
| 本地 HTTP 代理 | 对比浏览器请求与后端请求 |
| Suricata | 网络检测规则验证 |
| YARA | Web 目录静态文件检测 |
| Sysmon / Windows 日志 | 监控 Web 服务进程派生系统命令 |
| CyberChef / 离线脚本 | 编码识别与解密验证 |
参考项目:
1.3 样本配置
靶场只能解析 PHP,因此实验选择 PHP 动态载荷:
|
|

服务端文件扩展名必须与 Web 容器能力匹配。例如将 JSP 文件放入只支持 PHP 的站点时,可能显示源码或被作为静态文件下载,这不代表 JSP 已被执行。蓝队也可以利用“脚本扩展名与实际容器不匹配”发现配置错误或源码泄露。
1.4 抓包证据
| 项目 | 数值 |
|---|---|
| 文件 | gsl.pcapng |
| 抓包时间 | 2026-07-22 16:19:19 ~ 16:23:58 |
| 时长 | 279.678120 秒 |
| 数据包数量 | 约 12,000 |
| 文件大小 | 9,422,236 字节 |
| PCAP SHA-256 | D9585F0770551D64E9049068D06360594D6A77DCE5F992C13038C66B0D09138D |
为保证实验可复核,还应同时保存:
- 样本文件副本及 SHA-256;
- Web 访问日志和错误日志;
- 测试前后的 Web 目录文件清单;
- 操作时间记录;
- 管理客户端版本、载荷和加密器名称。
2. Wireshark 流量分析
2.1 工作流程
第一步:确认 HTTP 服务与代理端口
|
|
如果 HTTP 运行在其他端口,应使用“分析 → 解码为 → HTTP”。不能仅因为 Protocol 列显示 TCP,就判断没有 HTTP 流量。
第二步:流量过滤
|
|

可添加以下列:
|
|
第三步:定位文件上传
|
|

展开:
|
|
记录 filename、multipart 字段名、声明的 Content-Type、文件真实开头、请求帧、响应帧以及代理前后的字段变化。
第四步:定位上传后的资源访问
|
|
GET 返回 200 且正文为空,只能证明资源可访问或被 PHP 引擎处理,不能单独证明已经发生控制行为。
第五步:定位持续控制通信
|
|
结合已知样本参数:
|
|

重点观察:
- 是否持续访问同一低访问量脚本;
- 参数名是否固定;
- 参数值是否为 Base64、URL 编码或高熵数据;
- 初始化请求与后续控制请求的长度差异;
- 响应是否具有稳定的首尾边界;
- 是否缺少正常页面的 Referer、Cookie 或浏览器访问链。
第六步:关联请求与响应
选择请求帧,在 HTTP 协议树中查看:
|
|
例如定位帧 10714 的响应:
|
|
由响应返回请求:
|
|
第七步:追踪 TCP 流并关联主机证据
单独查看目标流:
|
|
网络证据最终需要与以下主机证据闭环:
|
|
2.2 上传阶段
2.2.1 浏览器到代理
| 项目 | 内容 |
|---|---|
| 请求帧 | 4971 |
| 响应帧 | 5778 |
| TCP 流 | 105 |
| 方向 | 127.0.0.1:19693 → 127.0.0.1:8080 |
| URI | http://localhost/pikachu-master/vul/unsafeupload/clientcheck.php |
| Content-Length | 1170 |
| 文件名 | test1.jpg |
| 声明类型 | image/jpeg |
| 文件数据 | 876 字节 |


2.2.2 代理到 Web 服务器
| 项目 | 内容 |
|---|---|
| 请求帧 | 5772 |
| 响应帧 | 5776 |
| TCP 流 | 131 |
| 方向 | 127.0.0.1:60648 → 127.0.0.1:80 |
| URI | /pikachu-master/vul/unsafeupload/clientcheck.php |
| Content-Length | 1170 |
| 文件名 | test1.php |
| 声明类型 | image/jpeg |
| 文件数据 | 876 字节 |


两个请求使用相同 multipart boundary,文件数据长度和内容一致,但文件名从 test1.jpg 变为 test1.php。帧 5776 返回 HTTP 200,HTML 中出现:
|
|
从帧 5772 提取的文件:
|
|

该证据说明服务端没有对最终文件名、真实内容和上传目录执行权限进行联合控制。
2.3 上传后的访问
| 请求帧 | 响应帧 | TCP流 | 方向 | 结果 |
|---|---|---|---|---|
| 6408 | 6498 | 106 | 浏览器→代理→浏览器 | HTTP 200,正文长度0 |
| 6494 | 6496 | 152 | 代理→Web服务器→代理 | HTTP 200,正文长度0 |
过滤:
|
|
空响应与样本源码一致:普通 GET 没有携带 pass123 参数,不会进入处理分支。
2.4 测试连接
测试连接位于 TCP 流 194:
|
|
| 请求帧 | 响应帧 | 请求体长度 | HTTP结果 | 解密结果 |
|---|---|---|---|---|
| 8699 | 8701 | 52538 | 200,响应长度0 | 初始化动态载荷 |
| 8705 | 8707 | 40 | 200,响应体64字节 | methodName=test |
| 8759 | 8761 | 44 | 200,响应体64字节 | methodName=g_close |
行为顺序:
|
|
首次请求只把动态载荷写入 PHP Session,不直接输出数据,因此帧 8701 的 Content-Length 为 0。
2.5 正式会话初始化
正式会话位于 TCP 流 214:
|
|
| 请求帧 | 响应帧 | 请求体长度 | HTTP结果 | 解密结果 |
|---|---|---|---|---|
| 9847 | 9849 | 52538 | 200,响应长度0 | 初始化动态载荷 |
| 9853 | 9855 | 40 | 200,响应体64字节 | methodName=test |
| 9871 | 9873 | 52 | 200,响应体1208字节 | methodName=getBasicsInfo |
行为顺序:
|
|
测试连接和正式会话都出现约 52 KB 的初始化请求,但最后一个方法不同:测试连接以 g_close 结束,正式会话继续执行 getBasicsInfo。
2.6 命令执行
命令执行位于 TCP 流 229:
|
|
| 项目 | 内容 |
|---|---|
| 请求帧 | 10714 |
| 响应帧 | 10740 |
| 请求时间 | 2026-07-22 16:23:21.244529 |
| 请求体长度 | 218 字节 |
| 响应编码体长度 | 1612 字节 |
| 解密后响应长度 | 5811 字节 |
| HTTP状态 | 200 OK |
| 请求至响应时间 | 约112.7毫秒 |
| 解密方法字段 | methodName=execCommand |

帧 10714 解密后确认执行的是 Windows 网络配置查询,帧 10740 解密后包含:
|
|

原始 HTTP 正文中不能直接搜索命令明文,因为命令参数经过 GZip、循环 XOR、Base64 和 URL 编码。网络检测应先识别通信外层,协议解密用于后续调查确认。
3. 定义流量特征字符串
3.1 样本专用 IOC
|
|
这些值来自本次样本配置。修改文件名、路径、密码或密钥后,IOC 会随之变化,不能将其视为所有哥斯拉样本的通用特征。
3.2 上传行为特征
高置信度组合:
|
|
静态代码字符串:
|
|
单独匹配 image/jpeg、Base64 或 <?php 容易产生误报,应将文件名、MIME、真实内容和代码语义组合。
3.3 通信外层特征
|
|
所有有效响应均采用相同边界:
|
|
该边界在帧 8707、8761、9855、9873 和 10740 中重复出现,是本实验样本的强网络指纹。
3.4 通用行为特征
比固定字符串更稳定的行为包括:
|
|
推荐的告警评分:
| 特征 | 建议权重 |
|---|---|
| 上传目录内脚本收到POST | 高 |
| 脚本伪装成图片上传 | 高 |
| Web服务进程派生命令解释器 | 极高 |
| 固定响应边界同时匹配 | 高 |
| 单纯Base64或高熵 | 低 |
单纯application/x-www-form-urlencoded |
低 |
4. 检测应用
4.1 Wireshark 过滤器
定位上传:
|
|
定位全部目标 POST:
|
|
结合参数:
|
|
定位固定响应:
|
|
定位伪装上传:
|
|
4.2 Suricata 实验规则
以下规则用于本实验 PCAP 验证。投入生产前应调整 $HOME_NET、URI、SID、正文检查上限和误报白名单。
检测伪装成 JPEG 的 PHP 上传:
|
|
检测上传目录中的可疑 PHP POST:
|
|
检测固定响应边界:
|
|
http.request_body 和 http.response_body 的可检查大小受 Suricata 配置中的 request-body-limit 与 response-body-limit 影响。规则未告警时,应先确认正文是否被完整检查。
4.3 YARA 静态检测
|
|
生产环境应使用“已知良好文件基线 + 新增/修改文件检测 + YARA”组合,而不是只扫描固定文件名。
5. 加密流量解密
5.1 密钥获取
蓝队获取密钥的推荐顺序:
- 隔离并复制可疑服务端脚本,进行静态代码审计。
- 检查客户端配置、测试记录或合法授权材料。
- 根据公开实现确认算法、字符编码、压缩及响应封装顺序。
- 使用已知明文验证推导结果,避免无边界暴力破解。
本次样本中:
|
|
三个变量分别表示:
| 变量 | 作用 |
|---|---|
$pass |
HTTP POST 参数名,即 $_POST['pass123'] |
$payloadName |
PHP Session 中缓存动态载荷的键名 |
$key |
请求和响应使用的16字符循环XOR密钥 |
密钥值为密码 MD5 结果的前 16 个十六进制字符:
|
|
响应边界来自:
|
|
所以:
|
|

5.2 代码审计得到的协议流程
服务端逻辑可抽象为:
|
|
循环索引:
|
|
XOR 可逆,因此请求和响应使用相同函数处理。
5.3 离线解密代码
处理 Wireshark“追踪 HTTP 流”导出的请求值或响应正文。
|
|
正确处理顺序:
|
|
如果顺序错误,常见表现是 Base64 格式错误、解密结果无固定字段或 GZip magic 不正确。
5.4 控制过程还原
| 阶段 | TCP流 | 请求帧 | 解密结果 |
|---|---|---|---|
| 测试连接 | 194 | 8705 | methodName=test |
| 关闭测试 | 194 | 8759 | methodName=g_close |
| 会话测试 | 214 | 9853 | methodName=test |
| 基础信息 | 214 | 9871 | methodName=getBasicsInfo |
| 命令执行 | 229 | 10714 | methodName=execCommand |
帧 10740 经边界剥离、Base64 解码、XOR 和 GZip 解压后为 5811 字节,内容与实验操作产生的 Windows 网络配置回显一致。
6. 代码审计
6.1 上传功能审计
帧 4971 与 5772 表明,同一个 876 字节文件在代理前名为 test1.jpg,代理后名为 test1.php,服务端仍接受 Content-Type: image/jpeg。由此可以判断:
|
|
修复建议:
- 所有上传校验必须在服务端完成。
- 使用允许列表限制规范化后的最终扩展名。
- 使用
finfo、图像解析和重新编码验证真实图片内容。 - 由服务端生成随机文件名。
- 将上传目录放在 Web 根目录之外。
- 如必须公开访问,关闭上传目录中的脚本执行权限。
- 记录原始文件名、最终文件名、哈希、用户、源地址和处理结果。
6.2 服务端样本审计
关键数据流:
|
|
危险点:
| 代码行为 | 风险 |
|---|---|
@error_reporting(0) |
隐藏运行异常,降低可见性 |
@set_time_limit(0) |
支持长时间执行 |
$_POST[$pass] |
从固定POST参数接收控制数据 |
base64_decode + XOR |
混淆通信内容 |
$_SESSION[$payloadName] |
在Session中缓存动态载荷 |
eval($payload) |
直接执行动态PHP代码 |
@run($data) |
将控制操作交给动态载荷 |
| 固定MD5边界 | 帮助客户端定位正文,同时形成检测指纹 |
代码层面的核心恶意语义不是 Base64 或 XOR,而是“不可信输入经过还原后进入动态执行,并能够调用系统能力”。静态检测应结合数据流、危险函数和持久会话,避免对正常编码业务产生大量误报。
7. 蓝队操作点
7.1 网络侧
- 在反向代理、WAF 或 TLS 终止点保留 HTTP 请求元数据。
- 对上传目录中的脚本 POST 建立高优先级告警。
- 统计每个 URI 的客户端数、User-Agent 数和 Referer 分布,寻找低频孤立端点。
- 将正文熵和长度序列作为评分项,不单独定性。
- 使用 Suricata 检查上传正文、固定参数和响应边界。
- 使用 Zeek 或 SIEM 聚合“同一 URI 短时间连续 POST”。
- 将请求帧、响应帧、TCP流、时间、URI和正文长度写入调查记录。
7.2 主机侧
重点监控以下进程链:
|
|
即使执行的是普通网络信息查询,只要父进程是 Web 服务进程,也应作为高风险行为调查。
文件侧:
- 对 Web 根目录和上传目录建立已知良好哈希基线;
- 实时监控新增或修改的
.php文件; - 检查图片、压缩包和临时文件中的脚本内容;
- 使用 YARA 检测动态执行、编码和 Session 缓存组合;
- 保存创建时间、修改时间、所有者、ACL 和哈希;
- 将 PCAP 时间与 Web 日志、Sysmon Event ID 1 和文件事件关联。
7.3 应急响应
发现高置信度 WebShell 后:
- 隔离受影响服务器或限制入口访问。
- 保存 PCAP、Web 日志、进程、网络连接、内存和可疑文件副本。
- 计算证据哈希并建立时间线。
- 搜索同目录、同哈希、同 URI 和相同代码片段。
- 排查 Web 服务账号权限、计划任务、启动项和横向连接。
- 修补上传功能或其他初始入口。
- 从可信镜像恢复,不能把删除单个脚本视为处置完成。
- 轮换应用密钥、数据库凭据和服务账号凭据。
- 将 IOC 和行为特征回灌至 NDR、WAF、SIEM、EDR 与文件完整性监控。
8. 总结
本实验从文件上传、HTTP 通信、协议解密、代码语义和主机行为五个视角还原了哥斯拉 PHP XOR/Base64 样本的控制过程:
|
|
样本专用检测可以使用:
|
|
更稳定的检测思路是:
|
|
网络规则用于快速发现,代码审计用于确认恶意语义,协议解密用于还原控制过程,主机遥测用于证明执行,文件基线和应急处置用于最终闭环。