docs: 补 XML-RPC 端点行为、动态 token 与 .php 变体说明,记实现取舍

This commit is contained in:
tamakyi
2026-10-23 12:58:49 +08:00
parent 307bd94ed1
commit 9d3f5a72fa
2 changed files with 57 additions and 0 deletions
+40
View File
@@ -113,3 +113,43 @@ IPv6 丢掉了精确地址(IPv4 无损);邮件里也只给键,运维会
**新工具** `dev/preview-honeypot`:渲染 9 个家族到 `outputs/honeypot-preview/index.html`,
配 chrome headless 截图核对外观。靠它发现两处「源码看不出、截图一眼假」:
Tomcat 的 skin 忘了 css(按钮白字无底色)、WP 登录页底部挂着 404 文案。
### 测试环境验收:9 家族伪装(部署 7225f68 后,只读 GET)
全部符合预期:8 个家族 title/generator 各不相同、Tomcat 401+Basic realm、
静态资源 200 且 MIME 正确(128B css / 81B js / 70B png)、插件目录 .php 回 404、
业务路由(/login /user /static /api)无 canary、界外路径仍 302 → /。
发现的漏洞(按优先级):
1. **P1 `/xmlrpc.php` 回 HTML 登录页** —— 而我们自己发了 `X-Pingback: /xmlrpc.php`,
真实 WP 回 text/xml,等于自己指给自己一个明显假的地址;同时丢掉 WP 爆破最有价值的
捕获通道(xmlrpc system.multicall 一次可试上千组,绕开登录限速)。
2. **P2 每次请求恒定值**:PMA `token=e1a4c0b7d2f58g93`(还带非 hex 的 g)、
generic `csrf_token=a7f3c1e9b2d4`、`X-Jenkins-Session: 8b1f2c4d`。
两次请求完全相同 → 跨请求指纹即可识别。应改每请求随机。
3. **P2 `X-Pingback` 用相对路径**,真实 WP 给绝对 URL。
4. **P3 `/phpinfo.php` 漏匹配**:路径清单里是 `/phpinfo`、`/info.php` 精确项,
段前缀匹配不覆盖 `.php` 变体 → 302。可加「p 去掉 .php 后在集合内」的兜底。
5. **P3 `/wp-json/wp/v2/users` 回 HTML**,真实 WP 回 JSON(用户枚举接口)。
6. P3 `X-Frame-Options: DENY` / `Cache-Control: no-store` 与真实 WP 不同,
但属全局中间件,改动影响全站。
### 修 P1/P2:XML-RPC 端点 + 动态 token + .php 兜底
- 新增 `internal/security/honeypot/xmlrpc.go`:`/xmlrpc.php` 不再回登录页。
GET→405+XML fault「XML-RPC services are disabled」;POST methodCall→200+fault
「Incorrect username or password」;POST system.listMethods→方法列表模板。
凭据提取用正则(不上 encoding/xml,蜜罐高频命中):抓 `<string>`、剔除
methodName 与 http(s) URL、两两配对;`blog_id=1` 这种「奇数长度+首项短数字」
会丢首项纠正错位;第一组进标准字段,其余进 `cred2.*`,原始串全量进 `xmlrpc.N`
(配置上限 16 对 / raw 8KB)。
- 动态占位符:`LoginPageVars.Nonce`(32 hex,`math/rand/v2`,刻意不用 crypto/rand,
这些是假 token 只需「每次不同」)。支持 `$hex8/16/32` 与 `$base`;
页头、hidden、Set-Cookie 共用同一套 nonce。
- `X-Pingback` 改绝对地址(`$base/xmlrpc.php`),没配 `external_url` 就不发。
- `Cache-Control` 从 `no-store` 改 `no-cache, must-revalidate, max-age=0`(更像 WP/PMA)。
- Grafana/GitLab 补真实会种的会话 cookie(`grafana_sess`、`_gitlab_session`,动态)。
- `Match` 增加 `.php` 变体兜底:`/phpinfo.php` 之前漏匹配 → 302。
- 性能:RenderFamily 3.7µs→4.18µs、2→3 allocs(多的一次是 hex 编码),可接受。
四处变异验证有效(固定 token / xmlrpc 分支失效 / Pingback 相对路径 / .php 兜底失效)。
+17
View File
@@ -45,6 +45,19 @@
**静态资源也一起伪装**:`/wp-admin/css/login.min.css`、`/wp-includes/js/*.js`、`/wp-admin/images/*.png` 这类请求回占位 CSS/JS/真 1×1 PNG。这些请求**不落库、不计数** —— 一次页面加载会打 5–10 个资源,逐个 INSERT 等于帮攻击者把写入放大十倍。插件目录下的 `.php` 则回 404(那是 webshell 探测,回登录页反而不像)。
**`/xmlrpc.php` 单独按 XML-RPC 回应**,不是登录页:
| 请求 | 响应 |
|---|---|
| `GET` / 其它非 POST | `405` + XML fault「XML-RPC services are disabled on this site.」 |
| `POST` 普通 `methodCall` | `200` + XML fault「Incorrect username or password.」 |
| `POST` `system.multicall` | 同上,但**一包里的所有凭据对都会被记下来** |
| `POST` `system.listMethods` | `200` + 方法列表(枚举类工具靠它确认对面是真 WP) |
为什么值得单独做:`system.multicall` 一次请求能塞进上千组用户名/密码,且绕开登录页的限速插件,是 WordPress 爆破里产出最高的一条通道。我们自己的 `X-Pingback` 响应头又正指着这个地址 —— 回 HTML 等于自己指路给对方看破绽。
动态值(phpMyAdmin 的 `token`、通用后台的 `csrf_token`、Jenkins 的 `X-Jenkins-Session`、GitLab 的 `_gitlab_session`)**每个请求都不同**。恒定值是最容易被指纹识别的特征:连打两次拿到一模一样的 token,任何脚本都能判定对面是伪造的。
单页渲染约 3.7 µs / 2 次分配、页面 1.5–2.5 KB,资源请求 0 分配(`BenchmarkRenderFamily` / `BenchmarkAssetFor` 守着)。
---
@@ -87,6 +100,8 @@ honeypot_cred_dir = data/honeypot-creds
| `GET /wp-login.php` 看一眼假页面 | 常规 `autoban.Record` | `ban_threshold_sensitive`(默认 10 次 / 60 秒) | 否 |
| `GET /wp-admin/css/login.min.css` 等资源 | **不计数、不落库** | — | 否 |
| `POST` 带该家族的凭据字段(`log`/`pwd`、`pma_*`、`j_*`、`user[login]` …) | 蜜罐 `autoban.RecordHoneypot` | `honeypot_threshold`(默认 1) | 是,立即封 |
| `POST /xmlrpc.php` 带 `methodCall`(含 `system.multicall`) | 蜜罐 `autoban.RecordHoneypot` | 同上 | 是,立即封 |
| `GET /xmlrpc.php` / `POST system.listMethods` | 常规通道 | 同上 | 否 |
| `POST` 不带任何凭据字段 | 常规通道 | 同上 | 否 |
**为什么 GET 不立即封**:爆破脚本的标准流程是「先 GET 探表单 → 再 POST 提交」。如果 GET 就吃 403,假登录页只被看了一次,对方根本没机会填表,凭据永远拿不到 —— 而这个功能的全部价值就在那份凭据上。
@@ -250,3 +265,5 @@ pg_dump -Fc <dbname> > before-purge-$(date +%F-%H%M).dump
- **只挡 NotFound**:如果将来给 `/admin` 之类路径注册了真实路由,蜜罐会自动让位 —— 这是刻意设计(业务优先)。
- **告警邮件仍带明文用户名/密码**:这正是它的价值(看到对方在用什么字典),但也意味着邮件是敏感物,收件人只应是你自己。
- **不主动反制**:不回打、不上报第三方、不做删除/篡改,只有「记录 + 临时封禁」。
- **XML-RPC 的凭据提取是启发式的**:不上 `encoding/xml` 解析(蜜罐会被高频命中),而是抓出所有 `<string>`、剔除方法名与站点 URL 后两两配对。`wp.getUsersBlogs` 的第一个参数是 `blog_id`,若是数字会让配对错位一位——为此会把原始串全量写进取证文件的 `fields`(`xmlrpc.0`、`xmlrpc.1` …),即便配对失败原文也不丢。
- **`X-Pingback` 需要配 `external_url`**:没配就不发这个头(相对路径比不发更假)。