VYPR
Medium severity5.6NVD Advisory· Published May 24, 2026

CVE-2026-9365

CVE-2026-9365

Description

A vulnerability has been found in Ettercap up to 0.8.3. The affected element is the function FUNC_DECODER of the file src/dissectors/ec_gg.c of the component GG Dissector. The manipulation of the argument gg leads to heap-based buffer overflow. The attack is possible to be carried out remotely. The complexity of an attack is rather high. The exploitability is described as difficult. The exploit has been disclosed to the public and may be used. Upgrading to version 0.8.4 is sufficient to fix this issue. The identifier of the patch is feeae6fa366e01a3dd9f1857ec6aae847b2ae00c. It is suggested to upgrade the affected component.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Ettercap GG dissector up to 0.8.3 has a heap buffer overflow via crafted network packets, risking denial of service or code execution.

Vulnerability

A heap-based buffer overflow exists in the GG (Gadu-Gadu) dissector of Ettercap versions up to 0.8.3. The vulnerability is located in the function FUNC_DECODER within the file src/dissectors/ec_gg.c. The dissector fails to properly validate the length of attacker-controlled data before copying it into a fixed-size heap buffer (tbuf2, 71 bytes). Versions up to and including 0.8.3 are affected. The issue was fixed in version 0.8.4 [1][2][3].

Exploitation

An attacker must be on the same network segment or in a Man-in-the-Middle (MITM) position to send crafted network packets to TCP port 8074. No authentication is required. The attack complexity is considered high. The attacker sends a specially crafted GG packet that provides a gg->len value exceeding the allocated buffer size, triggering the overflow [3].

Impact

Successful exploitation leads to heap memory corruption, potentially causing denial of service (DoS) or enabling further exploitation depending on heap layout. The attacker may achieve arbitrary code execution, though this is considered difficult due to heap layout constraints [3].

Mitigation

Upgrade to Ettercap version 0.8.4, which includes the patch with commit feeae6fa366e01a3dd9f1857ec6aae847b2ae00c [2][4]. No workaround is available. The vulnerability is not listed on the CISA Known Exploited Vulnerabilities (KEV) catalog.

AI Insight generated on May 26, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

2
feeae6fa366e

fix Heap buffer overflow in GG Dissector, thanks to <dapickle> for the bug report and fix Closes: #1306

