WPScan 实战入门:从 Docker 靶场到弱密码爆破

WordPress 用的人多,插件和主题又基本决定了它的安全底线。同一个插件换个大版本,风险面就完全变了。所以要摸清一个 WordPress 站点,第一步永远是同一件事:它装了什么,是什么版本。WPScan 专门干这件事。

我在自己机器上用 Docker 搭了一个靶场,从装库文件到跑出 SUCCESS,前后十五分钟。下面这条路径就是我走过的,命令和输出都能照抄。前提是你只能在自己有权测试的站点上用,练手请自己搭靶场。

一、它到底在做什么

WPScan 站在普通访客的位置,从网页表面收集信息:WordPress 版本、装了哪些主题和插件、有哪些用户名、哪些入口开着。拿到这些之后再去对官方漏洞库,告诉你哪几样有已知 CVE。

探测点 它靠什么线索
WordPress 核心版本 首页 meta 标签、/readme.html、RSS 输出
主题 主题目录名、style.css 文件头
插件 页面里露出的插件路径、插件目录探测
用户名 /?author=1 的跳转、RSS 作者字段、登录报错文案
配置备份文件 常见备份文件名的字典探测
入口外露 xmlrpc.php、wp-cron.php 是否直接可达
密码强度 走登录接口逐个试密码

它的输出分两层,新手容易混在一起。探测层的结果不用账号也不用 token,装完就有;漏洞层要把探测到的东西对到官方库里,得有一个免费 API token,在 wpscan.com 注册后每天 25 次查询。刚开始只用探测层就够了,一个用户名加一份插件清单,后面的事都从这里长出来。

二、先把库文件弄好

Kali 上两条命令装完:

复制代码
sudo apt install -y wpscan
wpscan --version      # 实测输出:Version 3.8.28

第一次用之前更新漏洞库,文件放在 ~/.wpscan/db/,更新完大概 26MB,八个文件:metadata.json 是版本和指纹总表,wp_fingerprints.json 是文件指纹清单,dynamic_finders.yml 管动态探测规则,另外还有 timthumb 字典和备份、导出文件的字典。

我在这台机器上卡住了。到 data.wpscan.org 直连只有每秒几 KB,还时不时 SSL 握手失败,wpscan --update 半小时下不完一个文件。官方在 GitHub 上说过手动放库文件是被支持的,于是换成从官方 Docker 镜像里把库拷出来,镜像里烘焙了发布时的完整库:

复制代码
docker pull docker.1ms.run/wpscanteam/wpscan
docker create --name wpscantmp docker.1ms.run/wpscanteam/wpscan
docker cp wpscantmp:/wpscan/.cache/wpscan/db/. /root/.wpscan/db/
docker rm wpscantmp
ls /root/.wpscan/db        # 上面那八个文件齐了

之后每次扫描加一个参数,告诉它库已经备好,别联网检查更新:

复制代码
wpscan --url <目标> --no-update

网络正常的环境照常用 wpscan --update 就行。--no-update 只跳过启动时的联网检查,扫描功能不受影响。

三、八个参数够用了

完整列表看 wpscan -hh,新手先记住这八个:

参数 干什么用 怎么记
--url URL 目标网址 一切的起点,必填
-e u 枚举用户名 u 就是 users,第一个该跑的
-e ap / -e at 列出全部插件 / 主题 ap 就是 all plugins
-U admin -P 字典 指定用户名和字典试密码 U 是 usernames,P 是 passwords
--password-attack xmlrpc|wp-login 选从哪条门试密码 一般不用手动选,工具挑快的
--detection-mode passive|mixed|aggressive 探测力度三档 aggressive 最细,请求也最多
--no-update 跳过启动时的联网检查 慢网的救命参数
-t N / --throttle MS 线程数 / 每请求间隔毫秒 对真站请主动降速

枚举选项 -e 可以连写,比如 -e ap,u。常用的值有 vp(有已知漏洞的插件)、ap(所有插件)、p(热门插件)、vt 和 at、t(主题相关)、tt(timthumb 脚本)、cb(配置备份)、dbe(数据库导出)、u(用户,默认查 1 到 10)、m(媒体文件)。

有件事我一开始想不通:新版 WPScan 默认只自动查主题,插件必须显式加 -e ap。第一次默认扫描里那句 No plugins Found,多半就是这个原因,跟人家站上有没有插件没关系。

四、搭靶场,跑第一份报告

四步,十五分钟:

复制代码
① 用 Docker 搭一个带弱密码的 WordPress    (约 5 分钟)
② 跑一次全景扫描,学会读输出              (约 70 秒)
③ 枚举用户名,拿到"爆破靶子"              (约 30 秒)
④ 拿字典试密码,看到 [SUCCESS]            (约 10 秒)

4.1 起数据库和 WordPress

先起数据库,再起 WordPress,8080 端口留给扫描:

复制代码
docker run -d --name wpdb -e MYSQL_ROOT_PASSWORD=root123 \
  -e MYSQL_DATABASE=wordpress mysql:5.7
