CVE-2023-25136: Double Free (CWE-415)
value: OpenSSH server (sshd) 9.1 introduced a double-free vulnerability during options.kex_algorithms handling. This is fixed in OpenSSH 9.2. The double free can be leveraged, by an unauthenticated remote attacker in the default configuration, to jump to any location in the sshd address space. One third-party report states "remote code execution is theoretically possible."
CWE description: The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.
CVSS v3 base score: 6.5
Published: 2023-02-03T06:15Z
Repository: https://github.com/openssh/openssh-portable
Language: C
Files changed: compat.c
Commit date: 2022-07-01 13:41:16 +1000
Commit message: upstream: Always return allocated strings from the kex filtering so

that we can free them later.  Fix one leak in compat_kex_proposal.  Based on
github PR#324 from ZoltanFridrich with some simplications by me. ok djm@

OpenBSD-Commit-ID: 9171616da3307612d0ede086fd511142f91246e4

Diff: diff --git a/compat.c b/compat.c
index 0dbea68c62..46dfe3a9c2 100644
--- a/compat.c
+++ b/compat.c
@@ -1,6 +1,6 @@
-/* $OpenBSD: compat.c,v 1.119 2021/09/10 05:46:09 djm Exp $ */
+/* $OpenBSD: compat.c,v 1.120 2022/07/01 03:35:45 dtucker Exp $ */
 /*
  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -154,53 +154,60 @@ compat_banner(struct ssh *ssh, const char *version)
 		}
 	}
 	debug_f("no match: %s", version);
 }
 
+/* Always returns pointer to allocated memory, caller must free. */
 char *
 compat_cipher_proposal(struct ssh *ssh, char *cipher_prop)
 {
 	if (!(ssh->compat & SSH_BUG_BIGENDIANAES))
-		return cipher_prop;
+		return xstrdup(cipher_prop);
 	debug2_f("original cipher proposal: %s", cipher_prop);
 	if ((cipher_prop = match_filter_denylist(cipher_prop, "aes*")) == NULL)
 		fatal("match_filter_denylist failed");
 	debug2_f("compat cipher proposal: %s", cipher_prop);
 	if (*cipher_prop == '\0')
 		fatal("No supported ciphers found");
 	return cipher_prop;
 }
 
+/* Always returns pointer to allocated memory, caller must free. */
 char *
 compat_pkalg_proposal(struct ssh *ssh, char *pkalg_prop)
 {
 	if (!(ssh->compat & SSH_BUG_RSASIGMD5))
-		return pkalg_prop;
+		return xstrdup(pkalg_prop);
 	debug2_f("original public key proposal: %s", pkalg_prop);
 	if ((pkalg_prop = match_filter_denylist(pkalg_prop, "ssh-rsa")) == NULL)
 		fatal("match_filter_denylist failed");
 	debug2_f("compat public key proposal: %s", pkalg_prop);
 	if (*pkalg_prop == '\0')
 		fatal("No supported PK algorithms found");
 	return pkalg_prop;
 }
 
+/* Always returns pointer to allocated memory, caller must free. */
 char *
 compat_kex_proposal(struct ssh *ssh, char *p)
 {
+	char *cp = NULL;
+
 	if ((ssh->compat & (SSH_BUG_CURVE25519PAD|SSH_OLD_DHGEX)) == 0)
-		return p;
+		return xstrdup(p);
 	debug2_f("original KEX proposal: %s", p);
 	if ((ssh->compat & SSH_BUG_CURVE25519PAD) != 0)
 		if ((p = match_filter_denylist(p,
 		    "curve25519-sha256@libssh.org")) == 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;

Vulnerable code: /* $OpenBSD: compat.c,v 1.119 2021/09/10 05:46:09 djm Exp $ */
		return cipher_prop;
		return pkalg_prop;
		return p;
Fixed code: /* $OpenBSD: compat.c,v 1.120 2022/07/01 03:35:45 dtucker Exp $ */
/* Always returns pointer to allocated memory, caller must free. */
		return xstrdup(cipher_prop);
/* Always returns pointer to allocated memory, caller must free. */
		return xstrdup(pkalg_prop);
/* Always returns pointer to allocated memory, caller must free. */
	char *cp = NULL;

		return xstrdup(p);
		cp = p;
		free(cp);