https://github.com/Ettercap/ettercapGianfranco CostamagnaApr 24, 2026via nvd-ref
1 file changed · +40 24
  • src/dissectors/ec_gg.c+40 24 modified
    @@ -359,10 +359,12 @@ FUNC_DECODER(dissector_gg)
     if ((gg->type == GG_LOGIN50_CMD) && !FROM_SERVER("gg", PACKET)) {
        gg_get_status(gg_login50->status,tbuf);
        gg_get_version(gg_login50->version,tbuf3);
    -   if ((int)gg->len-22 < 0)
    +   size_t offset=22;
    +   if (gg->len-offset < 0)
            return NULL;
    -   strncpy(tbuf2,gg_login50->description, (gg->len)-22);
    -   tbuf2[(gg->len)-22]='\0';
    +   size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +   strncpy(tbuf2,gg_login50->description, copy_len);
    +   tbuf2[copy_len]='\0';
        sprintf(user,"%u",gg_login50->uin);
        PACKET->DISSECTOR.user = strdup(user);
        sprintf(pass,"%u",gg_login50->hash);
    @@ -381,10 +383,12 @@ if ((gg->type == GG_LOGIN50_CMD) && !FROM_SERVER("gg", PACKET)) {
     else if (gg->type == GG_LOGIN60_CMD) {
        gg_get_status(gg_login60->status,tbuf);
        gg_get_version(gg_login60->version,tbuf3);
    -   if ((int)gg->len-31 < 0)
    +   size_t offset=31;
    +   if (gg->len-offset < 0)
            return NULL;
    -   strncpy(tbuf2,gg_login60->description, (gg->len)-31);
    -   tbuf2[(gg->len)-31]='\0';
    +   size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +   strncpy(tbuf2,gg_login60->description, copy_len);
    +   tbuf2[copy_len]='\0';
        sprintf(user,"%u",gg_login60->uin);
        PACKET->DISSECTOR.user = strdup(user);
        sprintf(pass,"%u",gg_login60->hash);
    @@ -405,10 +409,12 @@ else if (gg->type == GG_LOGIN60_CMD) {
     else if (gg->type == GG_LOGIN70_CMD) {
        gg_get_status(gg_login70->status,tbuf);
        gg_get_version(gg_login70->version,tbuf3);
    -   if ((int)gg->len-92 < 0)
    +   size_t offset=92;
    +   if (gg->len-offset < 0)
            return NULL;
    -   strncpy(tbuf2,gg_login70->description, (gg->len)-92);
    -   tbuf2[(gg->len)-92]='\0';
    +   size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +   strncpy(tbuf2,gg_login70->description, copy_len);
    +   tbuf2[copy_len]='\0';
        sprintf(user,"%u",gg_login70->uin);
        PACKET->DISSECTOR.user = strdup(user);
        sprintf(pass,"%X%X%X%X%X",gg_login70->hash[0],gg_login70->hash[1],gg_login70->hash[2],gg_login70->hash[3],gg_login70->hash[4]);
    @@ -454,10 +460,12 @@ else if (gg->type == GG_WELCOME_CMD) {
     #ifdef GG_CONTACTS_STATUS_CHANGES
     else if ((gg->type == GG_STATUS_CMD) && FROM_SERVER("gg", PACKET)) {
         gg_get_status(gg_status->status,tbuf);
    -    if ((int)gg->len-8 < 0)
    +    size_t offset=8;
    +    if (gg->len-offset < 0)
             return NULL;
    -    strncpy(tbuf2,gg_status->description, (gg->len)-8);
    -    tbuf2[(gg->len)-8]='\0';
    +    size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +    strncpy(tbuf2,gg_status->description, copy_len);
    +    tbuf2[copy_len]='\0';
         DISSECT_MSG("GG : %s:%d -> %s:%d - STATUS CHANGED  UIN: %u  STATUS: %s (%s)\n", ip_addr_ntoa(&PACKET->L3.src, tmp),
                                      ntohs(PACKET->L4.src),
                                      ip_addr_ntoa(&PACKET->L3.dst, tmp2),
    @@ -468,10 +476,12 @@ else if ((gg->type == GG_STATUS_CMD) && FROM_SERVER("gg", PACKET)) {
     #endif
     else if ((gg->type == GG_NEW_STATUS_CMD) && !FROM_SERVER("gg", PACKET)) {
           gg_get_status(gg_new_status->status,tbuf);
    -      if ((int)gg->len-4 < 0)
    +      size_t offset=4;
    +      if (gg->len-offset < 0)
               return NULL;
    -      strncpy(tbuf2,gg_new_status->description, (gg->len)-4);
    -      tbuf2[(gg->len)-4]='\0';
    +      size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +      strncpy(tbuf2,gg_new_status->description, copy_len);
    +      tbuf2[copy_len]='\0';
           DISSECT_MSG("GG : %s:%d -> %s:%d - NEW STATUS  STATUS: %s (%s)\n", ip_addr_ntoa(&PACKET->L3.src, tmp),
                                      ntohs(PACKET->L4.src),
                                      ip_addr_ntoa(&PACKET->L3.dst, tmp2),
    @@ -482,10 +492,12 @@ else if ((gg->type == GG_NEW_STATUS_CMD) && !FROM_SERVER("gg", PACKET)) {
     else if ((gg->type == GG_STATUS50_CMD) && FROM_SERVER("gg", PACKET)) {
           gg_get_status(gg_status50->status,tbuf);
           gg_get_version(gg_status50->version,tbuf3);
    -      if ((int)gg->len-20 < 0)
    +      size_t offset=20;
    +      if (gg->len-offset < 0)
               return NULL;
    -      strncpy(tbuf2,gg_status50->description, (gg->len)-20);
    -      tbuf2[(gg->len)-20]='\0';
    +      size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +      strncpy(tbuf2,gg_status50->description, copy_len);
    +      tbuf2[copy_len]='\0';
           DISSECT_MSG("GG4/5 : %s:%d -> %s:%d - STATUS CHANGED  UIN: %u  STATUS: %s (%s)  VERSION: %s  RIP: %u.%u.%u.%u:%u\n", ip_addr_ntoa(&PACKET->L3.src, tmp),
                                      ntohs(PACKET->L4.src),
                                      ip_addr_ntoa(&PACKET->L3.dst, tmp2),
    @@ -499,10 +511,12 @@ else if ((gg->type == GG_STATUS50_CMD) && FROM_SERVER("gg", PACKET)) {
     else if (gg->type == GG_STATUS60_CMD) {
           gg_get_status(gg_status60->status,tbuf);
           gg_get_version(gg_status60->version,tbuf3);
    -      if ((int)gg->len-14 < 0)
    +      size_t offset=14;
    +      if (gg->len-offset < 0)
               return NULL;
    -      strncpy(tbuf2,gg_status60->description, (gg->len)-14);
    -      tbuf2[(gg->len)-14]='\0';
    +      size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +      strncpy(tbuf2,gg_status60->description, copy_len);
    +      tbuf2[copy_len]='\0';
           DISSECT_MSG("GG6 : %s:%d -> %s:%d - STATUS CHANGED  UIN: %u  STATUS: %s (%s)  VERSION: %s  RIP: %u.%u.%u.%u:%u\n", ip_addr_ntoa(&PACKET->L3.src, tmp),
                                      ntohs(PACKET->L4.src),
                                      ip_addr_ntoa(&PACKET->L3.dst, tmp2),
    @@ -515,11 +529,13 @@ else if (gg->type == GG_STATUS60_CMD) {
     }
     else if (gg->type == GG_STATUS70_CMD) {
           gg_get_status(gg_status70->status,tbuf);
    -      if ((int)gg->len-18 < 0)
    +      size_t offset=18;
    +      if (gg->len-offset < 0)
               return NULL;
    +      size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
           gg_get_version(gg_status70->version,tbuf3);
    -      strncpy(tbuf2,gg_status70->description, (gg->len)-18);
    -      tbuf2[(gg->len)-18]='\0';
    +      strncpy(tbuf2,gg_status70->description, copy_len);
    +      tbuf2[copy_len]='\0';
           DISSECT_MSG("GG7 : %s:%d -> %s:%d - STATUS CHANGED  UIN: %u  STATUS: %s (%s)  VERSION: %s  RIP: %u.%u.%u.%u:%u\n", ip_addr_ntoa(&PACKET->L3.src, tmp),
                                      ntohs(PACKET->L4.src),
                                      ip_addr_ntoa(&PACKET->L3.dst, tmp2),
    
e4be6c8530a1

Merge pull request #1307 from LocutusOfBorg/1306-fix

https://github.com/Ettercap/ettercapGianfranco CostamagnaApr 27, 2026via nvd-ref
16 files changed · +90 69
  • plug-ins/search_promisc/search_promisc.c+1 1 modified
    @@ -118,7 +118,7 @@ static EC_THREAD_FUNC(search_promisc_thread)
        
        char tmp[MAX_ASCII_ADDR_LEN];
        struct hosts_list *h;
    -   u_char bogus_mac[2][6]={"\xfd\xfd\x00\x00\x00\x00", "\xff\xff\x00\x00\x00\x00"};
    +   uint8_t bogus_mac[2][6] = {{0xfd, 0xfd, 0x00, 0x00, 0x00, 0x00},{0xff, 0xff, 0x00, 0x00, 0x00, 0x00}};
        char messages[2][48]={"\nLess probably sniffing NICs:\n", "\nMost probably sniffing NICs:\n"};
        u_char i;
      
    
  • plug-ins/sslstrip/sslstrip.c+8 6 modified
    @@ -776,7 +776,7 @@ static void http_handle_request(struct http_connection *connection, struct packe
        char *r = (char*)po->DATA.data;
     
        //Skip the first line of request
    -   if ((r = strstr((const char*)po->DATA.data, "\r\n")) == NULL)
    +   if ((r = strstr((char*)po->DATA.data, "\r\n")) == NULL)
           return; // This doesn't seem to look as a HTTP header
     
        r += 2; //Skip \r\n
    @@ -887,11 +887,13 @@ static void http_send(struct http_connection *connection, struct packet_object *
        curl_easy_setopt(connection->handle, CURLOPT_COOKIEFILE, ""); //Initialize cookie engine
     
        /* Only allow HTTP and HTTPS */
    -   curl_easy_setopt(connection->handle, CURLOPT_PROTOCOLS, (long) CURLPROTO_HTTP | 
    -                  (long)CURLPROTO_HTTPS);
    -   curl_easy_setopt(connection->handle, CURLOPT_REDIR_PROTOCOLS, (long) CURLPROTO_HTTP | 
    -                  (long) CURLPROTO_HTTPS);
    -
    +#if LIBCURL_VERSION_NUM >= 0x075500
    +   curl_easy_setopt(connection->handle, CURLOPT_PROTOCOLS_STR, "http,https");
    +   curl_easy_setopt(connection->handle, CURLOPT_REDIR_PROTOCOLS_STR, "http,https");
    +#else
    +   curl_easy_setopt(connection->handle, CURLOPT_PROTOCOLS, (long) CURLPROTO_HTTP | (long)CURLPROTO_HTTPS);
    +   curl_easy_setopt(connection->handle, CURLOPT_REDIR_PROTOCOLS, (long) CURLPROTO_HTTP | (long) CURLPROTO_HTTPS);
    +#endif
     
        if(connection->request->method == HTTP_POST) {
           curl_easy_setopt(connection->handle, CURLOPT_POST, 1L);
    
  • src/dissectors/ec_gg.c+56 39 modified
    @@ -149,22 +149,23 @@ uncomment #define below if you want to see all contacts status changes - be care
     
     /* globals */
     
    -#define GG_LOGIN50_CMD		0x0000000c
    -#define GG_LOGIN60_CMD		0x00000015
    -#define GG_LOGIN70_CMD		0x00000019
    +#define GG_LOGIN50_CMD	   0x0000000c
    +#define GG_LOGIN60_CMD	   0x00000015
    +#define GG_LOGIN70_CMD	   0x00000019
     
    -#define GG_WELCOME_CMD		0x00000001
    +#define GG_WELCOME_CMD	   0x00000001
     
    -#define GG_SEND_MSG_CMD		0x0000000b
    -#define GG_RECV_MSG_CMD		0x0000000a
    +#define GG_SEND_MSG_CMD	   0x0000000b
    +#define GG_RECV_MSG_CMD	   0x0000000a
     
     #define GG_NEW_STATUS_CMD  0x00000002
     
     #define GG_STATUS_CMD      0x00000002
     
    -#define GG_STATUS50_CMD		0x0000000c
    +#define GG_STATUS50_CMD	   0x0000000c
     #define GG_STATUS60_CMD    0x0000000f
     #define GG_STATUS70_CMD    0x00000017
    +#define GG_MAX_LEN         70
     
     struct gg_hdr {
        u_int32 type;
    @@ -182,7 +183,7 @@ struct gg_login50_hdr {
        u_int32 version;   
        u_int8 local_ip[4];  
        u_int16 local_port;
    -   char description[71];     /* 70+1 (0x0) */
    +   char description[GG_MAX_LEN + 1];     /* 70+1 (0x0) */
        u_int32 time;
     }__attribute__ ((packed));
     
    @@ -198,7 +199,7 @@ struct gg_login60_hdr {
        u_int16 remote_port;
        u_int8 image_size;
        u_int8 unknown2;          /* 0xbe */
    -   char description[71];     /* 70+1 (0x0) */
    +   char description[GG_MAX_LEN + 1];     /* 70+1 (0x0) */
        u_int32 time;
     }__attribute__ ((packed));
     
    @@ -217,7 +218,7 @@ struct gg_login70_hdr {
        u_int16 remote_port;
        u_int8 image_size;
        u_int8 unknown5;          /* 0xbe */
    -   char description[71];     /* 70+1 (0x0) */
    +   char description[GG_MAX_LEN + 1];     /* 70+1 (0x0) */
        u_int32 time;
     }__attribute__ ((packed));
     
    @@ -238,14 +239,14 @@ struct gg_recv_msg_hdr {
     
     struct gg_new_status_hdr {
        u_int32 status;
    -   char description[71];     /* 70+1 (0x0) */
    +   char description[GG_MAX_LEN + 1];     /* 70+1 (0x0) */
        u_int32 time;
     };
     
     struct gg_status_hdr {
        u_int32 uin;
        u_int32 status;
    -   char description[71];     /* 70+1 (0x0) */
    +   char description[GG_MAX_LEN + 1];     /* 70+1 (0x0) */
        u_int32 time;
     };
     
    @@ -256,7 +257,7 @@ struct gg_status50_hdr {
        u_int16 remote_port;
        u_int32 version;   
        u_int16 unknown1;         /* remote port again? */
    -   char description[71];     /* 70+1 (0x0) */
    +   char description[GG_MAX_LEN + 1];     /* 70+1 (0x0) */
        u_int32 time;
     }__attribute__ ((packed));
     
    @@ -268,7 +269,7 @@ struct gg_status60_hdr {
        u_int8 version;   
        u_int8 image_size;
        u_int8 unknown1;          /* 0x00 */
    -   char description[71];     /* 70+1 (0x0) */
    +   char description[GG_MAX_LEN + 1];     /* 70+1 (0x0) */
        u_int32 time;
     }__attribute__ ((packed));;
     
    @@ -281,7 +282,7 @@ struct gg_status70_hdr {
        u_int8 image_size;
        u_int8 unknown1;          /* 0x00 */
        u_int32 unknown2;         /* 0x00 */
    -   char description[71];     /* 70+1 (0x0) */
    +   char description[GG_MAX_LEN + 1];     /* 70+1 (0x0) */
        u_int32 time;
     }__attribute__ ((packed));;
     
    @@ -358,10 +359,12 @@ FUNC_DECODER(dissector_gg)
     if ((gg->type == GG_LOGIN50_CMD) && !FROM_SERVER("gg", PACKET)) {
        gg_get_status(gg_login50->status,tbuf);
        gg_get_version(gg_login50->version,tbuf3);
    -   if ((int)gg->len-22 < 0)
    +   size_t offset=22;
    +   if (gg->len < offset)
            return NULL;
    -   strncpy(tbuf2,gg_login50->description, (gg->len)-22);
    -   tbuf2[(gg->len)-22]='\0';
    +   size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +   strncpy(tbuf2,gg_login50->description, copy_len);
    +   tbuf2[copy_len]='\0';
        sprintf(user,"%u",gg_login50->uin);
        PACKET->DISSECTOR.user = strdup(user);
        sprintf(pass,"%u",gg_login50->hash);
    @@ -380,10 +383,12 @@ if ((gg->type == GG_LOGIN50_CMD) && !FROM_SERVER("gg", PACKET)) {
     else if (gg->type == GG_LOGIN60_CMD) {
        gg_get_status(gg_login60->status,tbuf);
        gg_get_version(gg_login60->version,tbuf3);
    -   if ((int)gg->len-31 < 0)
    +   size_t offset=31;
    +   if (gg->len < offset)
            return NULL;
    -   strncpy(tbuf2,gg_login60->description, (gg->len)-31);
    -   tbuf2[(gg->len)-31]='\0';
    +   size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +   strncpy(tbuf2,gg_login60->description, copy_len);
    +   tbuf2[copy_len]='\0';
        sprintf(user,"%u",gg_login60->uin);
        PACKET->DISSECTOR.user = strdup(user);
        sprintf(pass,"%u",gg_login60->hash);
    @@ -404,10 +409,12 @@ else if (gg->type == GG_LOGIN60_CMD) {
     else if (gg->type == GG_LOGIN70_CMD) {
        gg_get_status(gg_login70->status,tbuf);
        gg_get_version(gg_login70->version,tbuf3);
    -   if ((int)gg->len-92 < 0)
    +   size_t offset=92;
    +   if (gg->len < offset)
            return NULL;
    -   strncpy(tbuf2,gg_login70->description, (gg->len)-92);
    -   tbuf2[(gg->len)-92]='\0';
    +   size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +   strncpy(tbuf2,gg_login70->description, copy_len);
    +   tbuf2[copy_len]='\0';
        sprintf(user,"%u",gg_login70->uin);
        PACKET->DISSECTOR.user = strdup(user);
        sprintf(pass,"%X%X%X%X%X",gg_login70->hash[0],gg_login70->hash[1],gg_login70->hash[2],gg_login70->hash[3],gg_login70->hash[4]);
    @@ -453,10 +460,12 @@ else if (gg->type == GG_WELCOME_CMD) {
     #ifdef GG_CONTACTS_STATUS_CHANGES
     else if ((gg->type == GG_STATUS_CMD) && FROM_SERVER("gg", PACKET)) {
         gg_get_status(gg_status->status,tbuf);
    -    if ((int)gg->len-8 < 0)
    +    size_t offset=8;
    +    if (gg->len < offset)
             return NULL;
    -    strncpy(tbuf2,gg_status->description, (gg->len)-8);
    -    tbuf2[(gg->len)-8]='\0';
    +    size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +    strncpy(tbuf2,gg_status->description, copy_len);
    +    tbuf2[copy_len]='\0';
         DISSECT_MSG("GG : %s:%d -> %s:%d - STATUS CHANGED  UIN: %u  STATUS: %s (%s)\n", ip_addr_ntoa(&PACKET->L3.src, tmp),
                                      ntohs(PACKET->L4.src),
                                      ip_addr_ntoa(&PACKET->L3.dst, tmp2),
    @@ -467,10 +476,12 @@ else if ((gg->type == GG_STATUS_CMD) && FROM_SERVER("gg", PACKET)) {
     #endif
     else if ((gg->type == GG_NEW_STATUS_CMD) && !FROM_SERVER("gg", PACKET)) {
           gg_get_status(gg_new_status->status,tbuf);
    -      if ((int)gg->len-4 < 0)
    +      size_t offset=4;
    +      if (gg->len < offset)
               return NULL;
    -      strncpy(tbuf2,gg_new_status->description, (gg->len)-4);
    -      tbuf2[(gg->len)-4]='\0';
    +      size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +      strncpy(tbuf2,gg_new_status->description, copy_len);
    +      tbuf2[copy_len]='\0';
           DISSECT_MSG("GG : %s:%d -> %s:%d - NEW STATUS  STATUS: %s (%s)\n", ip_addr_ntoa(&PACKET->L3.src, tmp),
                                      ntohs(PACKET->L4.src),
                                      ip_addr_ntoa(&PACKET->L3.dst, tmp2),
    @@ -481,10 +492,12 @@ else if ((gg->type == GG_NEW_STATUS_CMD) && !FROM_SERVER("gg", PACKET)) {
     else if ((gg->type == GG_STATUS50_CMD) && FROM_SERVER("gg", PACKET)) {
           gg_get_status(gg_status50->status,tbuf);
           gg_get_version(gg_status50->version,tbuf3);
    -      if ((int)gg->len-20 < 0)
    +      size_t offset=20;
    +      if (gg->len < offset)
               return NULL;
    -      strncpy(tbuf2,gg_status50->description, (gg->len)-20);
    -      tbuf2[(gg->len)-20]='\0';
    +      size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +      strncpy(tbuf2,gg_status50->description, copy_len);
    +      tbuf2[copy_len]='\0';
           DISSECT_MSG("GG4/5 : %s:%d -> %s:%d - STATUS CHANGED  UIN: %u  STATUS: %s (%s)  VERSION: %s  RIP: %u.%u.%u.%u:%u\n", ip_addr_ntoa(&PACKET->L3.src, tmp),
                                      ntohs(PACKET->L4.src),
                                      ip_addr_ntoa(&PACKET->L3.dst, tmp2),
    @@ -498,10 +511,12 @@ else if ((gg->type == GG_STATUS50_CMD) && FROM_SERVER("gg", PACKET)) {
     else if (gg->type == GG_STATUS60_CMD) {
           gg_get_status(gg_status60->status,tbuf);
           gg_get_version(gg_status60->version,tbuf3);
    -      if ((int)gg->len-14 < 0)
    +      size_t offset=14;
    +      if (gg->len < offset)
               return NULL;
    -      strncpy(tbuf2,gg_status60->description, (gg->len)-14);
    -      tbuf2[(gg->len)-14]='\0';
    +      size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
    +      strncpy(tbuf2,gg_status60->description, copy_len);
    +      tbuf2[copy_len]='\0';
           DISSECT_MSG("GG6 : %s:%d -> %s:%d - STATUS CHANGED  UIN: %u  STATUS: %s (%s)  VERSION: %s  RIP: %u.%u.%u.%u:%u\n", ip_addr_ntoa(&PACKET->L3.src, tmp),
                                      ntohs(PACKET->L4.src),
                                      ip_addr_ntoa(&PACKET->L3.dst, tmp2),
    @@ -514,11 +529,13 @@ else if (gg->type == GG_STATUS60_CMD) {
     }
     else if (gg->type == GG_STATUS70_CMD) {
           gg_get_status(gg_status70->status,tbuf);
    -      if ((int)gg->len-18 < 0)
    +      size_t offset=18;
    +      if (gg->len < offset)
               return NULL;
    +      size_t copy_len = MIN(gg->len - offset, GG_MAX_LEN);
           gg_get_version(gg_status70->version,tbuf3);
    -      strncpy(tbuf2,gg_status70->description, (gg->len)-18);
    -      tbuf2[(gg->len)-18]='\0';
    +      strncpy(tbuf2,gg_status70->description, copy_len);
    +      tbuf2[copy_len]='\0';
           DISSECT_MSG("GG7 : %s:%d -> %s:%d - STATUS CHANGED  UIN: %u  STATUS: %s (%s)  VERSION: %s  RIP: %u.%u.%u.%u:%u\n", ip_addr_ntoa(&PACKET->L3.src, tmp),
                                      ntohs(PACKET->L4.src),
                                      ip_addr_ntoa(&PACKET->L3.dst, tmp2),
    
  • src/dissectors/ec_http.c+5 5 modified
    @@ -161,13 +161,13 @@ FUNC_DECODER(dissector_http)
           /* Check Proxy or WWW auth first
            * then password in the GET or POST.
            */
    -      if ((from_here = strstr((const char*)ptr, "Authorization: Passport")) && 
    +      if ((from_here = strstr((char*)ptr, "Authorization: Passport")) && 
              Parse_Passport_Auth(from_here + strlen("Authorization: Passport"), PACKET));       
    -      else if ((from_here = strstr((const char*)ptr, ": NTLM ")) && 
    +      else if ((from_here = strstr((char*)ptr, ": NTLM ")) && 
              Parse_NTLM_Auth((char*)ptr, from_here + strlen(": NTLM "), PACKET));
    -      else if ((from_here = strstr((const char*)ptr, ": Basic ")) &&
    +      else if ((from_here = strstr((char*)ptr, ": Basic ")) &&
              Parse_Basic_Auth((char*)ptr, from_here  + strlen(": Basic "), PACKET));
    -      else if ((from_here = strstr((const char*)ptr, "User-Agent: ")) &&
    +      else if ((from_here = strstr((char*)ptr, "User-Agent: ")) &&
               Parse_User_Agent(end, from_here + strlen("User-Agent: "), PACKET));
           else if (!strncmp((const char*)ptr, "GET ", 4))
              Parse_Method_Get((char*)ptr + strlen("GET "), PACKET);
    @@ -199,7 +199,7 @@ FUNC_DECODER(dissector_http)
               * packet as HTTP header? Otherwise put these lines
               * out from the if (decrease performances, checks all pcks)
               */
    -         if ((from_here = strstr((const char*)ptr, ": NTLM "))) 
    +         if ((from_here = strstr((char*)ptr, ": NTLM "))) 
                 Parse_NTLM_Auth((char*)ptr, from_here + strlen(": NTLM "), PACKET);
           }
        }
    
  • src/dissectors/ec_mysql.c+1 1 modified
    @@ -53,7 +53,7 @@ static void print_hex(unsigned char *str, int len)
     */
     #endif
     
    -static char itoa16[16] =  "0123456789abcdef";
    +static char itoa16[17] =  "0123456789abcdef";
     
     static inline void hex_encode(unsigned char *str, int len, unsigned char *out)
     {
    
  • src/dissectors/ec_ospf.c+2 2 modified
    @@ -167,14 +167,14 @@ FUNC_DECODER(dissector_ospf)
             }
     
             for (i=0; i<length; i++) {
    -           if (ptr+i == NULL)
    +           if (*(ptr+i) == '\0')
                   return NULL;
     
                DISSECT_MSG("%02x", *(ptr+i));
             }
             DISSECT_MSG("$");
             for (i=length; i<length+auth_data_len; i++) {
    -           if (ptr+i == NULL)
    +           if (*(ptr+i) == '\0')
                   return NULL;
     
                DISSECT_MSG("%02x", *(ptr+i));
    
  • src/dissectors/ec_postgresql.c+1 1 modified
    @@ -66,7 +66,7 @@ void postgresql_init(void);
             | ( (unsigned long) (b)[(i) + 3]       );       \
     }
     
    -static char itoa16[16] =  "0123456789abcdef";
    +static char itoa16[17] =  "0123456789abcdef";
     
     static inline void hex_encode(unsigned char *str, int len, unsigned char *out)
     {
    
  • src/dissectors/ec_rip.c+2 2 modified
    @@ -163,15 +163,15 @@ FUNC_DECODER(dissector_rip)
                     ntohs(PACKET->L4.dst));
     
                for (i = 0; i < rip_packet_len + RIP_HEADER_SIZE; i++) {
    -                if (ptr + i == NULL)
    +                if (*(ptr + i) == '\0')
                             return NULL;
     
                     DISSECT_MSG("%02x", *(ptr+i));
                }
                DISSECT_MSG("$");
                for (i = rip_packet_len + RIP_HEADER_SIZE; i < rip_packet_len + \
                             RIP_HEADER_SIZE + RIP_AUTH_MD5_SIZE; i++) {
    -                if (ptr + i == NULL)
    +                if (*(ptr + i) == '\0')
                             return NULL;
     
                     DISSECT_MSG("%02x", *(ptr+i));
    
  • src/dissectors/ec_ssh.c+1 1 modified
    @@ -177,7 +177,7 @@ FUNC_DECODER(dissector_ssh)
              /* Only if we are interested on key substitution */         
              if (EC_GBL_CONF->aggressive_dissectors && !EC_GBL_OPTIONS->unoffensive && !EC_GBL_OPTIONS->read) {
                 dissect_create_session(&s, PACKET, DISSECT_CODE(dissector_ssh));
    -            SAFE_CALLOC(s->data, sizeof(ssh_session_data), 1);
    +            SAFE_CALLOC(s->data, 1, sizeof(ssh_session_data));
                 session_put(s);
                 session_data =(ssh_session_data *)s->data;
                 session_data->status = WAITING_CLIENT_BANNER;
    
  • src/dissectors/ec_vnc.c+1 1 modified
    @@ -50,7 +50,7 @@ void vnc_init(void);
     
     /************************************************/
     
    -static char itoa16[16] =  "0123456789abcdef";
    +static char itoa16[17] =  "0123456789abcdef";
     
     static inline void hex_encode(unsigned char *str, int len, unsigned char *out)
     {
    
  • src/interfaces/daemon/ec_daemon.c+2 0 modified
    @@ -144,9 +144,11 @@ void daemon_interface(void)
        LIST_FOREACH_SAFE(plugin, &EC_GBL_OPTIONS->plugins, next, tmp) {
           /* check if the plugin exists */
           if (search_plugin(plugin->name) != E_SUCCESS)
    +      {
              plugin->exists = false;
              USER_MSG("Sorry, plugin '%s' can not be found - skipping!\n\n", 
                    plugin->name);
    +      }
        }
        
        /* build the list of active hosts */
    
  • src/interfaces/gtk3/ec_gtk3_plugins.c+2 2 modified
    @@ -28,7 +28,7 @@
     
     /* proto */
     
    -static void gtkui_load_plugin(const char *full);
    +static void gtkui_load_plugin(char *full);
     static void gtkui_add_plugin(char active, struct plugin_ops *ops);
     static void gtkui_plug_destroy(void);
     static void gtkui_plugins_detach(GtkWidget *child);
    @@ -104,7 +104,7 @@ void gtkui_plugin_load(GSimpleAction *action, GVariant *value, gpointer data)
        gtk_widget_destroy (dialog);
     }
     
    -static void gtkui_load_plugin(const char *full)
    +static void gtkui_load_plugin(char *full)
     {
        char *file;
        int ret;
    
  • src/interfaces/gtk/ec_gtk_plugins.c+2 2 modified
    @@ -28,7 +28,7 @@
     
     /* proto */
     
    -static void gtkui_load_plugin(const char *full);
    +static void gtkui_load_plugin(char *full);
     static void gtkui_add_plugin(char active, struct plugin_ops *ops);
     static void gtkui_plug_destroy(void);
     static void gtkui_plugins_detach(GtkWidget *child);
    @@ -89,7 +89,7 @@ void gtkui_plugin_load(void)
        gtk_widget_destroy (dialog);
     }
     
    -static void gtkui_load_plugin(const char *full)
    +static void gtkui_load_plugin(char *full)
     {
        char *file;
        int ret;
    
  • src/mitm/ec_port_stealing.c+1 1 modified
    @@ -114,7 +114,7 @@ static int port_stealing_start(char *args)
        struct eth_header *heth;
        struct arp_header *harp;
        char *p;
    -   char bogus_mac[6]="\x00\xe7\x7e\xe7\x7e\xe7";
    +   uint8_t bogus_mac[6]={0x00, 0xe7, 0x7e, 0xe7, 0x7e, 0xe7};
        
        DEBUG_MSG("port_stealing_start");
        USER_MSG("\nPort Stealing: starting...\n\n");
    
  • src/protocols/ec_cooked.c+1 1 modified
    @@ -53,7 +53,7 @@ FUNC_DECODER(decode_cook)
        FUNC_DECODER_PTR(next_decoder);
        u_int16 proto;
        u_int16 pck_type;
    -   char bogus_mac[6]="\x00\x01\x00\x01\x00\x01";
    +   uint8_t bogus_mac[6]={0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
     
        DECODED_LEN = COOKED_LEN;
        proto = pntos(DECODE_DATA + PROTO_OFFSET);
    
  • tests/test_ec_decode.c+4 4 modified
    @@ -12,25 +12,25 @@ struct ec_globals *ec_gbls;
     
     START_TEST (test_get_decoder_default)
     {
    -  fail_if(get_decoder(APP_LAYER, PL_DEFAULT) == NULL, "Could not find default decoder.");
    +  ck_assert_msg(get_decoder(APP_LAYER, PL_DEFAULT) != NULL, "Could not find default decoder.");
     }
     END_TEST
     
     START_TEST (test_get_decoder_ip)
     {
    -  fail_if(get_decoder(NET_LAYER, LL_TYPE_IP) == NULL, "Could not find IP decoder.");
    +  ck_assert_msg(get_decoder(NET_LAYER, LL_TYPE_IP) != NULL, "Could not find IP decoder.");
     }
     END_TEST
     
     START_TEST (test_get_decoder_tcp)
     {
    -  fail_if(get_decoder(PROTO_LAYER, NL_TYPE_TCP) == NULL, "Could not find TCP decoder.");
    +  ck_assert_msg(get_decoder(PROTO_LAYER, NL_TYPE_TCP) != NULL, "Could not find TCP decoder.");
     }
     END_TEST
     
     START_TEST (test_get_decoder_udp)
     {
    -  fail_if(get_decoder(PROTO_LAYER, NL_TYPE_UDP) == NULL, "Could not find UDP decoder.");
    +  ck_assert_msg(get_decoder(PROTO_LAYER, NL_TYPE_UDP) != NULL, "Could not find UDP decoder.");
     }
     END_TEST
     
    

Vulnerability mechanics

Root cause

"Missing bounds check on the copy length in `strncpy` allows a heap-based buffer overflow when the GG dissector copies an attacker-controlled description field into a fixed-size buffer."

Attack vector

An attacker sends a crafted GG protocol packet to a target running Ettercap. The packet contains a `description` field whose length, when subtracted by a protocol-specific offset, exceeds the fixed 71-byte destination buffer `tbuf2`. The original code used `strncpy(tbuf2, ..., gg->len - offset)` where the copy length was derived directly from the untrusted packet length without capping it to the buffer size, causing a heap-based buffer overflow [patch_id=2539849]. The attack is remote (network-delivered) but requires high complexity due to the need to craft a valid GG protocol packet that passes initial parsing [ref_id=1].

Affected code

The vulnerability resides in the GG dissector within `src/dissectors/ec_gg.c`. The function `FUNC_DECODER(dissector_gg)` processes GG protocol packets and copies the `description` field from several login and status header structures (`gg_login50_hdr`, `gg_login60_hdr`, `gg_login70_hdr`, `gg_status_hdr`, `gg_new_status_hdr`, `gg_status50_hdr`, `gg_status60_hdr`, `gg_status70_hdr`) into a fixed-size buffer `tbuf2` without proper bounds checking [patch_id=2539849][patch_id=2539850].

What the fix does

The patch introduces a `GG_MAX_LEN` constant (value 70) and caps the copy length using `MIN(gg->len - offset, GG_MAX_LEN)` before calling `strncpy` [patch_id=2539850]. This ensures that even if the packet advertises a large `description` length, no more than `GG_MAX_LEN` bytes are copied into `tbuf2`. Additionally, the header structure `description` fields are now declared as `char description[GG_MAX_LEN + 1]` instead of the hard-coded `char description[71]`, making the relationship between the buffer size and the copy limit explicit [patch_id=2539850]. The length check was also tightened from `(int)gg->len-offset

Preconditions

  • configEttercap must be running and actively dissecting GG protocol traffic (e.g., during a man-in-the-middle session).
  • networkAttacker must be able to send a crafted GG protocol packet to the target host running Ettercap.
  • inputThe crafted packet must contain a description field whose length exceeds the fixed 71-byte buffer after subtracting the protocol-specific offset.

Generated on May 26, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.