Vulnerability: In nDPI through 3.2 Stable, the SSH protocol dissector has multiple KEXINIT integer overflows that result in a controlled remote heap overflow in concat_hash_string in ssh.c. Due to the granular nature of the overflow primitive and the ability to control both the contents and layout of the nDPI library's heap memory through remote input, this vulnerability may be abused to achieve full Remote Code Execution against any network inspection stack that is linked against nDPI and uses it to perform network traffic analysis.
Vulnerability ID: CVE-2020-11939
CWE: CWE-787
Severity score: 6.4
Project: https://github.com/ntop/nDPI
Published: 2020-04-23
File: src/lib/protocols/ssh.c
Commit message: ssh: fixing unsigned overflow leading to heap overflow

cf GHSL-2020-051
Commit author: Philippe Antoine

Vulnerable version (before fix):
 static void ndpi_int_ssh_add_connection(struct ndpi_detection_module_struct  static u_int16_t concat_hash_string(struct ndpi_packet_struct *packet, 				   char *buf, u_int8_t client_hash) {   u_int16_t offset = 22, buf_out_len = 0;   if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   u_int32_t len = ntohl(*(u_int32_t*)&packet->payload[offset]); static u_int16_t concat_hash_string(struct ndpi_packet_struct *packet,     goto invalid_payload;   /* ssh.server_host_key_algorithms [None] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);   offset += 4 + len;    if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   /* ssh.encryption_algorithms_client_to_server [C] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    if(client_hash) {     offset += 4;      if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;     buf[buf_out_len++] = ';';     offset += len;   } else     offset += 4 + len;    if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   /* ssh.encryption_algorithms_server_to_client [S] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    if(!client_hash) {     offset += 4;      if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;     buf[buf_out_len++] = ';';     offset += len;   } else     offset += 4 + len;    if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   /* ssh.mac_algorithms_client_to_server [C] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    if(client_hash) {     offset += 4;      if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;     buf[buf_out_len++] = ';';     offset += len;   } else     offset += 4 + len;    if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   /* ssh.mac_algorithms_server_to_client [S] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    if(!client_hash) {     offset += 4;      if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;     buf[buf_out_len++] = ';';     offset += len;   } else     offset += 4 + len;    /* ssh.compression_algorithms_client_to_server [C] */   if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    if(client_hash) {     offset += 4;      if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;     offset += len;   } else     offset += 4 + len;    if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   /* ssh.compression_algorithms_server_to_client [S] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    if(!client_hash) {     offset += 4;      if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;     offset += len;   } else     offset += 4 + len;    /* ssh.languages_client_to_server [None] */ 

Fixed version (after fix):
 static void ndpi_int_ssh_add_connection(struct ndpi_detection_module_struct  static u_int16_t concat_hash_string(struct ndpi_packet_struct *packet, 				   char *buf, u_int8_t client_hash) {   u_int32_t offset = 22, buf_out_len = 0;   if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   u_int32_t len = ntohl(*(u_int32_t*)&packet->payload[offset]); static u_int16_t concat_hash_string(struct ndpi_packet_struct *packet,     goto invalid_payload;   /* ssh.server_host_key_algorithms [None] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);   if (len > UINT32_MAX - 4 - offset)     goto invalid_payload;   offset += 4 + len;    if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   /* ssh.encryption_algorithms_client_to_server [C] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    offset += 4;   if(client_hash) {     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;     buf[buf_out_len++] = ';';   }   if (len > UINT32_MAX - offset)     goto invalid_payload;   offset += len;    if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   /* ssh.encryption_algorithms_server_to_client [S] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    offset += 4;   if(!client_hash) {     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;     buf[buf_out_len++] = ';';   }   if (len > UINT32_MAX - offset)     goto invalid_payload;   offset += len;    if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   /* ssh.mac_algorithms_client_to_server [C] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    offset += 4;   if(client_hash) {     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;     buf[buf_out_len++] = ';';   }   if (len > UINT32_MAX - offset)     goto invalid_payload;   offset += len;    if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   /* ssh.mac_algorithms_server_to_client [S] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    offset += 4;   if(!client_hash) {     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;     buf[buf_out_len++] = ';';   }   if (len > UINT32_MAX - offset)     goto invalid_payload;   offset += len;    /* ssh.compression_algorithms_client_to_server [C] */   if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    offset += 4;   if(client_hash) {     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;   }   if (len > UINT32_MAX - offset)     goto invalid_payload;   offset += len;    if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)     goto invalid_payload;   /* ssh.compression_algorithms_server_to_client [S] */   len = ntohl(*(u_int32_t*)&packet->payload[offset]);    offset += 4;   if(!client_hash) {     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))       goto invalid_payload;      strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);     buf_out_len += len;   }   if (len > UINT32_MAX - offset)     goto invalid_payload;   offset += len;    /* ssh.languages_client_to_server [None] */ 

Patch:
patch: @@ -95,7 +95,7 @@ static void ndpi_int_ssh_add_connection(struct ndpi_detection_module_struct
 
 static u_int16_t concat_hash_string(struct ndpi_packet_struct *packet,
 				   char *buf, u_int8_t client_hash) {
-  u_int16_t offset = 22, buf_out_len = 0;
+  u_int32_t offset = 22, buf_out_len = 0;
   if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)
     goto invalid_payload;
   u_int32_t len = ntohl(*(u_int32_t*)&packet->payload[offset]);
@@ -114,113 +114,115 @@ static u_int16_t concat_hash_string(struct ndpi_packet_struct *packet,
     goto invalid_payload;
   /* ssh.server_host_key_algorithms [None] */
   len = ntohl(*(u_int32_t*)&packet->payload[offset]);
+  if (len > UINT32_MAX - 4 - offset)
+    goto invalid_payload;
   offset += 4 + len;
 
   if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)
     goto invalid_payload;
   /* ssh.encryption_algorithms_client_to_server [C] */
   len = ntohl(*(u_int32_t*)&packet->payload[offset]);
 
+  offset += 4;
   if(client_hash) {
-    offset += 4;
-
     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))
       goto invalid_payload;
 
     strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);
     buf_out_len += len;
     buf[buf_out_len++] = ';';
-    offset += len;
-  } else
-    offset += 4 + len;
+  }
+  if (len > UINT32_MAX - offset)
+    goto invalid_payload;
+  offset += len;
 
   if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)
     goto invalid_payload;
   /* ssh.encryption_algorithms_server_to_client [S] */
   len = ntohl(*(u_int32_t*)&packet->payload[offset]);
 
