[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Bugs when using rsa-sha2 (+patches)
[Thread Prev] | [Thread Next]
- Subject: Re: Bugs when using rsa-sha2 (+patches)
- From: Tilo Eckert <tilo.eckert@xxxxxxx>
- Reply-to: libssh@xxxxxxxxxx
- Date: Wed, 21 Nov 2018 12:03:34 +0100
- To: libssh@xxxxxxxxxx
Hi Jakub, the new patch set contains my previous rsa-sha2 related patches as well as fixes for the rsa-sha2 downgrade / ssh-rsa upgrade issues and the ECDSA signature bypass when ECC is turned off. Please check if my fixes are sufficient. Initially I attempted to verify hostkey and signature algos separately, but that didn't work out very well. It would have meant a lot of redundancy. After realizing that hostkey and signature need to be considered together, performing the necessary checks in the new hostkey type validation function seemed straight-forward. If you or someone else has an idea on how to talk OpenSSH into being a naughty server or if you can come up with some other way to emulate an rsa-sha2 downgrade attack, feel free to add a test. Regards, Tilo >> The function ssh_pki_signature_verify_blob() is used by server >> (messages.c) and client code (packet_cb.c). So, I think one way to >> fix >> it is to remove the ssh_pki_import_signature_blob() call from >> ssh_pki_signature_verify_blob(), move it to the two callers and pass >> sig >> instead of sig_blob to the verify function. That would allow us to >> check >> the signature type in ssh_packet_newkeys() depending on the allowed >> hostkey types. > > That sounds like a way to go. The ssh_pki_signature_verify_blob() > unfortunately does not have slightest idea what is the allowed list to > verify against. > > We will not have the same problem in the server (messages.c), because > server does not have any configuration which would allow to disable > some authentication algorithms. > >> I think there may be another unrelated security issue in >> ssh_pki_signature_verify_blob(): When compiling without ECC support >> and >> the server sends an unexpected ECDSA signature, it would be imported >> successfully, "if (key->type == SSH_KEYTYPE_ECDSA)" would be true, >> but >> the block does nothing. The function returns rc, which is still 0 >> from >> the import. Maybe I missed something, but this looks to me like a >> signature verification bypass. We should return SSH_ERROR if ECC is >> unsupported. > > I think you are right here. Setting the rc = SSH_ERROR in the else > branch should fix this problem, but there might be more issues like > this throughout the code. > > Would you like to submit a patch for these two issues? > > Thanks, >
From 463e7cd33561ccbf59570da662ba02fd7279731c Mon Sep 17 00:00:00 2001
From: Tilo Eckert <tilo.eckert@xxxxxxx>
Date: Tue, 13 Nov 2018 15:45:47 +0100
Subject: [PATCH 1/7] pki: Add hostkey type validation function
Signed-off-by: Tilo Eckert <tilo.eckert@xxxxxxx>
---
include/libssh/pki.h | 1 +
src/pki.c | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/include/libssh/pki.h b/include/libssh/pki.h
index 241cfdd1..af56f11a 100644
--- a/include/libssh/pki.h
+++ b/include/libssh/pki.h
@@ -140,4 +140,5 @@ ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key);
ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key);
int ssh_key_algorithm_allowed(ssh_session session, const char *type);
+int ssh_hostkey_algorithm_allowed(ssh_session session, const char *type);
#endif /* PKI_H_ */
diff --git a/src/pki.c b/src/pki.c
index 623c70f2..232327fa 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -296,6 +296,43 @@ int ssh_key_algorithm_allowed(ssh_session session, const char *type)
return ssh_match_group(allowed_list, type);
}
+/**
+ * @brief Checks the given host key against the configured allowed
+ * host key algorithm types
+ *
+ * @param[in] session The SSH session
+ * @param[in] type The host key algorithm to check
+ * @returns 1 if the host key algorithm is allowed, 0 otherwise
+ */
+int ssh_hostkey_algorithm_allowed(ssh_session session, const char *type)
+{
+ const char *allowed_types;
+
+ allowed_types = session->opts.wanted_methods[SSH_HOSTKEYS];
+ if (allowed_types == NULL) {
+ return 0; /* should never get here */
+ }
+
+ if (ssh_match_group(allowed_types, type)) {
+ return 1;
+ }
+ if (strcmp(type, "ssh-rsa") == 0) {
+ /*
+ * If rsa-sha2 hostkey algo is used, the server returns ssh-rsa as
+ * host key type. Hence, we need to check if one of the rsa-sha2
+ * variants is in the allowed hostkey types list.
+ */
+ if (ssh_match_group(allowed_types, "rsa-sha2-256")) {
+ return 1;
+ }
+ if (ssh_match_group(allowed_types, "rsa-sha2-512")) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/**
* @brief Convert a key type to a hash type. This is usually unambiguous
* for all the key types, unless the SHA2 extension (RFC 8332) is
--
2.18.0
From 29e5a998615b43090b3b72655022bd41a9ae41c2 Mon Sep 17 00:00:00 2001
From: Tilo Eckert <tilo.eckert@xxxxxxx>
Date: Tue, 13 Nov 2018 15:49:48 +0100
Subject: [PATCH 2/7] packet: Use new hostkey type validation function
The new function accepts hostkeys of type ssh-rsa
if rsa-sha2-256/512 is the only accepted hostkey type
Signed-off-by: Tilo Eckert <tilo.eckert@xxxxxxx>
---
src/packet_cb.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/packet_cb.c b/src/packet_cb.c
index af5b966c..784ca33d 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -190,8 +190,7 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
/* check if public key from server matches user preferences */
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
- if(!ssh_match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
- server_key->type_c)) {
+ if (!ssh_hostkey_algorithm_allowed(session, server_key->type_c)) {
ssh_set_error(session,
SSH_FATAL,
"Public key from server (%s) doesn't match user "
--
2.18.0
From 8327c3598c35b3834769c822b2849570eb778e30 Mon Sep 17 00:00:00 2001
From: Tilo Eckert <tilo.eckert@xxxxxxx>
Date: Tue, 13 Nov 2018 15:50:48 +0100
Subject: [PATCH 3/7] tests: Fix rsa-sha2 tests
Signed-off-by: Tilo Eckert <tilo.eckert@xxxxxxx>
---
tests/client/torture_hostkey.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/client/torture_hostkey.c b/tests/client/torture_hostkey.c
index 928137d5..a930a293 100644
--- a/tests/client/torture_hostkey.c
+++ b/tests/client/torture_hostkey.c
@@ -163,7 +163,7 @@ static void torture_hostkey_ecdsa(void **state) {
static void torture_hostkey_rsa_sha256(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
- char rsa[] = "rsa-sha2-256,ssh-rsa";
+ char rsa[] = "rsa-sha2-256";
int rc;
@@ -182,7 +182,7 @@ static void torture_hostkey_rsa_sha256(void **state) {
static void torture_hostkey_rsa_sha512(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
- char rsa[] = "rsa-sha2-512,ssh-rsa";
+ char rsa[] = "rsa-sha2-512";
int rc;
--
2.18.0
From 2daf48ca2c16edf53f7f891e0b4424a8448afdd4 Mon Sep 17 00:00:00 2001
From: Tilo Eckert <tilo.eckert@xxxxxxx>
Date: Tue, 20 Nov 2018 10:22:33 +0100
Subject: [PATCH 4/7] pki: Return SSH_ERROR if ECC is disabled instead of
accepting unverified signature
Signed-off-by: Tilo Eckert <tilo.eckert@xxxxxxx>
---
src/pki.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/pki.c b/src/pki.c
index 232327fa..2fa053d4 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -1992,6 +1992,8 @@ int ssh_pki_signature_verify_blob(ssh_session session,
key,
ehash,
elen);
+#else
+ rc = SSH_ERROR;
#endif
} else if (key->type == SSH_KEYTYPE_ED25519) {
rc = pki_signature_verify(session, sig, key, digest, dlen);
--
2.18.0
From 18ff296ba6eed8f897fdae7e42cd1effe3006f8d Mon Sep 17 00:00:00 2001
From: Tilo Eckert <tilo.eckert@xxxxxxx>
Date: Wed, 21 Nov 2018 10:56:14 +0100
Subject: [PATCH 5/7] pki: Move signature import to callers of
ssh_pki_signature_verify_blob()
Signed-off-by: Tilo Eckert <tilo.eckert@xxxxxxx>
---
include/libssh/pki.h | 2 +-
src/messages.c | 23 ++++++++++++++++-------
src/packet_cb.c | 17 +++++++++++++----
src/pki.c | 11 +----------
4 files changed, 31 insertions(+), 22 deletions(-)
diff --git a/include/libssh/pki.h b/include/libssh/pki.h
index af56f11a..f345e9f4 100644
--- a/include/libssh/pki.h
+++ b/include/libssh/pki.h
@@ -111,7 +111,7 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob,
const ssh_key pubkey,
ssh_signature *psig);
int ssh_pki_signature_verify_blob(ssh_session session,
- ssh_string sig_blob,
+ ssh_signature sig,
const ssh_key key,
unsigned char *digest,
size_t dlen);
diff --git a/src/messages.c b/src/messages.c
index 9c3e0672..53651a93 100644
--- a/src/messages.c
+++ b/src/messages.c
@@ -730,6 +730,7 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
*/
SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
ssh_message msg = NULL;
+ ssh_signature sig = NULL;
char *service = NULL;
char *method = NULL;
int cmp;
@@ -863,17 +864,25 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
goto error;
}
- rc = ssh_pki_signature_verify_blob(session,
- sig_blob,
- msg->auth_request.pubkey,
- ssh_buffer_get(digest),
- ssh_buffer_get_len(digest));
+ rc = ssh_pki_import_signature_blob(sig_blob, msg->auth_request.pubkey, &sig);
+ if (rc == SSH_OK) {
+ rc = ssh_pki_signature_verify_blob(session,
+ sig,
+ msg->auth_request.pubkey,
+ ssh_buffer_get(digest),
+ ssh_buffer_get_len(digest));
+ }
+
ssh_string_free(sig_blob);
ssh_buffer_free(digest);
+ ssh_signature_free(sig);
+ sig_blob = NULL;
+ digest = NULL;
+ sig = NULL;
if (rc < 0) {
SSH_LOG(
SSH_LOG_PACKET,
- "Received an invalid signature from peer");
+ "Received an invalid signature from peer");
msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG;
goto error;
}
@@ -1124,7 +1133,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){
ssh_set_error(session,SSH_FATAL, "Invalid state when receiving channel open request (must be authenticated)");
goto error;
}
-
+
if (strcmp(type_c,"session") == 0) {
msg->channel_request_open.type = SSH_CHANNEL_SESSION;
SAFE_FREE(type_c);
diff --git a/src/packet_cb.c b/src/packet_cb.c
index 784ca33d..1d70951e 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -138,6 +138,7 @@ error:
SSH_PACKET_CALLBACK(ssh_packet_newkeys){
ssh_string sig_blob = NULL;
+ ssh_signature sig = NULL;
int rc;
(void)packet;
(void)user;
@@ -188,6 +189,13 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
return SSH_ERROR;
}
+ /* get the server signature */
+ rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
+ ssh_string_burn(sig_blob);
+ if (rc < 0) {
+ goto error;
+ }
+
/* check if public key from server matches user preferences */
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
if (!ssh_hostkey_algorithm_allowed(session, server_key->type_c)) {
@@ -197,18 +205,19 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
"preference (%s)",
server_key->type_c,
session->opts.wanted_methods[SSH_HOSTKEYS]);
+ ssh_signature_free(sig);
+ sig = NULL;
goto error;
}
}
rc = ssh_pki_signature_verify_blob(session,
- sig_blob,
+ sig,
server_key,
session->next_crypto->secret_hash,
session->next_crypto->digest_len);
- ssh_string_burn(sig_blob);
- ssh_string_free(sig_blob);
- sig_blob = NULL;
+ ssh_signature_free(sig);
+ sig = NULL;
if (rc == SSH_ERROR) {
goto error;
}
diff --git a/src/pki.c b/src/pki.c
index 2fa053d4..d78216bc 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -1957,24 +1957,17 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob,
}
int ssh_pki_signature_verify_blob(ssh_session session,
- ssh_string sig_blob,
+ ssh_signature sig,
const ssh_key key,
unsigned char *digest,
size_t dlen)
{
- ssh_signature sig;
int rc;
- rc = ssh_pki_import_signature_blob(sig_blob, key, &sig);
- if (rc < 0) {
- return SSH_ERROR;
- }
-
SSH_LOG(SSH_LOG_FUNCTIONS,
"Going to verify a %s type signature",
sig->type_c);
-
if (key->type == SSH_KEYTYPE_ECDSA) {
#if HAVE_ECC
unsigned char ehash[EVP_DIGEST_LEN] = {0};
@@ -2039,8 +2032,6 @@ int ssh_pki_signature_verify_blob(ssh_session session,
hlen);
}
- ssh_signature_free(sig);
-
return rc;
}
--
2.18.0
From e6f6d14e155ceafd51f5d0da67c8810044ad2d2d Mon Sep 17 00:00:00 2001
From: Tilo Eckert <tilo.eckert@xxxxxxx>
Date: Wed, 21 Nov 2018 11:03:59 +0100
Subject: [PATCH 6/7] pki: Rename ssh_pki_signature_verify_blob() to
ssh_pki_signature_verify()
Signed-off-by: Tilo Eckert <tilo.eckert@xxxxxxx>
---
include/libssh/pki.h | 10 +++++-----
src/messages.c | 10 +++++-----
src/packet_cb.c | 10 +++++-----
src/pki.c | 10 +++++-----
4 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/include/libssh/pki.h b/include/libssh/pki.h
index f345e9f4..4839e5c1 100644
--- a/include/libssh/pki.h
+++ b/include/libssh/pki.h
@@ -110,11 +110,11 @@ int ssh_pki_export_signature_blob(const ssh_signature sign,
int ssh_pki_import_signature_blob(const ssh_string sig_blob,
const ssh_key pubkey,
ssh_signature *psig);
-int ssh_pki_signature_verify_blob(ssh_session session,
- ssh_signature sig,
- const ssh_key key,
- unsigned char *digest,
- size_t dlen);
+int ssh_pki_signature_verify(ssh_session session,
+ ssh_signature sig,
+ const ssh_key key,
+ unsigned char *digest,
+ size_t dlen);
/* SSH Public Key Functions */
int ssh_pki_export_pubkey_blob(const ssh_key key,
diff --git a/src/messages.c b/src/messages.c
index 53651a93..437494ce 100644
--- a/src/messages.c
+++ b/src/messages.c
@@ -866,11 +866,11 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
rc = ssh_pki_import_signature_blob(sig_blob, msg->auth_request.pubkey, &sig);
if (rc == SSH_OK) {
- rc = ssh_pki_signature_verify_blob(session,
- sig,
- msg->auth_request.pubkey,
- ssh_buffer_get(digest),
- ssh_buffer_get_len(digest));
+ rc = ssh_pki_signature_verify(session,
+ sig,
+ msg->auth_request.pubkey,
+ ssh_buffer_get(digest),
+ ssh_buffer_get_len(digest));
}
ssh_string_free(sig_blob);
diff --git a/src/packet_cb.c b/src/packet_cb.c
index 1d70951e..c7a878bd 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -211,11 +211,11 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
}
}
- rc = ssh_pki_signature_verify_blob(session,
- sig,
- server_key,
- session->next_crypto->secret_hash,
- session->next_crypto->digest_len);
+ rc = ssh_pki_signature_verify(session,
+ sig,
+ server_key,
+ session->next_crypto->secret_hash,
+ session->next_crypto->digest_len);
ssh_signature_free(sig);
sig = NULL;
if (rc == SSH_ERROR) {
diff --git a/src/pki.c b/src/pki.c
index d78216bc..a72e8f49 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -1956,11 +1956,11 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob,
return SSH_OK;
}
-int ssh_pki_signature_verify_blob(ssh_session session,
- ssh_signature sig,
- const ssh_key key,
- unsigned char *digest,
- size_t dlen)
+int ssh_pki_signature_verify(ssh_session session,
+ ssh_signature sig,
+ const ssh_key key,
+ unsigned char *digest,
+ size_t dlen)
{
int rc;
--
2.18.0
From 319325fefc8322a4194308c0bdd7f48f8a19e15a Mon Sep 17 00:00:00 2001
From: Tilo Eckert <tilo.eckert@xxxxxxx>
Date: Wed, 21 Nov 2018 11:09:21 +0100
Subject: [PATCH 7/7] pki: Prevent downgrade to SHA-1 if rsa-sha2 hostkeys are
allowed, but not ssh-rsa
Signed-off-by: Tilo Eckert <tilo.eckert@xxxxxxx>
---
include/libssh/pki.h | 5 ++++-
src/packet_cb.c | 4 ++--
src/pki.c | 44 +++++++++++++++++++++++++++-----------------
3 files changed, 33 insertions(+), 20 deletions(-)
diff --git a/include/libssh/pki.h b/include/libssh/pki.h
index 4839e5c1..281ff6c3 100644
--- a/include/libssh/pki.h
+++ b/include/libssh/pki.h
@@ -140,5 +140,8 @@ ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key);
ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key);
int ssh_key_algorithm_allowed(ssh_session session, const char *type);
-int ssh_hostkey_algorithm_allowed(ssh_session session, const char *type);
+int ssh_hostkey_algorithm_allowed(ssh_session session,
+ const char *type,
+ enum ssh_keytypes_e sig_key_type,
+ enum ssh_digest_e sig_hash_type);
#endif /* PKI_H_ */
diff --git a/src/packet_cb.c b/src/packet_cb.c
index c7a878bd..79462a11 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -196,9 +196,9 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
goto error;
}
- /* check if public key from server matches user preferences */
+ /* check if public key type and signature type from server match user preferences */
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
- if (!ssh_hostkey_algorithm_allowed(session, server_key->type_c)) {
+ if (!ssh_hostkey_algorithm_allowed(session, server_key->type_c, sig->type, sig->hash_type)) {
ssh_set_error(session,
SSH_FATAL,
"Public key from server (%s) doesn't match user "
diff --git a/src/pki.c b/src/pki.c
index a72e8f49..183e8b1a 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -300,35 +300,45 @@ int ssh_key_algorithm_allowed(ssh_session session, const char *type)
* @brief Checks the given host key against the configured allowed
* host key algorithm types
*
- * @param[in] session The SSH session
- * @param[in] type The host key algorithm to check
- * @returns 1 if the host key algorithm is allowed, 0 otherwise
+ * @param[in] session The SSH session
+ * @param[in] type The host key algorithm to check
+ * @param[in] sig_key_type The key algorithm in the signature
+ * @param[in] sig_hash_type The hash algorithm in the signature
+ * @returns 1 if the host key algorithm is allowed, 0 otherwise
*/
-int ssh_hostkey_algorithm_allowed(ssh_session session, const char *type)
+int ssh_hostkey_algorithm_allowed(ssh_session session,
+ const char *type,
+ enum ssh_keytypes_e sig_key_type,
+ enum ssh_digest_e sig_hash_type)
{
const char *allowed_types;
allowed_types = session->opts.wanted_methods[SSH_HOSTKEYS];
if (allowed_types == NULL) {
- return 0; /* should never get here */
+ return 0; /* should never get here */
}
- if (ssh_match_group(allowed_types, type)) {
- return 1;
- }
if (strcmp(type, "ssh-rsa") == 0) {
- /*
- * If rsa-sha2 hostkey algo is used, the server returns ssh-rsa as
- * host key type. Hence, we need to check if one of the rsa-sha2
- * variants is in the allowed hostkey types list.
- */
- if (ssh_match_group(allowed_types, "rsa-sha2-256")) {
- return 1;
+ /*
+ * If rsa-sha2 hostkey algo is used, the server returns "ssh-rsa" as
+ * host key type, but an "rsa-sha2-256" or "rsa-sha2-512" signature type.
+ */
+ if (sig_key_type != SSH_KEYTYPE_RSA) {
+ return 0;
}
- if (ssh_match_group(allowed_types, "rsa-sha2-512")) {
- return 1;
+ if (sig_hash_type == SSH_DIGEST_SHA256) {
+ /* server sent us an "rsa-sha2-256" hostkey, is it allowed? */
+ return ssh_match_group(allowed_types, "rsa-sha2-256");
+ } else if (sig_hash_type == SSH_DIGEST_SHA512) {
+ /* server sent us an "rsa-sha2-256" hostkey, is it allowed? */
+ return ssh_match_group(allowed_types, "rsa-sha2-512");
+ } else if (sig_hash_type == SSH_DIGEST_SHA1 || sig_hash_type == SSH_DIGEST_AUTO) {
+ return ssh_match_group(allowed_types, "ssh-rsa");
}
}
+ if (ssh_match_group(allowed_types, type)) {
+ return 1;
+ }
return 0;
}
--
2.18.0
| Re: Bugs when using rsa-sha2 (+patches) | Jakub Jelen <jjelen@xxxxxxxxxx> |
| Bugs when using rsa-sha2 (+patches) | Tilo Eckert <tilo.eckert@xxxxxxx> |
| Re: Bugs when using rsa-sha2 (+patches) | Jakub Jelen <jjelen@xxxxxxxxxx> |
| Re: Bugs when using rsa-sha2 (+patches) | Tilo Eckert <tilo.eckert@xxxxxxx> |
| Re: Bugs when using rsa-sha2 (+patches) | Jakub Jelen <jjelen@xxxxxxxxxx> |