From 18ca8c90663b800a21bdce39dab4e402b8ad4bc0 Mon Sep 17 00:00:00 2001 From: Norbert Pocs Date: Wed, 27 Dec 2023 20:32:18 +0100 Subject: [PATCH 1/3] misc: Add function to check username syntax Malicious code can be injected using the username with metacharacters, therefore the username must be validated before using it with any %u. Signed-off-by: Norbert Pocs Reviewed-by: Jakub Jelen --- include/libssh/misc.h | 1 + src/misc.c | 32 ++++++++++++++++++++++++++++++++ tests/unittests/torture_misc.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/include/libssh/misc.h b/include/libssh/misc.h index a5bee930..c8328722 100644 --- a/include/libssh/misc.h +++ b/include/libssh/misc.h @@ -98,5 +98,6 @@ int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len); int ssh_newline_vis(const char *string, char *buf, size_t buf_len); int ssh_check_hostname_syntax(const char *hostname); +int ssh_check_username_syntax(const char *username); #endif /* MISC_H_ */ diff --git a/src/misc.c b/src/misc.c index 8bbb2587..23fad83d 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1835,4 +1835,36 @@ int ssh_check_hostname_syntax(const char *hostname) return SSH_OK; } +/** + * @brief Checks syntax of a username + * + * This check disallows metacharacters in the username + * + * @param username The username to be checked, has to be null terminated + * + * @return SSH_OK if the username passes syntax check + * SSH_ERROR otherwise or if username is NULL or empty string + */ +int ssh_check_username_syntax(const char *username) +{ + size_t username_len; + + if (username == NULL || *username == '-') { + return SSH_ERROR; + } + + username_len = strlen(username); + if (username_len == 0 || username[username_len - 1] == '\\' || + strpbrk(username, "'`\";&<>|(){}") != NULL) { + return SSH_ERROR; + } + for (size_t i = 0; i < username_len; i++) { + if (isspace(username[i]) != 0 && username[i + 1] == '-') { + return SSH_ERROR; + } + } + + return SSH_OK; +} + /** @} */ diff --git a/tests/unittests/torture_misc.c b/tests/unittests/torture_misc.c index f16b766e..3737ed27 100644 --- a/tests/unittests/torture_misc.c +++ b/tests/unittests/torture_misc.c @@ -735,6 +735,39 @@ static void torture_ssh_check_hostname_syntax(void **state) assert_int_equal(rc, SSH_ERROR); } +static void torture_ssh_check_username_syntax(void **state) { + int rc; + (void)state; + + rc = ssh_check_username_syntax("username"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_username_syntax("Alice"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_username_syntax("Alice and Bob"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_username_syntax("n4me?"); + assert_int_equal(rc, SSH_OK); + + rc = ssh_check_username_syntax("alice&bob"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_username_syntax("backslash\\"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_username_syntax("&var|()us\" Date: Thu, 28 Dec 2023 12:16:29 +0100 Subject: [PATCH 2/3] Check any input username for validity Check possible inputs of username for malicious code. Signed-off-by: Norbert Pocs Reviewed-by: Jakub Jelen --- src/config_parser.c | 4 ++++ src/misc.c | 10 ++++++++-- src/options.c | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/config_parser.c b/src/config_parser.c index 9e07a0b7..3c43b806 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -166,6 +166,10 @@ int ssh_config_parse_uri(const char *tok, if (*username == NULL) { goto error; } + rc = ssh_check_username_syntax(*username); + if (rc != SSH_OK) { + goto error; + } } tok = endp + 1; /* If there is second @ character, this does not look like our URI */ diff --git a/src/misc.c b/src/misc.c index 23fad83d..c1c4dcfa 100644 --- a/src/misc.c +++ b/src/misc.c @@ -178,6 +178,7 @@ int gettimeofday(struct timeval *__p, void *__t) { char *ssh_get_local_username(void) { DWORD size = 0; char *user; + int rc; /* get the size */ GetUserName(NULL, &size); @@ -188,7 +189,10 @@ char *ssh_get_local_username(void) { } if (GetUserName(user, &size)) { - return user; + rc = ssh_check_username_syntax(user); + if (rc == SSH_OK) { + return user; + } } return NULL; @@ -330,8 +334,10 @@ char *ssh_get_local_username(void) } name = strdup(pwd.pw_name); + rc = ssh_check_username_syntax(name); - if (name == NULL) { + if (rc != SSH_OK) { + free(name); return NULL; } diff --git a/src/options.c b/src/options.c index 082c6cf4..f7eaab42 100644 --- a/src/options.c +++ b/src/options.c @@ -595,6 +595,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, ssh_set_error_oom(session); return -1; } + rc = ssh_check_username_syntax(session->opts.username); + if (rc != SSH_OK) { + ssh_set_error_invalid(session); + return -1; + } } break; case SSH_OPTIONS_SSH_DIR: From bf16e4df1809e1fa12aaff2fab6c43b31a108011 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Thu, 2 Apr 2026 15:39:25 +0200 Subject: [PATCH 3/3] CVE-2026-59846 Block shell metacharacters from usernames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an attacker could sneak the dollar sign or backslash into the username expanded for example in proxy command, it can result in printing environment variables that might contain secrets. This is a fixup of CVE-2023-6004 which fixed this for hostnames, but these two metacharacters were left out from the username filter. This keeps the list in one place to simplify maintenance. Signed-off-by: Jakub Jelen Reviewed-by: Pavol Žáčik (cherry picked from commit 6309df220e3431deb41946f892f4bb5af8b59dba) --- include/libssh/priv.h | 2 ++ src/misc.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/libssh/priv.h b/include/libssh/priv.h index 3e910860..dee941c4 100644 --- a/include/libssh/priv.h +++ b/include/libssh/priv.h @@ -218,6 +218,8 @@ int gettimeofday(struct timeval *__p, void *__t); # define LIBSSH_MEM_PROTECTION #endif +#define SSH_DANGEROUS_SHELL_CHARS "'`\";&<>|(){}$\\," + /* forward declarations */ struct ssh_common_struct; struct ssh_kex_struct; diff --git a/src/misc.c b/src/misc.c index c1c4dcfa..11f799e1 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1861,7 +1861,7 @@ int ssh_check_username_syntax(const char *username) username_len = strlen(username); if (username_len == 0 || username[username_len - 1] == '\\' || - strpbrk(username, "'`\";&<>|(){}") != NULL) { + strpbrk(username, SSH_DANGEROUS_SHELL_CHARS) != NULL) { return SSH_ERROR; } for (size_t i = 0; i < username_len; i++) {