CVE-2019-17498 (published 2019-10-21T22:15Z)

In libssh2 v1.9.0 and earlier versions, the SSH_MSG_DISCONNECT logic in packet.c has an integer overflow in a bounds check, enabling an attacker to specify an arbitrary (out-of-bounds) offset for a subsequent memory read. A crafted SSH server may be able to disclose sensitive information or cause a denial of service condition on the client system when a user connects to the server.

Severity: MEDIUM
CVSS v2 base score: 5.8 (AV:N/AC:M/Au:N/C:P/I:N/A:P)
CVSS v3 base score: 8.1, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:H)

Weakness information:
cwe_id: CWE-190
cwe_name: Integer Overflow or Wraparound
description: The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
extended_description: 
            
            
            
            
         
         

Fix information:
repo_url: https://github.com/libssh2/libssh2
commit_details: repo_url: https://github.com/libssh2/libssh2
author: Will Cosgrove
author_date: 2019-08-30 09:57:38-07:00
msg: packet.c: improve message parsing (#402)

* packet.c: improve parsing of packets

file: packet.c

notes:
Use _libssh2_get_string API in SSH_MSG_DEBUG/SSH_MSG_DISCONNECT. Additional uint32 bounds check in SSH_MSG_GLOBAL_REQUEST.
repository: repo_url: https://github.com/libssh2/libssh2
repo_name: libssh2/libssh2
description: the SSH library
repo_language: C
owner: libssh2
file_changes: filename: packet.c
change_type: ModificationType.MODIFY
diff: @@ -419,8 +419,8 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
                     size_t datalen, int macstate)
 {
     int rc = 0;
-    char *message = NULL;
-    char *language = NULL;
+    unsigned char *message = NULL;
+    unsigned char *language = NULL;
     size_t message_len = 0;
     size_t language_len = 0;
     LIBSSH2_CHANNEL *channelp = NULL;
@@ -472,33 +472,23 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
 
         case SSH_MSG_DISCONNECT:
             if(datalen >= 5) {
-                size_t reason = _libssh2_ntohu32(data + 1);
+                uint32_t reason = 0;
+                struct string_buf buf;
+                buf.data = (unsigned char *)data;
+                buf.dataptr = buf.data;
+                buf.len = datalen;
+                buf.dataptr++; /* advance past type */
 
-                if(datalen >= 9) {
-                    message_len = _libssh2_ntohu32(data + 5);
+                _libssh2_get_u32(&buf, &reason);
+                _libssh2_get_string(&buf, &message, &message_len);
+                _libssh2_get_string(&buf, &language, &language_len);
 
-                    if(message_len < datalen-13) {
-                        /* 9 = packet_type(1) + reason(4) + message_len(4) */
-                        message = (char *) data + 9;
-
-                        language_len =
-                            _libssh2_ntohu32(data + 9 + message_len);
-                        language = (char *) data + 9 + message_len + 4;
-
-                        if(language_len > (datalen-13-message_len)) {
-                            /* bad input, clear info */
-                            language = message = NULL;
-                            language_len = message_len = 0;
-                        }
-                    }
-                    else
-                        /* bad size, clear it */
-                        message_len = 0;
-                }
                 if(session->ssh_msg_disconnect) {
-                    LIBSSH2_DISCONNECT(session, reason, message,
-                                       message_len, language, language_len);
+                    LIBSSH2_DISCONNECT(session, reason, (const char *)message,
+                                       message_len, (const char *)language,
+                                       language_len);
                 }
+
                 _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
                                "Disconnect(%d): %s(%s)", reason,
                                message, language);
@@ -539,24 +529,24 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
                 int always_display = data[1];
 
                 if(datalen >= 6) {
-                    message_len = _libssh2_ntohu32(data + 2);
-
-                    if(message_len <= (datalen - 10)) {
-                        /* 6 = packet_type(1) + display(1) + message_len(4) */
-                        message = (char *) data + 6;
-                        language_len = _libssh2_ntohu32(data + 6 +
-                                                        message_len);
-
-                        if(language_len <= (datalen - 10 - message_len))
-                            language = (char *) data + 10 + message_len;
-                    }
+                    struct string_buf buf;
+                    buf.data = (unsigned char *)data;
+                    buf.dataptr = buf.data;
+                    buf.len = datalen;
+                    buf.dataptr += 2; /* advance past type & always display */
+
+                    _libssh2_get_string(&buf, &message, &message_len);
+                    _libssh2_get_string(&buf, &language, &language_len);
                 }
 
                 if(session->ssh_msg_debug) {
-                    LIBSSH2_DEBUG(session, always_display, message,
-                                  message_len, language, language_len);
+                    LIBSSH2_DEBUG(session, always_display,
+                                  (const char *)message,
+                                  message_len, (const char *)language,
+                                  language_len);
                 }
             }
+
             /*
              * _libssh2_debug will actually truncate this for us so
              * that it's not an inordinate about of data
@@ -579,7 +569,7 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
                 uint32_t len = 0;
                 unsigned char want_reply = 0;
                 len = _libssh2_ntohu32(data + 1);
-                if(datalen >= (6 + len)) {
+                if((len <= (UINT_MAX - 6)) && (datalen >= (6 + len))) {
                     want_reply = data[5 + len];
                     _libssh2_debug(session,
                                    LIBSSH2_TRACE_CONN,

programming_language: C