- 网络安全
- 漏洞扫描
- 渗透测试
- 应用安全
- CLI
【免费下载链接】wpscan
WPScan WordPress security scanner. Written for security professionals and blog maintainers to test the security of their WordPress websites. Contact us via contact@wpscan.com
导读
本文以 WPScan 仓库中 Crosswinds Blocks 插件的changelog.md测试夹具为切入点,系统讲解 WPScan 如何利用"动态查找器(Dynamic Finder)"从插件自带的变更日志文件中自动提取版本号。读完本文,你将掌握dynamic_finders.yml中 ChangeLog 类配置的完整语义、BodyPattern 匹配正则的编写规则、被动/主动两种检测模式的差异,以及如何通过仓库内的测试用例验证这一识别链路。
一、场景背景:为什么一份 changelog 会成为版本指纹
WordPress 插件在发布时通常会在根目录附带readme.txt、changelog.md(或changelog.txt)等元数据文件。其中changelog文件会按版本号分段记录每次更新的内容,例如本文主角 crosswinds-blocks/change_log/changelog.md 中记录的三个版本:
| 版本号 | 发布日期 | 变更要点 |
|---|---|---|
| 1.0.2 | 2023-11-20 | 修复部分文件未进入 SVN 仓库的问题 |
| 1.0.1 | 2023-11-18 | 修复版权块问题;更新 Crosswinds Framework 与 Crosswinds Blocks 的品牌信息 |
| 1.0 | 2023-10-28 | 首次发布到仓库 |
从结构上看,该 changelog 的版本条目遵循统一模式——以##开头的 Markdown 二级标题承载版本号(如## 1.0.2 - November 20, 2023)。这种"机器可读、规律稳定"的格式恰好为 WPScan 提供了理想的版本指纹:只要插件目录下存在该文件,扫描器就能通过正则匹配定位版本号。
注意:该 changelog 存放在插件
change_log/子目录下(文件路径为change_log/changelog.md),而 WPScan 的配置中使用的路径为changelog.md,二者之间的对应关系由实际目标站点上插件目录结构决定,详见下文第三节。
二、核心机制:Dynamic Finder 与版本提取的完整链路
WPScan 并不为每个插件手写版本探测逻辑,而是维护一张集中式的查找器配置表 spec/fixtures/db/dynamic_finders.yml,运行时按需为每个插件"动态生成"查找器类。Crosswinds Blocks 的配置位于该文件的第 31618-31627 行:
crosswinds-blocks: ChangeLog: class: BodyPattern path: changelog.md pattern: !ruby/regexp /\#\# (?<v>\d+\.[\.\d]+)/ version: true Readme: path: - readme.txt - readme.md这段配置揭示了版本提取链路的关键要素:
- 查找器名称
ChangeLog:声明这是一个基于变更日志文件的查找器。 class: BodyPattern:指定实现类。BodyPattern 是 WPScan 的动态版本查找器之一(对应源码 lib/wpscan/finders/dynamic_finder/version/body_pattern.rb),其类注释明确指出:它"典型用于响应体并非 HTML 文档、无法使用 XPath 的场景"——changelog 是纯文本/Markdown 文件,正好落入此类。path: changelog.md:主动(aggressive)模式下请求的相对路径,拼接在插件 URL 之后。pattern:核心版本提取正则。/\#\# (?<v>\d+\.[\.\d]+)/的含义是:匹配以##开头、后跟形如1.0.2或1.0.2.1的版本串,并通过命名捕获组(?<v>...)捕获版本号。这正是为了匹配前文 changelog 中## 1.0.2 - November 20, 2023这类标题行。version: true:标记该查找器产出的是"版本"而非其他类型的发现项。
版本如何被消费
匹配到的版本号会进入 Finder 基类 的create_version方法,被包装为WPScan::Model::Version对象,并携带found_by(来源标识)与confidence(置信度)两个元数据字段。BodyPattern 类在 child_class_constants 中为CONFIDENCE设定的默认值是60(可在配置中用confidence键覆盖)。
源码级匹配逻辑
BodyPattern 的find方法(body_pattern.rb)逻辑非常简洁但严谨:
def find(response, _opts = {}) return unless response.code != 404 && response.body =~ self.class::PATTERN create_version( Regexp.last_match[:v], interesting_entries: ["#{response.effective_url}, Match: '#{Regexp.last_match}'"] ) end关键点有三:
- 先排除 404:文件不存在(404)时直接返回
nil,不产生误报; - 正则匹配响应体:使用配置注入的
PATTERN常量(即上文的/\#\# (?<v>\d+\.[\.\d]+)/)扫描文件内容; - 记录来源证据:将
effective_url与完整匹配结果一并写入interesting_entries,方便审计"版本号到底是从哪个 URL、哪段文本提取出来的"。
三、被动与主动:两种检测模式的差异
依据 spec/lib/finders/dynamic_finder/plugin_version_spec.rb 中的测试约定,带path配置的查找器(如 Crosswinds Blocks 的 ChangeLog)行为如下:
| 模式 | 行为 | 测试断言 |
|---|---|---|
| 被动(passive) | 仅分析首页与 404 页的响应体,不会请求changelog.md | 当配置了path时,被动模式直接返回nil(见 L68-L73) |
| 主动(aggressive) | 显式请求plugin.url(config['path']),即目标站点的changelog.md路径,再对其响应体做正则匹配 | 命中则返回WPScan::Model::Version;未命中返回nil |
这一设计的原因是:changelog 等内部文件不会出现在首页 HTML 中,被动扫描(只读首页/404 页)无法获取其内容,只有主动扫描才值得发出额外请求去抓取该文件。测试代码通过stub_request(:get, plugin.url(config['path'])).to_return(stubbed_response)模拟响应(L151-L153),夹具文件正是spec/fixtures/dynamic_finders/plugin_version/crosswinds-blocks/change_log/changelog.md。
四、用仓库测试验证:changelog 夹具如何被断言
在 plugin_version_spec.rb 的"版本被检测到"分支(L156-L177)中,针对 Crosswinds Blocks 的期望结果定义在spec/fixtures/dynamic_finders/expected.yml。验证逻辑包含四条断言:
version必须是WPScan::Model::Version实例;version.number必须等于期望版本号(对本文夹具而言,正则会命中## 1.0.2 - November 20, 2023这一行,提取出1.0.2,即该 changelog 中的最新版本);version.found_by与期望的发现途径一致;version.interesting_entries与期望条目完全匹配(包含effective_url与正则匹配片段),且若配置了confidence,还需校验置信度值。
测试注释还给出了一条排错建议:失败时可用rspec -e "<完整描述>"定向运行单个用例,例如:
rspec -e "WPScan::Finders::PluginVersion::CrosswindsBlocks::BodyPattern#aggressive"另外,plugin_version_spec.rb头部注释(L19-L24)说明了一个测试工程细节:这类按配置自动生成的 spec 数以万计,绝大多数被标记为slow,仅在 master 全量套件中运行;而每个"父类/路径/版本键"组合的第一个查找器不标记slow,从而保证 PR 的覆盖率报告(--tag ~slow)中每一条底层代码路径至少被执行一次。
五、从单个夹具到通用方法论
Crosswinds Blocks 的 changelog 夹具只是dynamic_finders.yml中大量 ChangeLog 类配置的缩影。在 同一配置表 中可看到多种变体,例如crowdcue插件使用/\#\# \[(?<v>\d+\.[\.\d]+)\]/(匹配## [1.0]这种带方括号的标题),还有大量插件使用changelog.txt作为路径。这说明撰写新的动态查找器配置时只需把握三条原则:
- 正则必须与真实 changelog 格式对齐:先观察目标插件 changelog 的版本标题写法(
## x.y.z、## [x.y.z]、### Version x.y.z等),再设计对应的(?<v>...)命名捕获组; - 路径必须精确:
path要指向插件根目录下真实的相对路径(changelog.md、changelog.txt,或如本案例的change_log/changelog.md的实际站点路径对应物); - 保留
version: true与可选的confidence:确保结果被当作版本项消费,并可按需调整置信度。
六、总结
通过本文的拆解可以看到,一份看似平淡无奇的changelog.md,在 WPScan 中承担着"版本指纹库"的角色:它由 dynamic_finders.yml 声明识别规则,由 BodyPattern 查找器 在主动模式下抓取并匹配,最终经 Finder 基类 包装为带置信度与来源证据的版本对象,并由 plugin_version_spec.rb 以自动化测试锁定正确行为。理解这条链路,既能帮助你读懂 WPScan 的插件版本识别结果,也能为你在实际工作中设计类似的"从可公开访问文件中提取软件版本"的指纹方案提供参考。
- 网络安全
- 漏洞扫描
- 渗透测试
- 应用安全
- CLI
【免费下载链接】wpscan
WPScan WordPress security scanner. Written for security professionals and blog maintainers to test the security of their WordPress websites. Contact us via contact@wpscan.com
相关推荐
从 CHANGELOG.md 到插件版本指纹:WPScan 动态查找器如何用 BodyPattern 识别 array-partition 插件版本
从 CHANGELOG.md 到插件版本指纹:WPScan 动态查找器如何用 BodyPattern 识别 array partition 插件版本 导读 本文
网络安全漏洞扫描渗透测试应用安全CLI从 CHANGELOG.md 到版本指纹:WPScan 动态查找器识别插件版本的实战与原理
从 CHANGELOG.md 到版本指纹:WPScan 动态查找器识别插件版本的实战与原理 导读 本篇文章以 WPScan 仓库中 aw woocommerce
网络安全漏洞扫描渗透测试应用安全CLIWPScan 动态查找器实战:从插件 CHANGELOG.md 精确识别 WordPress 插件版本
WPScan 动态查找器实战:从插件 CHANGELOG.md 精确识别 WordPress 插件版本 导读 本篇文章以 WPScan 仓库内一份真实的 Wor
网络安全漏洞扫描渗透测试应用安全CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考