NGINXDAV模块缓冲区溢出漏洞|CVE-2026-27654原理分析&研究

admin 2026-04-25 04:42:46 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档分析了NGINXDAV模块缓冲区溢出漏洞CVE-2026-27654,指出在特定配置下(前缀location使用alias并启用dav_methods的copy/move方法),攻击者可通过构造短于location前缀的Destination请求头触发无符号下溢,导致堆缓冲区溢出。漏洞影响NGINX1.29.7和1.28.3以下版本,可能造成进程崩溃或远程代码执行。文档详细阐述了漏洞原理、复现步骤及流量特征,并建议升级至安全版本。 综合评分: 82 文章分类: 漏洞分析,WEB安全,应急响应,解决方案,技术标准


cover_image

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&nbsp;{&nbsp;listen&nbsp;8080;
# 前缀 location(非正则) + alias&nbsp;location&nbsp;/dav/{alias&nbsp;D:/webdav_root/;# Windows 示例;Linux 可用 /var/www/dav/&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;dav_methods&nbsp;put delete mkcol copy move;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;create_full_put_path&nbsp;on;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;dav_access&nbsp;user:rw group:rw all:r;&nbsp; &nbsp; }}

为什么这份配置会导致漏洞?(原则上)

  • 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&nbsp;/dav/a.txt HTTP/1.1Host:&nbsp;127.0.0.1:8080Destination: /davOverwrite: TContent-Length:&nbsp;0

观察:

  • 服务端日志:可能出现worker异常、崩溃,或在调试版/ASAN下直接报内存越界。

  • 响应行为:可能直接断开连接、返回500,或返回异常状态码(取决于溢出是否立即破坏关键堆元数据)。

#

2.3 原理场景 C:触发崩溃/异常行为(MOVE)

MOVE复现方式类似,区别在于后续逻辑可能走rename / ext_rename_file分支,但目标路径的生成入口仍在ngx_http_map_uri_to_path(),因此同样可触发。

模拟 HTTP 流量

MOVE&nbsp;/dav/a.txt HTTP/1.1Host:&nbsp;127.0.0.1:8080Destination: /davOverwrite: TContent-Length:&nbsp;0

2.4 流量特征总结

• 方法:&nbsp;COPY 或 MOVE
• 必备头:&nbsp;Destination
• 关键特征:&nbsp; • Destination 采用绝对路径形式(以/ 开头)或同仓库绝对 URL(http://host/...)&nbsp; • Destination 的路径 短于当前 location 前缀长度(例如 location /dav/,Destination 却为 /dav)&nbsp; • 源 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&nbsp;ngx_int_tngx_http_dav_copy_move_handler(ngx_http_request_t&nbsp;*r){&nbsp; &nbsp;&nbsp;// ...&nbsp; &nbsp; dest = r->headers_in.destination;&nbsp; &nbsp;&nbsp;if&nbsp;(dest ==&nbsp;NULL) {&nbsp;/* ... */&nbsp;}
&nbsp; &nbsp;&nbsp;// 解析 Destination,得到 duri&nbsp; &nbsp; duri.len = last - p;&nbsp; &nbsp; duri.data = p;&nbsp; &nbsp; flags = NGX_HTTP_LOG_UNSAFE;
&nbsp; &nbsp;&nbsp;if&nbsp;(ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;goto&nbsp;invalid_destination;&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;// 先 map 源路径&nbsp; &nbsp;&nbsp;if&nbsp;(ngx_http_map_uri_to_path(r, &path, &root,&nbsp;0) ==&nbsp;NULL) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;NGX_HTTP_INTERNAL_SERVER_ERROR;&nbsp; &nbsp; }
&nbsp; &nbsp; uri = r->uri;&nbsp; &nbsp; r->uri = duri;
&nbsp; &nbsp;&nbsp;// 再 map 目的路径(危险:r->uri 已经被 Destination 接管)&nbsp; &nbsp;&nbsp;if&nbsp;(ngx_http_map_uri_to_path(r, ©.path, &root,&nbsp;0) ==&nbsp;NULL) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;NGX_HTTP_INTERNAL_SERVER_ERROR;&nbsp; &nbsp; }
&nbsp; &nbsp; r->uri = uri;&nbsp; &nbsp;&nbsp;// ...}
  • *预期的安全边界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,最常见路径)时,它直接计算需要的缓冲区长度并分配,然后把 rootr->uri拷到这块堆内存里:

u_char *
ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *path,
&nbsp; &nbsp; size_t *root_length, size_t reserved)
{
&nbsp; &nbsp; u_char &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*last;
&nbsp; &nbsp; size_t &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; alias;
&nbsp; &nbsp; ngx_http_core_loc_conf_t &nbsp;*clcf;

&nbsp; &nbsp; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
&nbsp; &nbsp; alias = clcf->alias;

&nbsp; &nbsp; if (alias && !r->valid_location) {
&nbsp; &nbsp; &nbsp; &nbsp; // ...
&nbsp; &nbsp; &nbsp; &nbsp; return NULL;
&nbsp; &nbsp; }

&nbsp; &nbsp; if (clcf->root_lengths == NULL) {
&nbsp; &nbsp; &nbsp; &nbsp; *root_length = clcf->root.len;
&nbsp; &nbsp; &nbsp; &nbsp; path->len = clcf->root.len + reserved + r->uri.len - alias + 1;

&nbsp; &nbsp; &nbsp; &nbsp; path->data = ngx_pnalloc(r->pool, path->len);
&nbsp; &nbsp; &nbsp; &nbsp; if (path->data == NULL) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return NULL;
&nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp; last = ngx_copy(path->data, clcf->root.data, clcf->root.len);
&nbsp; &nbsp; } else {
&nbsp; &nbsp; &nbsp; &nbsp; // ...
&nbsp; &nbsp; }

&nbsp; &nbsp; last = ngx_copy(last, r->uri.data + alias, r->uri.len - alias);
&nbsp; &nbsp; *last = '\0';
&nbsp; &nbsp; 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&nbsp;- alias在size_t上发生无符号下溢,变成一个极大的数(例如(4-5)会变成(2^{N}-1)这种级别的值)。
· path->len&nbsp;= root.len&nbsp;+ reserved + (r->uri.len&nbsp;- alias) +&nbsp;1又在size_t上发生加法溢出回绕,最终得到一个看起来很小的分配长度。
· ngx_pnalloc()按这个“小长度”分配堆缓冲区。
· 随后ngx_copy(last, r->uri.data + alias, r->uri.len&nbsp;- alias)按“下溢后的巨大长度”执行拷贝,把堆缓冲区直接写穿。
  • 预期设计alias扣除的是“location前缀长度”,因此理论上r->uri必须至少包含该前缀;否则这次映射就不会继续。
  • 实际实现:把“前缀必然存在”当作前置条件,却没有在函数边界显式检查。平时这一假设成立,是因为正常路由匹配下r->uriclcf来源一致;但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(非正则)+&nbsp;alias:前缀 location 下 clcf->alias&nbsp;通常是 clcf->name.len 的具体数值(例如 /dav/ => 5),而正则 location 的&nbsp;alias&nbsp;走 NGX_MAX_SIZE_T_VALUE等特殊分支,链路不同。
4、Destination 必须“短于” location 前缀:这是触发 r->uri.len -&nbsp;alias&nbsp;下溢的最直接方式,例如:&nbsp; · location /dav/(alias=5)&nbsp; · Destination: /dav(r->uri.len=4)&nbsp; ·下溢成立:(4 - 5)(在 size_t 上回绕成极大值)&nbsp;&nbsp;&nbsp; /**原则上奥TZM**/

3.5-[攻击链路] 从堆溢出到“文件读写 / Webshell / RCE”的推演闭环

如何能扩大危害呢?“能否打到RCE”拆成两步:先看是否能形成稳定的内存破坏原语,再看是否能把它转化为可利用的控制流或文件落地。

  • 第一:内存破坏原语这里的越界写发生在堆上,而且拷贝源来自r->uri.data + alias,长度来自r->uri.len - alias(下溢后超大)。这意味着:
1、可以尝试通过构造超长Destination(以及精心控制其内容)影响覆盖数据?
2、覆盖范围可能跨越多个堆块/对象,取决于分配大小回绕后落在哪个size&nbsp;class?
  • 第二:风险场景落地(在 WebDAV 场景下更危险)WebDAV 本身就是“文件系统操作接口”,一旦出现内存破坏,攻击面就从“崩溃”升级为“利用后执行更高危文件操作”:
1、任意文件写/覆盖:在&nbsp;COPY/MOVE&nbsp;流程中,目标路径一旦被破坏(或后续对象被篡改),可能将写入指向非预期位置。
2、Webshell 落地 -> RCE:若 Nginx 后端(或同主机上的其他组件)会执行某目录下的脚本/二进制,攻击者可尝试将 payload 写入可执行位置,从而实现代码执行。
3、DoS:即使无法稳定利用,堆元数据破坏往往可稳定导致 worker 崩溃,形成拒绝服务。

#

3.6-链路总结(注入点 -> 爆发点)

  • 完整调用/数据链:
HTTP&nbsp;COPY/MOVE&nbsp;+&nbsp;Destination(**利用点**)->&nbsp;`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 -&nbsp;alias`)-> `path->len` 发生整数回绕(under-allocation)-> `ngx_copy(..., r->uri.len -&nbsp;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原理分析&研究》

评论:0   参与:  0