影响范围

Openssh Server 9.1

漏洞触发

漏洞利用很简单,在连接建立与版本协商的时候将 softwareid 设置成特定版本(如 PuTTY_Release_0.64)即可触发。

漏洞分析

这个漏洞的成因位于算法协商与密钥交换阶段:

// sshd.c 
	do_ssh2_kex(ssh);        # First Free
	do_authentication2(ssh); # Double Free

首先查看上游修复位置,主要修复了这一段代码,根据 BugZilla 中的报告,这里应该是第一次被 free 的位置:

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("original KEX proposal: %s", p);
	if ((ssh->compat & SSH_BUG_CURVE25519PAD) != 0)
		if ((p = match_filter_denylist(p,
		    "[email protected]")) == NULL)
			fatal("match_filter_denylist failed");
	if ((ssh->compat & SSH_OLD_DHGEX) != 0) {
		cp = p;
		if ((p = match_filter_denylist(p,
		    "diffie-hellman-group-exchange-sha256,"
		    "diffie-hellman-group-exchange-sha1")) == NULL)
			fatal("match_filter_denylist failed");
		free(cp);
	}
	debug2_f("compat KEX proposal: %s", p);
	if (*p == '\0')
		fatal("No supported key exchange algorithms found");
	return p;
}

SSH_OLD_DHGEX 被设置时,会将字符串 p 赋值给 cp 然后释放掉。此时传进来的字符串 p 是 option->kex_algorithms。此时这个指针已经变成了一个悬空指针。

崩溃现场位于 kex_assemble_names 函数。kex_assemble_names 函数在 assemble_algorithms 中被调用,传进来的参数也是 option->kex_algorithms 导致了 double free。

接下来看一下漏洞路径,重点在于 SSH_OLD_DHGEX 这个参数,这个参数是在 compat_banner 函数中定义的:

	static struct {
		char	*pat;
		int	bugs;
	} check[] = {
		... ...
		{ "PuTTY_Local:*,"	/* dev versions < Sep 2014 */
		  ... ...
		  "PuTTY_Release_0.64*",
					SSH_OLD_DHGEX },
		... ...
	};

除了 PuTTY 的旧版本外,理论上其它带有 SSH_OLD_DHGEX 标志的客户端也能触发漏洞。

补丁分析

补丁的做法很简单,删去了 cp=p 这一行,并修改了 check 逻辑,避免传入的 p 成为悬空指针。

参考资料