+  offset += 4;
   if(!client_hash) {
-    offset += 4;
-
     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))
       goto invalid_payload;
 
     strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);
     buf_out_len += len;
     buf[buf_out_len++] = ';';
-    offset += len;
-  } else
-    offset += 4 + len;
+  }
+  if (len > UINT32_MAX - offset)
+    goto invalid_payload;
+  offset += len;
 
   if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)
     goto invalid_payload;
   /* ssh.mac_algorithms_client_to_server [C] */
   len = ntohl(*(u_int32_t*)&packet->payload[offset]);
 
+  offset += 4;
   if(client_hash) {
-    offset += 4;
-
     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))
       goto invalid_payload;
 
     strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);
     buf_out_len += len;
     buf[buf_out_len++] = ';';
-    offset += len;
-  } else
-    offset += 4 + len;
+  }
+  if (len > UINT32_MAX - offset)
+    goto invalid_payload;
+  offset += len;
 
   if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)
     goto invalid_payload;
   /* ssh.mac_algorithms_server_to_client [S] */
   len = ntohl(*(u_int32_t*)&packet->payload[offset]);
 
+  offset += 4;
   if(!client_hash) {
-    offset += 4;
-
     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))
       goto invalid_payload;
 
     strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);
     buf_out_len += len;
     buf[buf_out_len++] = ';';
-    offset += len;
-  } else
-    offset += 4 + len;
+  }
+  if (len > UINT32_MAX - offset)
+    goto invalid_payload;
+  offset += len;
 
   /* ssh.compression_algorithms_client_to_server [C] */
   if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)
     goto invalid_payload;
   len = ntohl(*(u_int32_t*)&packet->payload[offset]);
 
+  offset += 4;
   if(client_hash) {
-    offset += 4;
-
     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))
       goto invalid_payload;
 
     strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);
     buf_out_len += len;
-    offset += len;
-  } else
-    offset += 4 + len;
+  }
+  if (len > UINT32_MAX - offset)
+    goto invalid_payload;
+  offset += len;
 
   if(offset+sizeof(u_int32_t) >= packet->payload_packet_len)
     goto invalid_payload;
   /* ssh.compression_algorithms_server_to_client [S] */
   len = ntohl(*(u_int32_t*)&packet->payload[offset]);
 
+  offset += 4;
   if(!client_hash) {
-    offset += 4;
-
     if((offset >= packet->payload_packet_len) || (len >= packet->payload_packet_len-offset-1))
       goto invalid_payload;
 
     strncpy(&buf[buf_out_len], (const char *)&packet->payload[offset], len);
     buf_out_len += len;
-    offset += len;
-  } else
-    offset += 4 + len;
+  }
+  if (len > UINT32_MAX - offset)
+    goto invalid_payload;
+  offset += len;
 
   /* ssh.languages_client_to_server [None] */
path: src/lib/protocols/ssh.c