sleep 25

docker run -d --name wp -p 8080:80 --link wpdb:mysqlhost \
  -e WORDPRESS_DB_HOST=mysqlhost:3306 \
  -e WORDPRESS_DB_USER=root \
  -e WORDPRESS_DB_PASSWORD=root123 \
  wordpress:latest
sleep 6
docker ps --format '{{.Names}} {{.Status}} {{.Ports}}'

然后用一次请求走完安装向导。这里得故意留一个弱密码 admin/admin123,给第四步的密码攻击当靶子:

复制代码
curl -s 'http://127.0.0.1:8080/wp-admin/install.php?step=2' \
  --data 'weblog_title=LabWP&language=&user_name=admin&admin_password=admin123&admin_password2=admin123&pw_weak=on&blog_public=1&admin_email=admin@lab.local'

返回页面里有 Success! 就说明装好了。打开 /wp-login.php 用 admin/admin123 能进后台,靶场就绪。

4.2 第一次全景扫描

复制代码
wpscan --url http://127.0.0.1:8080 --no-update

[+] URL: http://127.0.0.1:8080/ [127.0.0.1]
Interesting Finding(s):

[+] Headers
 |  - Server: Apache/2.4.69 (Debian)
 |  - X-Powered-By: PHP/8.3.36
 | Found By: Headers (Passive Detection)

[+] XML-RPC seems to be enabled: http://127.0.0.1:8080/xmlrpc.php
[+] WordPress readme found: http://127.0.0.1:8080/readme.html
[+] The external WP-Cron seems to be enabled: http://127.0.0.1:8080/wp-cron.php

[i] The WordPress version could not be detected.
[+] WordPress theme in use: twentytwentyfive
[+] Enumerating All Plugins (via Passive Methods)
[i] No plugins Found.

[+] Requests Done: 1441
[+] Elapsed time: 00:01:10

一个刚装好的干净站,报告已经交出六条信息:

发现 为什么值得看
Server: Apache / PHP 8.3.36 PHP 是 WordPress 的运行底座,版本决定了哪批已知漏洞存在
XML-RPC enabled 一次请求能打包多件事,试密码时最省流量
readme.html found 自带说明文件没删,版本和历史都对外露着
WP-Cron enabled 定时任务接口对外开放,常见的骚扰点
version not detected 反探测挡住了它,但版本未必真的未知
No plugins Found 默认的被动探测找不到不露面的插件

最后两行是这次体检的消耗:1441 个请求,70 秒。打自己的靶场无所谓,打真站之前先掂量一下这两个数字。

4.3 枚举用户名

复制代码
wpscan --url http://127.0.0.1:8080 --no-update -e u

[i] User(s) Identified:

[+] admin
 | Found By: Rss Generator (Passive Detection)
 | Confirmed By:
 |  Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 |  Login Error Messages (Aggressive Detection)

一条命令,三个不相干的角度都对上了同一个名字。这三种方法为什么都能读出名字?

  • RSS Generator:不少站的 RSS 会把作者写进 feed,名字直接是明文。
  • Author Id 枚举:访问 /?author=1,WordPress 自动跳到作者档案页,跳转地址里就藏着用户名。
  • 登录报错文案:密码输错时系统会提示该用户存在但密码不对,这条报错本身就是探测器。

有了用户名,密码攻击才有靶子。只要任何一处露了名,就够了。

4.4 插件点名:找出隐身的 Akismet

上面说 No plugins Found,可 Akismet 明明在装着。默认的被动探测只看首页 HTML 有没有主动提到插件路径,Akismet 开着但不在前台露面,就隐身了。点名确认一下:

复制代码
wpscan --url http://127.0.0.1:8080 --no-update \
  --plugins-list akismet,hello-dolly --plugins-detection aggressive

[+] akismet
 | Location: http://127.0.0.1:8080/wp-content/plugins/akismet/
 | Latest Version: 5.7.2
 | Found By: Known Locations (Aggressive Detection)
 |  - status: 403

原理很朴素:对着常见插件路径字典挨个发请求,看目录在不在。返回 403 表示目录存在但禁止访问,这就是它在这的硬证据;返回 404 就是没有。

三档探测的分工也就清楚了。passive 是站远了看一眼,快,对目标几乎没影响;aggressive 是挨家挨户敲门,准,慢,请求多。隐身插件只能用 aggressive 喊出来。

4.5 密码攻击:看到 SUCCESS

先备一本小字典,每行一个密码,然后开打:

复制代码
wpscan --url http://127.0.0.1:8080 --no-update -U admin -P /tmp/wp/dict.txt

[+] Performing password attack on Xmlrpc against 1 user/s
[SUCCESS] - admin / admin123

[+] Valid Combinations Found:
 | Username: admin, Password: admin123

这份输出怎么读:

  • Performing password attack on Xmlrpc 说明工具选了 XMLRPC 通道,因为一个请求能打包大约 100 个候选密码,由 --multicall-max-passwords 控制,比网页登录框快得多。
  • SUCCESS 那一行就是试出来的明文组合。
  • 如果目标的 xmlrpc 被管理员关了,工具会自动换 wp-login 通道,一个密码一个请求,慢但通用。

