文章总结: 该文档分析了NGINXDAV模块缓冲区溢出漏洞CVE-2026-27654,指出在特定配置下(前缀location使用alias并启用dav_methods的copy/move方法),攻击者可通过构造短于location前缀的Destination请求头触发无符号下溢,导致堆缓冲区溢出。漏洞影响NGINX1.29.7和1.28.3以下版本,可能造成进程崩溃或远程代码执行。文档详细阐述了漏洞原理、复现步骤及流量特征,并建议升级至安全版本。 综合评分: 82 文章分类: 漏洞分析,WEB安全,应急响应,解决方案,技术标准
NGINXDAV模块缓冲区溢出漏洞 | CVE-2026-27654原理分析&研究
原创
404号浪漫 404号浪漫
404号浪漫
2026年4月18日 22:07 北京
在小说阅读器读本章
去阅读
点击蓝字,关注我们
0x0 背景介绍
NGINX是一款高性能的HTTP和反向代理服务器,ngx_http_dav_module模块用于处理WebDAV协议请求。该模块在处理MOVE或COPY方法时,若location配置了alias指令且为前缀匹配,存在缓冲区溢出漏洞。攻击者可通过构造恶意的Destination请求头,导致 worker 进程崩溃或在document root之外修改文件名。
漏洞详情
| 漏洞类型 | 影响版本 | 利用复杂度 | CVE编号 | | — | — | — | — | | 堆缓冲区溢出 | NGINX Open Source < 1.29.7 NGINX Open Source < 1.28.3 | 低 | CVE-2026-27654 |
攻击效果:
- 进程崩溃,重则RCE。
0x1 环境搭建(Ubuntu24)
1.1-Ubuntu24+Docker搭建配置
:>暂无,其实配置下nginx.conf就行
0x2 漏洞复现
#
2.1 原理场景 A:前缀 location + alias + dav_methods 开启 copy/move(最小触发面)
示例:受影响配置
- 关键点:
location为前缀匹配(非正则),并使用alias;同时dav_methods允许copy move
server { listen 8080;
# 前缀 location(非正则) + alias location /dav/{alias D:/webdav_root/;# Windows 示例;Linux 可用 /var/www/dav/ dav_methods put delete mkcol copy move; create_full_put_path on; dav_access user:rw group:rw all:r; }}
为什么这份配置会导致漏洞?(原则上)
alias会让核心函数ngx_http_map_uri_to_path()在拼接路径时执行r->uri.len - alias这样的计算(alias值来源于location名称长度)- WebDAV的
COPY/MOVE会把Destination解析成新的r->uri,然后复用同一套路径映射逻辑去生成目标文件路径 - 只要让Destination的URI长度小于
location前缀长度,size_t的减法就会发生无符号下溢,为后续堆溢出埋雷
#
2.2 原理场景 B:触发崩溃/异常行为(COPY)
下面是一组“看起来很正常”的 COPY 请求:源 URI 是 /dav/a.txt(落在 /dav/ location),Destination 刻意写成更短的 /dav(不带尾部 /,与源保持“非 collection”一致)。
模拟 HTTP 流量
COPY /dav/a.txt HTTP/1.1Host: 127.0.0.1:8080Destination: /davOverwrite: TContent-Length: 0
观察:
-
服务端日志:可能出现worker异常、崩溃,或在调试版/ASAN下直接报内存越界。
-
响应行为:可能直接断开连接、返回500,或返回异常状态码(取决于溢出是否立即破坏关键堆元数据)。
#
2.3 原理场景 C:触发崩溃/异常行为(MOVE)
MOVE复现方式类似,区别在于后续逻辑可能走rename / ext_rename_file分支,但目标路径的生成入口仍在ngx_http_map_uri_to_path(),因此同样可触发。
模拟 HTTP 流量
MOVE /dav/a.txt HTTP/1.1Host: 127.0.0.1:8080Destination: /davOverwrite: TContent-Length: 0
2.4 流量特征总结
• 方法: COPY 或 MOVE
• 必备头: Destination
• 关键特征: • Destination 采用绝对路径形式(以/ 开头)或同仓库绝对 URL(http://host/...) • Destination 的路径 短于当前 location 前缀长度(例如 location /dav/,Destination 却为 /dav) • 源 URI 与 Destination 的“collection 状态”一致(源码强制要求:尾部 / 同步)
- ## 还是附上一半的实验吧
0x3 漏洞原理分析
3.1-架构与模块定位:从 WebDAV 到核心路径映射
三个问题:
• 用户输入(HTTP 报文)在哪一层被接受?• 这个输入如何被转换成“文件系统路径”?• 最终是谁在做内存分配/拷贝?边界条件是否由同一处统一守住?
- 沿着这三个问题往下走,很快就能把链路收敛到两个核心点:WebDAV的COPY/MOVE处理与 HTTP核心模块的URI->path映射。
| 层级 | 核心文件 | 关键函数/职责 | 在漏洞链中的位置 |
| — | — | — | — |
| 入口层(协议/方法) | src/http/modules/ngx_http_dav_module.c | ngx_http_dav_handler() :按方法分发到 PUT/DELETE/MKCOL/COPY/MOVE | 选择 COPY/MOVE 分支 |
| 逻辑层(解析与路由) | src/http/modules/ngx_http_dav_module.c | ngx_http_dav_copy_move_handler() :解析 Destination,做安全 URI 解析,生成目标路径并执行复制/移动 | 注入点 :Destination |
| 驱动层(路径映射) | src/http/ngx_http_core_module.c | ngx_http_map_uri_to_path() :结合 root/alias 与 r->uri 计算 path->len 并拷贝 URI 到目标缓冲区 | 爆发点 :无符号下溢 + 堆拷贝越界 |
#
3.2-[核心入口] Destination 的“可信假设”:它被允许改变 r->uri
从COPY/MOVE的入口开始:ngx_http_dav_handler()在识别到NGX_HTTP_COPY/NGX_HTTP_MOVE后,统一进入ngx_http_dav_copy_move_handler()
在这个函数里,有一段关键但容易被忽略的操作:它会把Destination解析成duri,然后暂时替换 r->uri,让后续复用核心的路径映射函数生成目标路径:
static ngx_int_tngx_http_dav_copy_move_handler(ngx_http_request_t *r){ // ... dest = r->headers_in.destination; if (dest == NULL) { /* ... */ }
// 解析 Destination,得到 duri duri.len = last - p; duri.data = p; flags = NGX_HTTP_LOG_UNSAFE;
if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) { goto invalid_destination; }
// 先 map 源路径 if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
uri = r->uri; r->uri = duri;
// 再 map 目的路径(危险:r->uri 已经被 Destination 接管) if (ngx_http_map_uri_to_path(r, ©.path, &root, 0) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
r->uri = uri; // ...}
- *预期的安全边界:
Destination虽然来自客户端,但应该被限制在“同一仓库/同一location 语义”下(例如alias的扣除长度必须小于等于URI长度)。* - 实际代码的缺失:这里的
Destination只做了“URL 与 unsafe URI”的通用校验,却没有做“与当前location前缀的一致性校验”。一旦r->uri被换成更短的duri,后续核心映射函数就可能在alias分支里。
换句话说:WebDAV层把一个外部输入升级为了核心层的关键上下文(r->uri),但没有同时把核心层所依赖的不变量(r->uri.len >= alias)一并守住。
3.3-[爆发点] map_uri_to_path()的无符号下溢:r->uri.len - alias
接着看向最后一道失守的防线:ngx_http_map_uri_to_path()
在 root_lengths == NULL非脚本 root/alias,最常见路径)时,它直接计算需要的缓冲区长度并分配,然后把 root与r->uri拷到这块堆内存里:
u_char *
ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *path,
size_t *root_length, size_t reserved)
{
u_char *last;
size_t alias;
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
alias = clcf->alias;
if (alias && !r->valid_location) {
// ...
return NULL;
}
if (clcf->root_lengths == NULL) {
*root_length = clcf->root.len;
path->len = clcf->root.len + reserved + r->uri.len - alias + 1;
path->data = ngx_pnalloc(r->pool, path->len);
if (path->data == NULL) {
return NULL;
}
last = ngx_copy(path->data, clcf->root.data, clcf->root.len);
} else {
// ...
}
last = ngx_copy(last, r->uri.data + alias, r->uri.len - alias);
*last = '\0';
return last;
}
现在,关键点来了:alias是什么?它不是布尔值,像是一个 长度扣除量。当你在前缀location里配置alias时,clcf->alias会被赋值为location名称长度(例如location /dav/则 alias = 5),用于从r->uri中扣除/dav/这段前缀。
问题在于:这段代码没有任何检查来保证 r->uri.len >= alias。
当r->uri.len < alias时:
· r->uri.len - alias在size_t上发生无符号下溢,变成一个极大的数(例如(4-5)会变成(2^{N}-1)这种级别的值)。
· path->len = root.len + reserved + (r->uri.len - alias) + 1又在size_t上发生加法溢出回绕,最终得到一个看起来很小的分配长度。
· ngx_pnalloc()按这个“小长度”分配堆缓冲区。
· 随后ngx_copy(last, r->uri.data + alias, r->uri.len - alias)按“下溢后的巨大长度”执行拷贝,把堆缓冲区直接写穿。
- 预期设计:
alias扣除的是“location前缀长度”,因此理论上r->uri必须至少包含该前缀;否则这次映射就不会继续。 - 实际实现:把“前缀必然存在”当作前置条件,却没有在函数边界显式检查。平时这一假设成立,是因为正常路由匹配下
r->uri与clcf来源一致;但WebDAV的COPY/MOVE把Destinatio塞进了r->uri,让这个假设就会失效。
3.4-[触发条件] 为什么必须是 “dav_methods + COPY/MOVE + 前缀 location + alias”
把条件逐个还原,会发现一个有意思的:
1、必须是 COPY/MOVE:只有这两个方法会读取 Destination 并把它映射为目标路径(见 ngx_http_dav_copy_move_handler())。
2、必须启用 dav_methods 的 copy/move:否则请求根本进不了这条链路(ngx_http_dav_handler() 会 NGX_DECLINED)。
3、必须是前缀 location(非正则)+ alias:前缀 location 下 clcf->alias 通常是 clcf->name.len 的具体数值(例如 /dav/ => 5),而正则 location 的 alias 走 NGX_MAX_SIZE_T_VALUE等特殊分支,链路不同。
4、Destination 必须“短于” location 前缀:这是触发 r->uri.len - alias 下溢的最直接方式,例如: · location /dav/(alias=5) · Destination: /dav(r->uri.len=4) ·下溢成立:(4 - 5)(在 size_t 上回绕成极大值) /**原则上奥TZM**/
3.5-[攻击链路] 从堆溢出到“文件读写 / Webshell / RCE”的推演闭环
如何能扩大危害呢?“能否打到RCE”拆成两步:先看是否能形成稳定的内存破坏原语,再看是否能把它转化为可利用的控制流或文件落地。
- 第一:内存破坏原语这里的越界写发生在堆上,而且拷贝源来自
r->uri.data + alias,长度来自r->uri.len - alias(下溢后超大)。这意味着:
1、可以尝试通过构造超长Destination(以及精心控制其内容)影响覆盖数据?
2、覆盖范围可能跨越多个堆块/对象,取决于分配大小回绕后落在哪个size class?
- 第二:风险场景落地(在 WebDAV 场景下更危险)WebDAV 本身就是“文件系统操作接口”,一旦出现内存破坏,攻击面就从“崩溃”升级为“利用后执行更高危文件操作”:
1、任意文件写/覆盖:在 COPY/MOVE 流程中,目标路径一旦被破坏(或后续对象被篡改),可能将写入指向非预期位置。
2、Webshell 落地 -> RCE:若 Nginx 后端(或同主机上的其他组件)会执行某目录下的脚本/二进制,攻击者可尝试将 payload 写入可执行位置,从而实现代码执行。
3、DoS:即使无法稳定利用,堆元数据破坏往往可稳定导致 worker 崩溃,形成拒绝服务。
#
3.6-链路总结(注入点 -> 爆发点)
- 完整调用/数据链:
HTTP COPY/MOVE + Destination(**利用点**)-> `ngx_http_dav_handler()(方法分发)-> `ngx_http_dav_copy_move_handler()`(解析 `Destination` 得到 `duri`,并将 `r->uri = duri`)-> `ngx_http_map_uri_to_path()`(`alias` 分支计算 `r->uri.len - alias`)-> `path->len` 发生整数回绕(under-allocation)-> `ngx_copy(..., r->uri.len - alias)`(**爆发点:堆缓冲区溢出**)
0x4 修复建议
1、升级最新版本:将插件升级安全版本
https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-27654
2、临时防护措施:
- 防火墙 / WAF:检测并拦截求方法为COPY|MOVE且存在Destination头
- 限制访问:仅保留必要方法(例如只需要 PUT 就移除 copy/move)
免责声明:本文仅用于安全研究目的,未经授权不得用于非法渗透测试活动。
/**同志门!水了一张,希望不要介意哦**/
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:404号浪漫 404号浪漫 404号浪漫《NGINXDAV模块缓冲区溢出漏洞 | CVE-2026-27654原理分析&研究》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论