From 391a442cc350cec07c7e056bf498c8d03e9396ae Mon Sep 17 00:00:00 2001 From: Fabrice Fontaine Date: Mon, 1 Jan 2024 23:08:18 +0100 Subject: [PATCH] package/dropbear: fix CVE-2023-48795 https://github.com/advisories/GHSA-45x7-px36-x8w8 Signed-off-by: Fabrice Fontaine Signed-off-by: Thomas Petazzoni --- .../0001-Implement-Strict-KEX-mode.patch | 232 ++++++++++++++++++ package/dropbear/dropbear.mk | 3 + 2 files changed, 235 insertions(+) create mode 100644 package/dropbear/0001-Implement-Strict-KEX-mode.patch diff --git a/package/dropbear/0001-Implement-Strict-KEX-mode.patch b/package/dropbear/0001-Implement-Strict-KEX-mode.patch new file mode 100644 index 0000000000..ce7b84861c --- /dev/null +++ b/package/dropbear/0001-Implement-Strict-KEX-mode.patch @@ -0,0 +1,232 @@ +From 6e43be5c7b99dbee49dc72b6f989f29fdd7e9356 Mon Sep 17 00:00:00 2001 +From: Matt Johnston +Date: Mon, 20 Nov 2023 14:02:47 +0800 +Subject: [PATCH] Implement Strict KEX mode + +As specified by OpenSSH with kex-strict-c-v00@openssh.com and +kex-strict-s-v00@openssh.com. + +Upstream: https://github.com/mkj/dropbear/commit/6e43be5c7b99dbee49dc72b6f989f29fdd7e9356 +Signed-off-by: Fabrice Fontaine +--- + src/cli-session.c | 11 +++++++++++ + src/common-algo.c | 6 ++++++ + src/common-kex.c | 26 +++++++++++++++++++++++++- + src/kex.h | 3 +++ + src/process-packet.c | 34 +++++++++++++++++++--------------- + src/ssh.h | 4 ++++ + src/svr-session.c | 3 +++ + 7 files changed, 71 insertions(+), 16 deletions(-) + +diff --git a/cli-session.c b/cli-session.c +index 5981b2470..d261c8f82 100644 +--- a/cli-session.c ++++ b/cli-session.c +@@ -46,6 +46,7 @@ static void cli_finished(void) ATTRIB_NORETURN; + static void recv_msg_service_accept(void); + static void cli_session_cleanup(void); + static void recv_msg_global_request_cli(void); ++static void cli_algos_initialise(void); + + struct clientsession cli_ses; /* GLOBAL */ + +@@ -117,6 +118,7 @@ void cli_session(int sock_in, int sock_out, struct dropbear_progress_connection + } + + chaninitialise(cli_chantypes); ++ cli_algos_initialise(); + + /* Set up cli_ses vars */ + cli_session_init(proxy_cmd_pid); +@@ -487,3 +489,12 @@ void cli_dropbear_log(int priority, const char* format, va_list param) { + fflush(stderr); + } + ++static void cli_algos_initialise(void) { ++ algo_type *algo; ++ for (algo = sshkex; algo->name; algo++) { ++ if (strcmp(algo->name, SSH_STRICT_KEX_S) == 0) { ++ algo->usable = 0; ++ } ++ } ++} ++ +diff --git a/common-algo.c b/common-algo.c +index 378f0ca8e..f9d46ebb6 100644 +--- a/common-algo.c ++++ b/common-algo.c +@@ -307,6 +307,12 @@ algo_type sshkex[] = { + /* Set unusable by svr_algos_initialise() */ + {SSH_EXT_INFO_C, 0, NULL, 1, NULL}, + #endif ++#endif ++#if DROPBEAR_CLIENT ++ {SSH_STRICT_KEX_C, 0, NULL, 1, NULL}, ++#endif ++#if DROPBEAR_SERVER ++ {SSH_STRICT_KEX_S, 0, NULL, 1, NULL}, + #endif + {NULL, 0, NULL, 0, NULL} + }; +diff --git a/common-kex.c b/common-kex.c +index ac8844246..8e33b12a6 100644 +--- a/common-kex.c ++++ b/common-kex.c +@@ -183,6 +183,10 @@ void send_msg_newkeys() { + gen_new_keys(); + switch_keys(); + ++ if (ses.kexstate.strict_kex) { ++ ses.transseq = 0; ++ } ++ + TRACE(("leave send_msg_newkeys")) + } + +@@ -193,7 +197,11 @@ void recv_msg_newkeys() { + + ses.kexstate.recvnewkeys = 1; + switch_keys(); +- ++ ++ if (ses.kexstate.strict_kex) { ++ ses.recvseq = 0; ++ } ++ + TRACE(("leave recv_msg_newkeys")) + } + +@@ -550,6 +558,10 @@ void recv_msg_kexinit() { + + ses.kexstate.recvkexinit = 1; + ++ if (ses.kexstate.strict_kex && !ses.kexstate.donefirstkex && ses.recvseq != 1) { ++ dropbear_exit("First packet wasn't kexinit"); ++ } ++ + TRACE(("leave recv_msg_kexinit")) + } + +@@ -859,6 +871,18 @@ static void read_kex_algos() { + } + #endif + ++ if (!ses.kexstate.donefirstkex) { ++ const char* strict_name; ++ if (IS_DROPBEAR_CLIENT) { ++ strict_name = SSH_STRICT_KEX_S; ++ } else { ++ strict_name = SSH_STRICT_KEX_C; ++ } ++ if (buf_has_algo(ses.payload, strict_name) == DROPBEAR_SUCCESS) { ++ ses.kexstate.strict_kex = 1; ++ } ++ } ++ + algo = buf_match_algo(ses.payload, sshkex, kexguess2, &goodguess); + allgood &= goodguess; + if (algo == NULL || algo->data == NULL) { +diff --git a/kex.h b/kex.h +index 77cf21a37..7fcc3c252 100644 +--- a/kex.h ++++ b/kex.h +@@ -83,6 +83,9 @@ struct KEXState { + + unsigned our_first_follows_matches : 1; + ++ /* Boolean indicating that strict kex mode is in use */ ++ unsigned int strict_kex; ++ + time_t lastkextime; /* time of the last kex */ + unsigned int datatrans; /* data transmitted since last kex */ + unsigned int datarecv; /* data received since last kex */ +diff --git a/process-packet.c b/process-packet.c +index 945416023..133a152d0 100644 +--- a/process-packet.c ++++ b/process-packet.c +@@ -44,6 +44,7 @@ void process_packet() { + + unsigned char type; + unsigned int i; ++ unsigned int first_strict_kex = ses.kexstate.strict_kex && !ses.kexstate.donefirstkex; + time_t now; + + TRACE2(("enter process_packet")) +@@ -54,22 +55,24 @@ void process_packet() { + now = monotonic_now(); + ses.last_packet_time_keepalive_recv = now; + +- /* These packets we can receive at any time */ +- switch(type) { + +- case SSH_MSG_IGNORE: +- goto out; +- case SSH_MSG_DEBUG: +- goto out; ++ if (type == SSH_MSG_DISCONNECT) { ++ /* Allowed at any time */ ++ dropbear_close("Disconnect received"); ++ } + +- case SSH_MSG_UNIMPLEMENTED: +- /* debugging XXX */ +- TRACE(("SSH_MSG_UNIMPLEMENTED")) +- goto out; +- +- case SSH_MSG_DISCONNECT: +- /* TODO cleanup? */ +- dropbear_close("Disconnect received"); ++ /* These packets may be received at any time, ++ except during first kex with strict kex */ ++ if (!first_strict_kex) { ++ switch(type) { ++ case SSH_MSG_IGNORE: ++ goto out; ++ case SSH_MSG_DEBUG: ++ goto out; ++ case SSH_MSG_UNIMPLEMENTED: ++ TRACE(("SSH_MSG_UNIMPLEMENTED")) ++ goto out; ++ } + } + + /* Ignore these packet types so that keepalives don't interfere with +@@ -98,7 +101,8 @@ void process_packet() { + if (type >= 1 && type <= 49 + && type != SSH_MSG_SERVICE_REQUEST + && type != SSH_MSG_SERVICE_ACCEPT +- && type != SSH_MSG_KEXINIT) ++ && type != SSH_MSG_KEXINIT ++ && !first_strict_kex) + { + TRACE(("unknown allowed packet during kexinit")) + recv_unimplemented(); +diff --git a/ssh.h b/ssh.h +index 1b4fec65f..ef3efdca0 100644 +--- a/ssh.h ++++ b/ssh.h +@@ -100,6 +100,10 @@ + #define SSH_EXT_INFO_C "ext-info-c" + #define SSH_SERVER_SIG_ALGS "server-sig-algs" + ++/* OpenSSH strict KEX feature */ ++#define SSH_STRICT_KEX_S "kex-strict-s-v00@openssh.com" ++#define SSH_STRICT_KEX_C "kex-strict-c-v00@openssh.com" ++ + /* service types */ + #define SSH_SERVICE_USERAUTH "ssh-userauth" + #define SSH_SERVICE_USERAUTH_LEN 12 +diff --git a/svr-session.c b/svr-session.c +index 769f0731d..a538e2c5c 100644 +--- a/svr-session.c ++++ b/svr-session.c +@@ -370,6 +370,9 @@ static void svr_algos_initialise(void) { + algo->usable = 0; + } + #endif ++ if (strcmp(algo->name, SSH_STRICT_KEX_C) == 0) { ++ algo->usable = 0; ++ } + } + } + diff --git a/package/dropbear/dropbear.mk b/package/dropbear/dropbear.mk index 36eb7898ba..7ac8a38559 100644 --- a/package/dropbear/dropbear.mk +++ b/package/dropbear/dropbear.mk @@ -14,6 +14,9 @@ DROPBEAR_PROGRAMS = dropbear $(DROPBEAR_TARGET_BINS) DROPBEAR_CPE_ID_VENDOR = dropbear_ssh_project DROPBEAR_CPE_ID_PRODUCT = dropbear_ssh +# 0001-Implement-Strict-KEX-mode.patch +DROPBEAR_IGNORE_CVES += CVE-2023-48795 + # Disable hardening flags added by dropbear configure.ac, and let # Buildroot add them when the relevant options are enabled. This # prevents dropbear from using SSP support when not available.