Pi-hole FTL管理竞态条件漏洞CVE-2026-44693
Pi-hole FTL是Pi-hole网络级广告和跟踪器拦截器的核心引擎,采用开源C语言编写,使用嵌入式CivetWeb Web服务器提供 HTTP API。
一、基本情况
FTL 全称 Faster Than Light(超光速),其完整程序名为 pihole-FTL,又被称 FTLDNS,是 Pi-hole 广告拦截系统唯一核心底层引擎。
Pi-hole FTL基于 dnsmasq 深度二次开发,用 C 语言编写,单文件运行,资源占用极低,高性能,树莓派等低配置硬件也能流畅运行。

栋科技漏洞库关注到 Pi-hole FTL 6.0 至 6.6.0 版本中的HTTP会话管理竞态条件漏洞,现追踪为CVE-2026-44693,CVSS 3.X评分8.8。
二、漏洞分析
CVE-2026-4469是 Pi-hole FTL相关版本HTTP会话管理子系统的竞争条件漏洞,是6.0版本基于CivetWeb重构内置 Web 服务后引入的。
程序使用全局字符缓冲区pi_hole_extra_headers,将携带管理员会话ID的Set-Cookie响应头从API认证层传递至 CivetWeb HTTP 引擎。
CivetWeb 默认启用 50 个工作线程并发处理请求,而该共享缓冲区未配置任何同步保护机制,
管理员线程写入会话 ID 与其他线程读取数据之间存在竞争窗口。
局域网内未授权攻击者向公开 API 接口发送并发请求,
即可获取本应发给在线管理员的 Set-Cookie 响应头,无需凭证就能获得完整管理员权限。
具体来说,受影响版本中,pi_hole_extra_headers 是全局 char[1024] 缓冲区,
被所有认证请求处理器写入并由 civetweb 的 send_additional_header() 读取,但 civetweb 运行时存在多达 50 个并发工作线程。
当多个线程并行处理认证请求时,一个线程可能覆盖或清除另一个线程的头部数据,
导致 Set-Cookie 头被发送给错误的客户端或 cookie 完全丢失。
auth_data 会话数组存在类似竞态:
并发线程无同步地读取和修改会话数据,get_all_sessions() 通过 JSON_REF_STR_IN_OBJECT 直接引用 auth_data[] 中的字符串,
而 delete_session() 对 slot 执行 memset 操作会在序列化过程中导致数据损坏,c
heck_client_auth() 存储的 api->session 指针在后续使用时会变为悬空指针。
(一)详细阐述
1、共享全局缓冲区(文件路径:src/webserver/http-common.c)
首个问题用于传递附加 HTTP 头(携带管理员会话 ID 的 Set-Cookie 头)的缓冲区被定义为普通全局变量:
char pi_hole_extra_headers[PIHOLE_HEADERS_MAXLEN] = { 0 };
C 语言全局变量在进程内仅有一份内存地址,所有线程共用。
CivetWeb 默认启动 50 个工作线程处理 HTTP 并发请求,全部线程都会访问该缓冲区,
代码未使用互斥锁、信号量、原子操作等任意同步手段,无法保证同一时间仅有一个线程读写缓冲区。
2、check_client_auth () 函数中无同步赋值逻辑(文件路径:src/api/auth.c)
从数据流角度,此处是漏洞核心。
check_client_auth () 函数校验客户端 Cookie 内的会话 ID,并为连接绑定对应会话,赋值操作全程未加锁。
// No mutex. Any other thread can modify auth_data[]
// between this line and the point where api->session->sid is used.
api->session = &auth_data[user_id];
竞争窗口过程如下:
线程 A(管理员)调用 check_client_auth (),获取 auth_data [用户 ID] 槽位指针,随后准备构造 HTTP 响应。
在线程 A 读取 api->session->sid 以生成 Set-Cookie 头之前,
处理公开接口并发请求的线程 B(攻击者)可先访问 auth_data [] 的同一槽位,或抢先进入响应头构造流程,
读取 pi_hole_extra_headers 缓冲区内容。
3、send_api_auth_status () 函数无保护写入操作(文件路径:src/api/auth.c)
漏洞链路最后一环是该函数,它构造 Set-Cookie 头并写入全局缓冲区。
此操作直接将管理员会话 SID 存入 pi_hole_extra_headers 供 CivetWeb 写入 HTTP 响应,全程未对该共享缓冲区做任何防护。
snprintf(pi_hole_extra_headers, sizeof(pi_hole_extra_headers),
"Set-Cookie: sid=%s; SameSite=Lax; Path=/; Max-Age=%u; HttpOnly\r\n",
auth_data[user_id].sid, ...);
三、POC概念验证
1、完整利用流程:
线程 A 将管理员会话 SID 写入 pi_hole_extra_headers 缓冲区。
CivetWeb 本应在构造线程 A 的 HTTP 响应时读取该缓冲区并添加对应响应头。
但在线程 A 通过 snprintf 完成写入、CivetWeb 读取缓冲区这两个操作的间隙,
处理攻击者请求的线程 B 若抢先执行 HTTP 响应头构造逻辑并读取 pi_hole_extra_headers,
攻击者的响应包中就会携带包含管理员 SID 的 Set-Cookie 字段,无需任何凭证即可获取完整管理员权限。
2、附带 PoC 工作原理
配套验证脚本 poc_session_race_min.py 专为受控环境下复现该漏洞而编写。
管理员已设置登录后刷新可查看脚本同步模拟攻击中的两类主体:正常访问 Web 管理页面的合法管理员、同一局域网内窃取会话的攻击者。
脚本在同一进程内并行执行两大阶段逻辑。
阶段 1:模拟管理员
该阶段模拟正常管理员操作 Pi-hole 网页后台。
脚本使用给定凭证调用 /api/auth 完成登录,获取会话 SID 后,
新建独立线程以不定间隔持续请求各类仪表盘接口(包括 /api/stats/summary、/api/config、/api/dns/blocking、/api/history 等),
模拟真人浏览行为。
该阶段作用是持续向全局缓冲区 pi_hole_extra_headers 写入管理员 SID,复现真实生产环境中管理员操作后台时的缓冲区写入场景。
poc_session_race_min.py
阶段 2:模拟攻击者
该阶段模拟局域网攻击者行为。
脚本默认启动 100 条并发线程,
无鉴权地高频访问 Pi-hole 三个公开接口:/api/info/login、/api/auth、/api/docs,所有线程均不携带登录凭证与会话 ID。
脚本会检查每条返回响应是否包含 Set-Cookie: sid = 响应头。
当时间窗口契合、攻击者线程在缓冲区存有管理员 SID 时执行 CivetWeb 响应头读取逻辑,
攻击者即可在自身响应中拿到管理员 Cookie,全程无需凭证。
一旦捕获到有效 SID,脚本会立即调用 /api/auth 校验该会话是否具备管理员权限,并输出可直接运行的 curl 利用命令。

