Commit message: Exit gracefully in case of corrupted filters (Closes issue #782)
CVE: CVE-2017-6430
CWE: CWE-125
Vulnerability description: The compile_tree function in ef_compiler.c in the Etterfilter utility in Ettercap 0.8.2 and earlier allows remote attackers to cause a denial of service (out-of-bounds read) via a crafted filter.

Project: ettercap
File: ef_compiler.c

struct block * compiler_add_ifblk(struct ifblock *ifb, struct block *blk)
{
   struct block *bl;

   SAFE_CALLOC(bl, 1, sizeof(struct block));

   /* copy the current instruction in the block */
   bl->type = BLK_IFBLK;
   bl->un.ifb = ifb;

   /* link it to the old block chain */
   bl->next = blk;

   return bl;
}

Project: ettercap
File: ef_compiler.c

struct block * compiler_add_instr(struct instruction *ins, struct block *blk)
{
   struct block *bl;

   SAFE_CALLOC(bl, 1, sizeof(struct block));

   /* copy the current instruction in the block */
   bl->type = BLK_INSTR;
   bl->un.ins = ins;

   /* link it to the old block chain */
   bl->next = blk;

   return bl;
}

Project: ettercap
File: ef_compiler.c

struct condition * compiler_concat_conditions(struct condition *a, u_int16 op, struct condition *b)
{
   struct condition *head = a;
   
   /* go to the last conditions in 'a' */
   while(a->next != NULL)
      a = a->next;
   
   /* set the operation */
   a->op = op;

   /* contatenate the two block */
   a->next = b;
   
   /* return the head of the conditions */
   return head;
}

Project: ettercap
File: ef_compiler.c

struct condition * compiler_create_condition(struct filter_op *fop)
{
   struct condition *cnd;

   SAFE_CALLOC(cnd, 1, sizeof(struct condition));
   
   /* copy the instruction */
   memcpy(&cnd->fop, fop, sizeof(struct filter_op));

   return cnd;
}

Project: ettercap
File: ef_compiler.c

struct ifblock * compiler_create_ifblock(struct condition *conds, struct block *blk)
{
   struct ifblock *ifblk;

   SAFE_CALLOC(ifblk, 1, sizeof(struct ifblock));

   /* associate the pointers */
   ifblk->conds = conds;
   ifblk->blk = blk;

   return ifblk;
}

Project: ettercap
File: ef_compiler.c

struct ifblock * compiler_create_ifelseblock(struct condition *conds, struct block *blk, struct block *elseblk)
{
   struct ifblock *ifblk;

   SAFE_CALLOC(ifblk, 1, sizeof(struct ifblock));
   
   /* associate the pointers */
   ifblk->conds = conds;
   ifblk->blk = blk;
   ifblk->elseblk = elseblk;

   return ifblk;
}

Project: ettercap
File: ef_compiler.c

struct instruction * compiler_create_instruction(struct filter_op *fop)
{
   struct instruction *ins;

   SAFE_CALLOC(ins, 1, sizeof(struct instruction));
   
   /* copy the instruction */
   memcpy(&ins->fop, fop, sizeof(struct filter_op));

   return ins;
}

Project: ettercap
File: ef_compiler.c

int compiler_set_root(struct block *blk)
{
   BUG_IF(blk == NULL);
   tree_root = blk;
   return E_SUCCESS;
}

Project: ettercap
File: ef_compiler.c

static void labels_to_offsets(void)
{
   struct unfold_elm *ue;
   struct unfold_elm *s;
   u_int32 offset = 0;

   fprintf(stdout, " Converting labels to real offsets ");
   fflush(stdout);
   
   TAILQ_FOREACH(ue, &unfolded_tree, next) {
      /* search only for jumps */
      if (ue->fop.opcode == FOP_JMP || 
          ue->fop.opcode == FOP_JTRUE ||
          ue->fop.opcode == FOP_JFALSE) {
        
         switch (ue->fop.opcode) {
            case FOP_JMP:
               ef_debug(1, "*"); 
               break;
            case FOP_JTRUE:
               ef_debug(1, "+");
               break;
            case FOP_JFALSE:
               ef_debug(1, "-");
               break;
         }
         
         /* search the offset associated with the label */
         TAILQ_FOREACH(s, &unfolded_tree, next) {
            if (s->label == ue->fop.op.jmp) {
               ue->fop.op.jmp = offset;
               /* reset the offset */
               offset = 0;
               break;
            }
            /* if it is an instruction, increment the offset */
            if (s->label == 0)
               offset++;
         }
      }
   }

   fprintf(stdout, " done.\n\n");
}

Project: ettercap
File: ef_compiler.c

static void unfold_blk(struct block **blk)
{
   struct unfold_elm *ue = NULL;
  
   BUG_IF(*blk == NULL);

   /* the progress bar */
   ef_debug(1, "+"); 
   
   do {
      switch((*blk)->type) {
         case BLK_INSTR:
            /* insert the instruction as is */
            SAFE_CALLOC(ue, 1, sizeof(struct unfold_elm));
            memcpy(&ue->fop, (*blk)->un.ins, sizeof(struct filter_op));
            TAILQ_INSERT_TAIL(&unfolded_tree, ue, next);
            break;

         case BLK_IFBLK:
            unfold_ifblk(blk);
            break;
            
         default:
            BUG("undefined tree element");
            break;
      } 
   } while ((*blk = (*blk)->next));
  
}

Project: ettercap
File: ef_compiler.c

static void unfold_ifblk(struct block **blk)
{
   struct ifblock *ifblk;
   struct unfold_elm *ue;
   u_int32 a = vlabel++; 
   u_int32 b = vlabel++; 
   u_int32 c = vlabel++; 

   /*
    * the virtual labels represent the three points of an if block:
    *
    *    if (conds) {
    * a ->
    *       ...
    *       jmp c;
    * b ->
    *    } else {
    *       ...
    *    }
    * c ->
    *
    * if the conds are true, jump to 'a'
    * if the conds are false, jump to 'b'
    * 'c' is used to skip the else if the conds were true
    */

   /* the progress bar */
   ef_debug(1, "#"); 
   
   /* cast the if block */
   ifblk = (*blk)->un.ifb;
  
   /* compile the conditions */
   unfold_conds(ifblk->conds, a, b);
   
   /* if the conditions are match, jump here */
   SAFE_CALLOC(ue, 1, sizeof(struct unfold_elm));
   ue->label = a;
   TAILQ_INSERT_TAIL(&unfolded_tree, ue, next);

   /* check if the block is empty. i.e.  { } */
   if (ifblk->blk != NULL) {
      /* recursively compile the main block */
      unfold_blk(&ifblk->blk);
   }

   /* 
    * if there is the else block, we have to skip it
    * if the condition was true
    */
   if (ifblk->elseblk != NULL) {
      SAFE_CALLOC(ue, 1, sizeof(struct unfold_elm));
      ue->fop.opcode = FOP_JMP;
      ue->fop.op.jmp = c;
      TAILQ_INSERT_TAIL(&unfolded_tree, ue, next);
   }
   
   /* if the conditions are NOT match, jump here (after the block) */
   SAFE_CALLOC(ue, 1, sizeof(struct unfold_elm));
   ue->label = b;
   TAILQ_INSERT_TAIL(&unfolded_tree, ue, next);
   
   /* recursively compile the else block */
   if (ifblk->elseblk != NULL) {
      unfold_blk(&ifblk->elseblk);
      /* this is the label to skip the else if the condition was true */
      SAFE_CALLOC(ue, 1, sizeof(struct unfold_elm));
      ue->label = c;
      TAILQ_INSERT_TAIL(&unfolded_tree, ue, next);
   }

}

Project: ettercap
File: ef_main.c

void ef_debug(u_char level, const char *message, ...)
{ 
   va_list ap;
   
   /* if not in debug don't print anything */
   if (EF_GBL_OPTIONS->debug < level)
      return;

   /* print the message */ 
   va_start(ap, message);
   vfprintf (stderr, message, ap);
   fflush(stderr);
   va_end(ap);
   
}

Project: ettercap
File: ef_main.c

void ef_globals_alloc(void)
{

   SAFE_CALLOC(ef_gbls, 1, sizeof(struct ef_globals));

   return;
}

Project: ettercap
File: ef_main.c

void ef_globals_free(void)
{
   SAFE_FREE(ef_gbls->source_file);
   SAFE_FREE(ef_gbls->output_file);
   SAFE_FREE(ef_gbls);

   return;

}

Project: ettercap
File: ef_output.c

static size_t add_data_segment(u_char **data, size_t base, u_char **string, size_t slen)
{
   /* make room for the new string */
   SAFE_REALLOC(*data, base + slen + 1);

   /* copy the string, NULL separated */
   memcpy(*data + base, *string, slen + 1);

   /* 
    * change the pointer to the new string location 
    * it is an offset from the base of the data segment
    */
   *string = (u_char *)base;
   
   /* retur the len of the added string */
   return slen + 1;
}

Project: ettercap
File: ef_output.c

static u_char * create_data_segment(struct filter_header *fh, struct filter_op *fop, size_t n)
{
   size_t i, len = 0;
   u_char *data = NULL;

   for (i = 0; i < n; i++) {
      
      switch(fop[i].opcode) {
         case FOP_FUNC:
            if (fop[i].op.func.slen) {
               ef_debug(1, "@");
               len += add_data_segment(&data, len, &fop[i].op.func.string, fop[i].op.func.slen);
            }
            if (fop[i].op.func.rlen) {
               ef_debug(1, "@");
               len += add_data_segment(&data, len, &fop[i].op.func.replace, fop[i].op.func.rlen);
            }
            break;
            
         case FOP_TEST:
            if (fop[i].op.test.slen) {
               ef_debug(1, "@");
               len += add_data_segment(&data, len, &fop[i].op.test.string, fop[i].op.test.slen);
            }
            break;

         case FOP_ASSIGN:
            if (fop[i].op.assign.slen) {
               ef_debug(1, "@");
               len += add_data_segment(&data, len, &fop[i].op.test.string, fop[i].op.test.slen);
            }
            break;
      }

   }
  
   /* where starts the code ? */
   fh->code = fh->data + len;
   
   return data;
}