abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · phpmyadmin-vqb-xss
类别: 存储型 XSS 影响范围: 远程
phpMyAdmin 5.2.3(RELEASE_5_2_3 / 962857e)- phpMyAdmin
js/src/designer/history.js 使用 innerHTML 构建可视化查询历史记录。列名经过 $('<div/>').text(...).html() 处理。表名则没有。TableName 仍然允许 < 和 >。一个在共享数据库上拥有 CREATE TABLE 权限的低权限账户植入一个 HTML 标识符。当更高权限的用户打开 Designer 时,getTableName() 就存在于页面上,而 DesignerHistory.display() 会将其写入 #ab。
phpMyAdmin 源中的存储型 XSS。与 PMASA-2025-1 同类,但功能不同。Check-tables 的标题已被转义。此路径未被转义。
| ID | 暂无 CVE |
| 类别 | 存储型 XSS |
| 影响范围 | 远程(已认证的 CREATE TABLE;受害者打开 Designer) |
| CWE | CWE-79 |
| CVSS | 高危:8.0 CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N |
| 产品 | phpMyAdmin |
| 受影响版本 | 5.2.3 标签 RELEASE_5_2_3(962857e4f63d42e38f11ff4d63f5e722018add76) |
| 认证 | 已认证。攻击者需要在受害者在 Designer 中打开的数据库上拥有 CREATE TABLE 权限 |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅限 127.0.0.1 |
植入一个 HTML 表名,然后等待更高权限的用户在该数据库上打开 Designer 并渲染可视化查询历史记录。
| 权限 | 方法 / 汇聚点 | 侦察 / 完整性 / 可用性 | 实验验证或推断 |
|---|---|---|---|
| 向 phpMyAdmin 源注入标记 | DesignerHistory.display() 将 getTab() 拼接到 #ab 的 innerHTML 中 | 受害者源的完整性 | 实验验证。phpmyadmin:5.2.3 上运行的 history.js。<h3> 包含 <i id="pma-vqb"> |
突破 designer 的 table_name 属性 | database_tables.twig 的 small_tab_pref_1 使用 designerTable.getTableName() | 在 Designer DOM 中侦察原始 MySQL 名称 | 实验验证可读取为原始标识符 |
读取受害者会话 cookie 和 CSRF token | 历史记录渲染后的同源脚本 | 侦察受害者 PMA 会话 | 从 PMA 源中的存储型 XSS 推断。本实验使用 <i> 标记,而非脚本 |
| 以受害者 MySQL 账户身份运行 SQL、管理用户、导出 | 带 token 的 POST /sql、/server/privileges、导出 | 该 MySQL 账户可触及的任何内容的完整性 | 推断。与 PMASA-2025-1 影响类别相同 |
| 更改 2FA / userconfig | 以受害者身份 POST /config/set | 受害者偏好存储的完整性 | 推断。同类遗留问题,非本实验 |
此遗留问题已排除:未认证访问、操作系统 RCE 以及 VQB OK 按钮路径。.small_tab_pref_click_opt 预先转义了 table_name,Twig 又再次自动转义,因此该点击目前会将实体送入 getTab()。这并非对 history.js 的修复。列单元格保持文本转义(c<b id=pma-col>y,没有 <b id="pma-col"> 节点)。
同一版本固定点:WebAuthn CustomServer 遗留问题。
我固定了 phpMyAdmin RELEASE_5_2_3。PMASA-2025-1/2/3 已在 5.2.2 中。Designer 仍然手工构建 VQB 历史记录 HTML。
js/src/designer/history.js(发布为 js/dist/designer/history.js):
temp = historyArray[i].getTab();
str += '<h3 class="tiger"><a href="#">' + temp + '</a></h3>';
// ...
'<td width="175" style="padding-left: 5px">' + $('<div/>').text(historyArray[i].getColumnName()).html() + '<td>';
libraries/classes/Dbal/TableName.php 仅检查非空、最大 64 字符、无尾随空格。< 和 > 是合法的。MySQL 在带引号的标识符中接受它们。
templates/database/designer/database_tables.twig 将 designerTable.getTableName() 复制到 .small_tab_pref_1 的 table_name 中。VQB 选项单元格使用同一名称的预转义副本。
已记录的弯路:将双重转义的选项按钮属性视为修复(并非如此——small_tab_pref_1 仍然暴露原始名称,而 history.js 才是汇聚点);将列辅助函数视为覆盖表名;声称存在未认证 XSS;反弹 shell。都是表演。
python3 phpmyadmin-vqb-xss-Abraxas-Labs.py
# or: cd lab && ./run.sh
目标仅限 http://127.0.0.1:19250。镜像 phpmyadmin:5.2.3 和 mysql:8.0。Compose 项目 phpmyadmin-vqb-xss。poc 服务中使用 Playwright Chromium。运行后堆栈拆除。
两个用户,共享 shareddb。低权限 creator 运行 CREATE TABLE \tx` (`cy` INT)。高权限 victim登录 phpMyAdmin 并以query=1打开 Designer。运行的history.js 是来自容器的副本(h3-unescaped=yes,列辅助函数存在)。Chromium 使用页面上 getTableName()的表名调用DesignerHistory.display()`。
结果:#ab h3.tiger 的 innerHTML 为 <a href="#">t<i id="pma-vqb">x</i></a>。列 td 文本为 c<b id=pma-col>y,没有 b#pma-col 节点。
SUCCESS phpmyadmin-vqb-xss h3-markup=yes col-escaped=yes how=live PMA-VQB-XSS-WITNESS
在拼接到 <h3> 之前,使用 $('<div/>').text(temp).html() 或 Functions.escapeHtml 转义 getTab()。不要依赖 Twig 对某个属性的双重编码。保留列名辅助函数作为范例。
962857e4f63d42e38f11ff4d63f5e722018add76)js/src/designer/history.jslibraries/classes/Dbal/TableName.phpGNU Affero GPL v3.0。参见 LICENSE。仅限回环实验环境。无担保。