3、影响
局域网内未授权攻击者利用该漏洞可获取有效的管理员会话 SID,凭借该 SID 登录 Pi-hole 网页管理后台,
完全掌控所有配置,包括修改 DNS 参数、关闭广告与恶意域名拦截功能、查看全部网络流量数据。
4、修复方案(2026 年 4 月 24 日)
FTL v6.6.1(2026 年 4 月 24 日)通过 PR #2835 修复此漏洞,
该 PR 原本用于解决高并发场景下程序崩溃(段错误 SIGSEGV)的多线程安全问题,本次安全漏洞修复属于附带完善改动。
本次修复包含两项配套修改:
核心修复:将全局缓冲区 pi_hole_extra_headers 改为线程本地存储(_Thread_local)。
修改后每个 CivetWeb 工作线程拥有独立私有缓冲区,不再共用同一块内存:
- char pi_hole_extra_headers[PIHOLE_HEADERS_MAXLEN] = { 0 };
+ _Thread_local char pi_hole_extra_headers[PIHOLE_HEADERS_MAXLEN] = { 0 };
基于 CivetWeb 线程模型,写入管理员 SID 的线程与构造 HTTP 响应的线程必然为同一个,
缓冲区读写操作处于同一执行上下文,彻底消除竞争条件窗口。
配套加固:新增 pthread_mutex_t 类型互斥锁 auth_lock,对会话数组 auth_data [] 的所有并发访问操作加锁保护;
同时不再直接使用数组内原生指针,改为拷贝会话结构体数据,API 处理逻辑操作私有副本而非共享内存。
四、影响范围
6.0 ≤ Pi-hole FTL ≤ 6.6.0
五、修复建议
Pi-hole FTL ≥ 6.61
六、参考链接
管理员已设置登录后刷新可查看