dinosn/CVE-2026-32710

GitHub: dinosn/CVE-2026-32710

针对 MariaDB 11.4.x JSON_SCHEMA_VALID() 堆溢出漏洞的完整利用链 PoC,演示从仅 SELECT 权限到持久化全库提权及 UDF 远程命令执行的全过程。

Stars: 12 | Forks: 0

# CVE-2026-32710 **MariaDB `JSON_SCHEMA_VALID()` 中的堆缓冲区溢出 → 持久化权限提升 → UDF RCE** | | | |---|---| | **受影响版本** | MariaDB 11.4.x(已在 11.4.9 上确认) | | **缺陷** | `json_get_normalized_string()` 中的越界写入 — `strncpy` 在没有边界检查的情况下写入 128 字节的 `DYNAMIC_STRING` | | **影响** | 仅 `SELECT` 权限的用户 → `ALL PRIVILEGES WITH GRANT OPTION` → 通过 UDF 执行任意命令 | | **来源** | `sql/json_schema_helper.cc:91` | ## 攻击前 `lowpriv` 只能对 `test` 数据库执行 `SELECT` 操作。系统表访问被拒绝。 ![before](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/cfbe509c17072634.png) ## 漏洞利用 单个 Python 脚本执行堆布局准备、通过用户变量元数据实现两步任意写入、通过 `GRANT ALL` 持久化权限提升,并通过 UDF 实现代码执行: ``` python3 exploit.py ``` ![exploit](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/365ad13009072635.png) ## 攻击后 `lowpriv` 现在拥有 `ALL PRIVILEGES WITH GRANT OPTION`,可以读取系统表、读写任意文件,并作为 `mysql` 用户执行 OS 命令。 授权在服务器重启后依然保留。 ![after](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/d6aeabac49072637.png)
完整漏洞利用输出 ![full run](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/5135b819a1072638.png)
## 阶段 ### 阶段 1:权限提升 ``` ┌──────────────────────────────────────────────────────────────────┐ │ SELECT json_schema_valid(overflow), │ │ @ccc...c := hop1, │ │ @aaa...a := hop2 │ └──────────────────────────────────────────────────────────────────┘ │ │ │ ▼ ▼ ▼ ┌───────────┐ ┌──────────────┐ ┌──────────────┐ │ 192-byte │ │ Write to @c │ │ Write to @a │ │ overflow │ │ through the │ │ through the │ │ corrupts │ │ corrupted │ │ redirected │ │ entry_c's │ │ pointer: │ │ pointer: │ │ value ptr │ │ │ │ │ │ (2-byte │ │ entry_a → │ │ master_access│ │ partial │ │ .value = │ │ = 0xFFFF.. │ │ overwrite)│ │ &master_ │ │ (ALL PRIVS) │ │ │ │ access │ │ │ └───────────┘ │ .length= 9 │ └──────────────┘ └──────────────┘ ``` 1. **堆布局准备 (Heap groom)** — 超过 100 个用户变量耗尽了 tcache,迫使 `Entry_a → Value_a → Entry_b → Value_b → Entry_c → Value_c` 被连续分配。 2. **溢出 (Overflow)** — `JSON_SCHEMA_VALID` 触发了一次 192 字节的 `strncpy` 写入越界,越过了 128 字节的缓冲区,破坏了 `entry_c→value`(在同一 64 KB 页面内的 2 字节部分指针覆盖),使其指向 `entry_a + 32`。 3. **跳转 1 (Hop 1)** — 对 `@c` 进行赋值,通过被破坏的指针将 126 字节写入 `entry_a` 的元数据中,设置如下: - `entry_a→value = &Security_context::master_access` - `entry_a→length = 9` 4. **跳转 2 (Hop 2)** — 对 `@a` 进行赋值,通过重定向的 `entry_a→value` 指针写入 8 字节的 `0xFF` → `master_access = ALL PRIVILEGES`。 5. **持久化 (Persist)** — 该会话现在持有所有权限。`GRANT ALL` 将权限提升提交到由 Aria 支持的 `mysql.global_priv` 表中。会话最终在清理期间崩溃(由于残留的堆损坏),但 GRANT 已完成检查点保存并在重启后保留。 ### 阶段 2:UDF RCE 持久化之后,服务器会重启(崩溃恢复),并利用提升的权限来安装 UDF 共享库: 1. `LOAD_FILE('/tmp/raptor_udf.so') INTO DUMPFILE '/usr/lib/mysql/plugin/raptor.so'` 2. `CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'raptor.so'` 3. `SELECT sys_exec('id > /tmp/pwned')` ### 解决的关键约束 | 约束 | 解决方案 | |---|---| | `STRING_RESULT` 在重新分配检查前执行 `length++` | Payload 为 N−1 字节,因此 N−1+1 = N 匹配存储的长度 → 在被破坏的指针上不触发重新分配 | | 126 字节的 hop2 破坏了 `Security_context` 之后的 THD 字段 | 在 hop1 中设置 `entry_a→length = 9`,使 hop2 只写入 8 字节 (master_access) + 1 个 NUL 字符 | | `Security_context` 中的 `master_access` 偏移量 | 距结构体基地址 1712 字节(`priv_user[384]` + `proxy_user[645]` + `priv_host[256]` + `priv_role[384]` + 填充 + 指针) | | Aria 崩溃恢复会回滚未提交的写入 | `GRANT ALL` + 10 秒的 `SLEEP` 允许在会话清理崩溃之前完成 Aria 检查点保存 | | 堆布局在不同连接之间会有所不同(即使 ASLR=0) | 内联的每次尝试扫描 `/proc/1/mem` 会发现每个连接的 entry_a 和 master_access | | plugin_dir 为 root 所有 | Dockerfile 预设了 `chmod 777`(便于实验) | ## 实验环境搭建 ``` # 1. 构建并启动容器 ./setup.sh # 2. 在 Docker 主机上禁用 ASLR sudo sh -c 'echo 0 > /proc/sys/kernel/randomize_va_space' # 3. 运行 exploit(每次尝试自动校准) python3 exploit.py # 4. 自定义命令 python3 exploit.py --cmd 'cat /etc/passwd > /tmp/out' ``` ### 前置要求 - Docker (x86_64) - Python 3 - 在 Docker **宿主机**上禁用 ASLR(`/proc/sys/kernel/randomize_va_space = 0`) - 容器以 `--cap-add SYS_PTRACE` 运行(用于访问 `/proc/1/mem`) ### 选项 ``` --calibrate Measure heap layout constants and exit --cmd CMD Command for Stage 2 UDF execution (default: id > /tmp/pwned) --stage1-only Run privilege escalation only, skip UDF RCE --attempts N Max stage 1 attempts (default: 5) --host HOST MariaDB host (default: 127.0.0.1) --port PORT MariaDB port (default: 3306) ``` ### 实验辅助工具提供的功能 漏洞利用脚本以 root 身份进入容器读取 `/proc/1/mem`。 这用于两件事: 1. **堆布局发现** — 定位三个哨兵 `user_var_entry` 结构体,并验证它们是相邻的(entry_a+32 与 entry_c→value 在同一个 64KB 页面内,用于 2 字节的部分覆盖)。 2. **`Security_context` 地址** — 查找 `master_access` 字段,以作为两步写入的目标。 扫描在每次尝试中内联运行,因为由于 MariaDB 的线程池分配不同的内存分配区域,堆布局在不同连接之间会有所不同(即使 ASLR=0)。实际的漏洞利用链 —— 溢出 + hop1 + hop2 —— 是通过 TCP 连接执行的纯 SQL。 在真实场景中,攻击者需要单独的信息泄露漏洞(或侧信道)来获取这些地址。 ## 文件 | 文件 | 描述 | |---|---| | `exploit.py` | 两阶段漏洞利用:权限提升 (TCP SQL) + UDF RCE | | `raptor_udf.c` | UDF 源码 — `sys_exec()` 调用 `system()` | | `Dockerfile` | 实验环境容器镜像(编译 UDF,开放 plugin_dir) | | `init.sql` | 创建 `lowpriv` 用户 | | `setup.sh` | 构建并启动实验环境 | | `screenshots/` | 终端截图 |
标签:0day漏洞, C++, CISA项目, CVE-2026-32710, DYNAMIC_STRING, JSON_SCHEMA_VALID, MariaDB, Python利用脚本, strncpy, UDF RCE, WITH GRANT OPTION, 二进制漏洞, 任意代码执行, 协议分析, 堆喷射, 堆溢出, 多线程, 提权, 数据擦除, 权限提升, 网络安全, 请求拦截, 越界写, 逆向工具, 隐私保护