四步串起来是一条链:全景扫描看站长什么样,用户枚举看谁有账号,插件清单看装了什么,密码攻击看能不能进。WPScan 的参数不是一堆孤立开关,都是围着这条链设计的。

五、几条能直接抄的命令

复制代码
# ① 隐身扫描:随机 UA + 只做被动探测(被 WAF 盯着的站首选)
wpscan --url https://站 --stealthy

# ② 全枚举一次打满(靶场随意;真站请配 --throttle 降速)
wpscan --url https://站 --no-update -e ap,at,cb,dbe,u1-10 -t 10

# ③ 带上 API token,报告加漏洞层
wpscan --url https://站 --api-token YOUR_TOKEN

# ④ 点名精确探测,不做全字典
wpscan --url https://站 --no-update \
  --plugins-list akismet,elementor --themes-list twentytwentyfive \
  --detection-mode aggressive

# ⑤ 强制 XMLRPC 通道试密码;被关时改走 wp-login
wpscan --url https://站 --no-update -U admin -P dict.txt --password-attack xmlrpc

# ⑥ 规定时限,到点自动收工
wpscan --url https://站 --no-update --max-scan-duration 300

# ⑦ 缓存管理(参数变了记得清)
wpscan --url https://站 --clear-cache

两个开关各管一件事,别搞混:--update 管探测层数据的新鲜度,--api-token 管有没有漏洞报告层。

六、七个坑

  • 坑一,更新卡死。data.wpscan.org 在部分网络极慢甚至握手失败,--update 永远下不完。用官方镜像 docker cp 取库,之后加 --no-update。
  • 坑二,No WPScan API Token given 不是报错。它只是提醒你这次只有探测层,没有漏洞匹配。没 token 照样能完整扫描。
  • 坑三,version not detected 不等于版本未知。人工核一遍首页源码(curl -s 站点 | grep generator)和 readme.html 是标准动作。
  • 坑四,No plugins Found 不等于没插件。补救顺序是先 -e ap 跑全量字典,慢但全;再 --plugins-list 名字 --plugins-detection aggressive 点名,快且准。
  • 坑五,请求量是肌肉秤。基础扫描 1441 个请求 70 秒,全枚举 7000 多个请求约 10 分钟。对生产站点等于做一次小压测,容易触发防护,也会在日志里留下痕迹,没有书面授权就是违规。真站必配 --throttle 200,最好再加 --max-scan-duration 兜底。
  • 坑六,看清密码攻击走的是哪条通道。Xmlrpc 是一包多试的快通道,被关掉后自动转 wp-login 慢通道,同样的字典耗时差十倍。
  • 坑七,新装的 WordPress 也会报一堆发现。XML-RPC、readme.html、wp-cron 都在标准加固清单上,不算误报。工具报得凶和站真的有漏洞,中间还隔着一层上下文。

七、小结

WPScan 大概可以记成三件事的合体:查 WordPress 版本,拿用户名名单,试密码。四步路线是:

复制代码
--url 看全景  →  -e u 找人  →  -e ap 找插件  →  -U -P 试密码

每一步的答案都明明白白出现在输出里。Requests Done 和 Elapsed time 是这次体检的子弹和体力,要不要打、打到什么程度,先看这两个数再决定。

最实在的一课还是自己动手:拿一台自己的机器,从 docker run 一路走到 SUCCESS - admin / admin123,十五分钟就能走通。工具用过一遍,再谈熟练。

本文所有操作都在自建靶场完成。扫描和爆破他人站点可能触犯法律,请只在有明确授权的范围内做安全测试。

相关推荐
qq_452396231 小时前
第十篇:《Kafka 安全加固:SASL、SSL、ACL 与多租户隔离》
安全·kafka·ssl
xbzb1 小时前
Docker 架构与部署:一条 docker run 到底走了三层
docker·容器·架构·镜像·加速·daemon
Decay__2 小时前
SQL Server 数据库全栈运维实战系列 — 合集索引(24 篇)
运维·数据库·sql·mysql·架构
Decay__10 小时前
12_MySQL InnoDB 内存与锁调优 —— Buffer Pool、redo log、隔离级别与锁等待
运维·数据库·sql·mysql·架构
ppo_wu10 小时前
解决夸克网盘自动修改PDF打开方式的方法
经验分享·安全·网络安全
java_logo12 小时前
Docker 部署 Dify:轻松搭建 LLM 应用开发平台
运维·docker·容器
方便面不加香菜13 小时前
Docker Container -- 综合实战案例详解
运维·docker·容器
海洋之恒15 小时前
GitHub Actions 自动化:5 个真实运维提效案例
运维·自动化·github
迪康coolmu16 小时前
出差终端数据安全落地实战:基于迪康端点安全一体化管理系统的四道防线实践
运维·服务器·经验分享·笔记·其他·安全·php