/* * make an assignment. */ static int execute_assign(struct filter_op *fop, struct packet_object *po) { /* initialize to the beginning of the packet */ u_char *base = po->L2.header; /* check the offensiveness */ if (GBL_OPTIONS->unoffensive) JIT_FAULT("Cannot modify packets in unoffensive mode"); DEBUG_MSG("filter engine: execute_assign: L%d O%d S%d", fop->op.assign.level, fop->op.assign.offset, fop->op.assign.size); /* * point to the right base. */ switch (fop->op.assign.level) { case 2: base = po->L2.header; break; case 3: base = po->L3.header; break; case 4: base = po->L4.header; break; case 5: base = po->DATA.data; break; default: JIT_FAULT("unsupported assignment level [%d]", fop->op.assign.level); break; } /* * get the value with the proper size. * 0 is a special case for strings (even binary) */ switch (fop->op.assign.size) { case 0: memcpy(base + fop->op.assign.offset, fop->op.assign.string, fop->op.assign.slen); break; case 1: *(u_int8 *)(base + fop->op.assign.offset) = (fop->op.assign.value & 0xff); break; case 2: *(u_int16 *)(base + fop->op.assign.offset) = ntohs(fop->op.assign.value & 0xffff); break; case 4: *(u_int32 *)(base + fop->op.assign.offset) = ntohl(fop->op.assign.value & 0xffffffff); break; default: JIT_FAULT("unsupported assign size [%d]", fop->op.assign.size); break; } /* mark the packet as modified */ po->flags |= PO_MODIFIED; return FLAG_TRUE; }
/* * inject a file into the communication */ static int func_inject(struct filter_op *fop, struct packet_object *po) { int fd; void *file; size_t size, ret; /* check the offensiveness */ if (GBL_OPTIONS->unoffensive) JIT_FAULT("Cannot inject packets in unoffensive mode"); DEBUG_MSG("filter engine: func_inject %s", fop->op.func.string); /* open the file */ if ((fd = open(fop->op.func.string, O_RDONLY | O_BINARY)) == -1) { USER_MSG("filter engine: inject(): File not found (%s)\n", fop->op.func.string); return -EFATAL; } /* get the size */ size = lseek(fd, 0, SEEK_END); /* load the file in memory */ SAFE_CALLOC(file, size, sizeof(char)); /* rewind the pointer */ lseek(fd, 0, SEEK_SET); ret = read(fd, file, size); close(fd); if (ret != size) FATAL_MSG("Cannot read the file into memory"); /* check if we are overflowing pcap buffer */ if(GBL_PCAP->snaplen - (po->L4.header - (po->packet + po->L2.len) + po->L4.len) <= po->DATA.len + size) JIT_FAULT("injected file too long"); /* copy the file into the buffer */ memcpy(po->DATA.data + po->DATA.len, file, size); /* Adjust packet len and delta */ po->DATA.delta += size; po->DATA.len += size; /* mark the packet as modified */ po->flags |= PO_MODIFIED; /* unset the flag to be dropped */ if (po->flags & PO_DROPPED) po->flags ^= PO_DROPPED; /* close and unmap the file */ SAFE_FREE(file); return ESUCCESS; }
/* * inject output of a executable into the communication */ static int func_execinject(struct filter_op *fop, struct packet_object *po) { FILE *pstream = NULL; unsigned char *output = NULL; size_t n = 0, offset = 0, size = 128; unsigned char buf[size]; /* check the offensiveness */ if (GBL_OPTIONS->unoffensive) JIT_FAULT("Cannot inject packets in unoffensive mode"); DEBUG_MSG("filter engine: func_execinject %s", fop->op.func.string); /* open the pipe */ if ((pstream = popen((const char*)fop->op.func.string, "r")) == NULL) { USER_MSG("filter engine: execinject(): Command not found (%s)\n", fop->op.func.string); return -E_FATAL; } while ((n = read(fileno(pstream), buf, size)) != 0) { if (output == NULL) { SAFE_CALLOC(output, offset+n, sizeof(unsigned char)); } else { SAFE_REALLOC(output, sizeof(unsigned char)*(offset+n)); } memcpy(output+offset, buf, n); offset += n; } /* close pipe stream */ pclose(pstream); /* check if we are overflowing pcap buffer */ if(GBL_PCAP->snaplen - (po->L4.header - (po->packet + po->L2.len) + po->L4.len) <= po->DATA.len + (unsigned)offset) JIT_FAULT("injected output too long"); /* copy the output into the buffer */ memcpy(po->DATA.data + po->DATA.len, output, offset); /* Adjust packet len and delta */ po->DATA.delta += offset; po->DATA.len += offset; /* mark the packet as modified */ po->flags |= PO_MODIFIED; /* unset the flag to be dropped */ if (po->flags & PO_DROPPED) po->flags ^= PO_DROPPED; /* free memory */ SAFE_FREE(output); return E_SUCCESS; }
/* * log the packet to a file */ static int func_log(struct filter_op *fop, struct packet_object *po) { int fd; DEBUG_MSG("filter engine: func_log"); /* open the file */ fd = open(fop->op.func.string, O_CREAT | O_APPEND | O_RDWR | O_BINARY, 0600); if (fd == -1) { USER_MSG("filter engine: Cannot open file %s\n", fop->op.func.string); return -EFATAL; } /* which data should I have to log ? */ switch(fop->op.func.level) { case 5: if (write(fd, po->DATA.data, po->DATA.len) < 0) USER_MSG("filter engine: Cannot write to file...%d\n", errno); break; case 6: if (write(fd, po->DATA.disp_data, po->DATA.disp_len) < 0) USER_MSG("filter engine: Cannot write to file...\n"); break; default: JIT_FAULT("unsupported log level [%d]", fop->op.func.level); break; } /* close the file */ close(fd); return ESUCCESS; }
/* * search a string with a regex and return TRUE if found */ static int func_regex(struct filter_op *fop, struct packet_object *po) { switch (fop->op.func.level) { case 5: /* search in the real packet */ if (regexec(fop->op.func.ropt->regex, po->DATA.data, 0, NULL, 0) == 0) return ESUCCESS; break; case 6: /* search in the decoded/decrypted packet */ if (regexec(fop->op.func.ropt->regex, po->DATA.disp_data, 0, NULL, 0) == 0) return ESUCCESS; break; default: JIT_FAULT("unsupported regex level [%d]", fop->op.func.level); break; } return -ENOTFOUND; }
/* * search a string and return TRUE if found */ static int func_search(struct filter_op *fop, struct packet_object *po) { switch (fop->op.func.level) { case 5: /* search in the real packet */ if (memmem(po->DATA.data, po->DATA.len, fop->op.func.string, fop->op.func.slen)) return ESUCCESS; break; case 6: /* search in the decoded/decrypted packet */ if (memmem(po->DATA.disp_data, po->DATA.disp_len, fop->op.func.string, fop->op.func.slen)) return ESUCCESS; break; default: JIT_FAULT("unsupported search level [%d]", fop->op.func.level); break; } return -ENOTFOUND; }
/* * JIT interpreter for binary filters. * it process the filter_ops and apply the instructions * on the given packet object */ static int filter_engine(struct filter_op *fop, struct packet_object *po) { u_int32 eip = 0; u_int32 flags = 0; #define FLAG_FALSE 0 #define FLAG_TRUE 1 /* sanity check */ BUG_IF(fop == NULL); FILTERS_LOCK; /* loop until EXIT */ while (fop[eip].opcode != FOP_EXIT) { switch (fop[eip].opcode) { case FOP_TEST: if (execute_test(&fop[eip], po) == FLAG_TRUE) flags |= FLAG_TRUE; else flags &= ~(FLAG_TRUE); break; case FOP_ASSIGN: execute_assign(&fop[eip], po); /* assignment always returns true */ flags |= FLAG_TRUE; break; case FOP_INC: case FOP_DEC: execute_incdec(&fop[eip], po); /* inc/dec always return true */ flags |= FLAG_TRUE; break; case FOP_FUNC: if (execute_func(&fop[eip], po) == FLAG_TRUE) flags |= FLAG_TRUE; else flags &= ~(FLAG_TRUE); break; case FOP_JMP: /* jump the the next eip */ eip = fop[eip].op.jmp; continue; break; case FOP_JTRUE: /* jump the the next eip if the TRUE FLAG is set*/ if (flags & FLAG_TRUE) { eip = fop[eip].op.jmp; continue; } break; case FOP_JFALSE: /* jump the the next eip if the TRUE FLAG is NOT set */ if (!(flags & FLAG_TRUE)) { eip = fop[eip].op.jmp; continue; } break; default: FILTERS_UNLOCK; JIT_FAULT("unsupported opcode [%d] (execution interrupted)", fop[eip].opcode); break; } /* autoincrement the instruction pointer */ eip++; } FILTERS_UNLOCK; return 0; }
/* * replace a string in the packet object DATA.data */ static int func_replace(struct filter_op *fop, struct packet_object *po) { u_int8 *ptr; u_int8 *end; size_t len; size_t slen = fop->op.func.slen; size_t rlen = fop->op.func.rlen; /* check the offensiveness */ if (GBL_OPTIONS->unoffensive) JIT_FAULT("Cannot modify packets in unoffensive mode"); /* check if it exist at least one */ if (!memmem(po->DATA.data, po->DATA.len, fop->op.func.string, fop->op.func.slen) ) return -ENOTFOUND; DEBUG_MSG("filter engine: func_replace"); /* * XXX BIG WARNING: * maxlen is GBL_PCAP->snaplen, but we can't * rely on this forever... */ /* take the beginning and the end of the data */ ptr = po->DATA.data; end = ptr + po->DATA.len; /* do the replacement */ do { /* the len of the buffer to be analized */ len = end - ptr; /* search the string */ ptr = memmem(ptr, len, fop->op.func.string, slen); /* string no found, exit */ if (ptr == NULL) break; /* update the len */ len = end - ptr - slen; /* set the delta */ po->DATA.delta += rlen - slen; po->DATA.len += rlen - slen; /* check if we are overflowing pcap buffer */ BUG_IF(po->DATA.data < po->packet); BUG_IF((u_int16)(GBL_PCAP->snaplen - (po->DATA.data - po->packet)) <= po->DATA.len); /* move the buffer to make room for the replacement string */ memmove(ptr + rlen, ptr + slen, len); /* copy the replacemente string */ memcpy(ptr, fop->op.func.replace, rlen); /* move the ptr after the replaced string */ ptr += rlen; /* adjust the new buffer end */ end += rlen - slen; /* mark the packet as modified */ po->flags |= PO_MODIFIED; } while(ptr != NULL && ptr < end); return ESUCCESS; }
/* * evaluate a perl regex and return TRUE if found */ static int func_pcre(struct filter_op *fop, struct packet_object *po) { #ifndef HAVE_PCRE JIT_FAULT("pcre_regex support not compiled in ettercap"); return -ENOTFOUND; #else int ovec[PCRE_OVEC_SIZE]; int ret; DEBUG_MSG("filter engine: func_pcre"); memset(&ovec, 0, sizeof(ovec)); switch (fop->op.func.level) { case 5: /* search in the real packet */ if ( (ret = pcre_exec(fop->op.func.ropt->pregex, fop->op.func.ropt->preg_extra, po->DATA.data, po->DATA.len, 0, 0, ovec, sizeof(ovec) / sizeof(*ovec))) < 0) return -ENOTFOUND; /* the pcre wants to modify the packet */ if (fop->op.func.replace) { u_char *replaced; u_char *q = fop->op.func.replace; size_t i, nlen = 0; /* don't modify if in unoffensive mode */ if (GBL_OPTIONS->unoffensive) JIT_FAULT("Cannot modify packets in unoffensive mode"); /* * the replaced string will not be larger than * the matched string + replacement string */ SAFE_CALLOC(replaced, ovec[1] + strlen(q) + 1, sizeof(char)); po->flags |= PO_MODIFIED; /* make the replacement */ for (i = 0; i < fop->op.func.rlen; i++) { /* there is a position marker */ if (q[i] == '$' && q[i - 1] != '\\') { int marker = atoi(q + i + 1); int t = ovec[marker * 2]; int r = ovec[marker * 2 + 1]; /* skip the chars of the marker */ while (q[++i + 1] != ' ' && q[i] < q[strlen(q)] ); /* check if the requested marker was found in the pce */ if (marker > ret - 1 || marker == 0) JIT_FAULT("Too many marker for this pcre expression"); /* copy the sub-string in place of the marker */ for ( ; t < r; t++) replaced[nlen++] = po->DATA.data[t]; /* the $ is escaped, copy it */ } else if (q[i] == '\\' && q[i + 1] == '$') { replaced[nlen++] = q[++i]; /* all the other chars are copied as they are */ } else { replaced[nlen++] = q[i]; } } /* calculate the delta */ po->DATA.delta += nlen - po->DATA.len; po->DATA.len = nlen; /* check if we are overflowing pcap buffer */ BUG_IF(po->DATA.data < po->packet); BUG_IF((u_int16)(GBL_PCAP->snaplen - (po->DATA.data - po->packet)) <= nlen); /* copy the temp buffer on the original packet */ memcpy(po->DATA.data, replaced, nlen); SAFE_FREE(replaced); } break; case 6: /* search in the decoded one */ if ( pcre_exec(fop->op.func.ropt->pregex, fop->op.func.ropt->preg_extra, po->DATA.disp_data, po->DATA.disp_len, 0, 0, NULL, 0) < 0) return -ENOTFOUND; break; default: JIT_FAULT("unsupported pcre_regex level [%d]", fop->op.func.level); break; } return ESUCCESS; #endif }
/* * make an increment or decrement. */ static int execute_incdec(struct filter_op *fop, struct packet_object *po) { /* initialize to the beginning of the packet */ u_char *base = po->L2.header; /* check the offensiveness */ if (GBL_OPTIONS->unoffensive) JIT_FAULT("Cannot modify packets in unoffensive mode"); DEBUG_MSG("filter engine: execute_incdec: L%d O%d S%d", fop->op.assign.level, fop->op.assign.offset, fop->op.assign.size); /* * point to the right base. */ switch (fop->op.assign.level) { case 2: base = po->L2.header; break; case 3: base = po->L3.header; break; case 4: base = po->L4.header; break; case 5: base = po->DATA.data; break; default: JIT_FAULT("unsupported inc/dec level [%d]", fop->op.assign.level); break; } /* * inc/dec the value with the proper size. */ switch (fop->op.assign.size) { case 1: if (fop->opcode == FOP_INC) *(u_int8 *)(base + fop->op.assign.offset) += (fop->op.assign.value & 0xff); else *(u_int8 *)(base + fop->op.assign.offset) -= (fop->op.assign.value & 0xff); break; case 2: if (fop->opcode == FOP_INC) *(u_int16 *)(base + fop->op.assign.offset) += ntohs(fop->op.assign.value & 0xffff); else *(u_int16 *)(base + fop->op.assign.offset) -= ntohs(fop->op.assign.value & 0xffff); break; case 4: if (fop->opcode == FOP_INC) *(u_int32 *)(base + fop->op.assign.offset) += ntohl(fop->op.assign.value & 0xffffffff); else *(u_int32 *)(base + fop->op.assign.offset) -= ntohl(fop->op.assign.value & 0xffffffff); break; default: JIT_FAULT("unsupported inc/dec size [%d]", fop->op.assign.size); break; } /* mark the packet as modified */ po->flags |= PO_MODIFIED; return FLAG_TRUE; }
/* * execute a test. * return FLAG_TRUE if the test was successful */ static int execute_test(struct filter_op *fop, struct packet_object *po) { /* initialize to the beginning of the packet */ u_char *base = po->L2.header; int (*cmp_func)(u_int32, u_int32) = &cmp_eq; /* * point to the right base. * if the test is L3.ttl, we have to start from * L3.header to count for offset. */ switch (fop->op.test.level) { case 2: base = po->L2.header; break; case 3: base = po->L3.header; break; case 4: base = po->L4.header; break; case 5: base = po->DATA.data; break; case 6: base = po->DATA.disp_data; break; default: JIT_FAULT("unsupported test level [%d]", fop->op.test.level); break; } /* set the pointer to the comparison function */ switch(fop->op.test.op) { case FTEST_EQ: cmp_func = &cmp_eq; break; case FTEST_NEQ: cmp_func = &cmp_neq; break; case FTEST_LT: cmp_func = &cmp_lt; break; case FTEST_GT: cmp_func = &cmp_gt; break; case FTEST_LEQ: cmp_func = &cmp_leq; break; case FTEST_GEQ: cmp_func = &cmp_geq; break; default: JIT_FAULT("unsupported test operation"); break; } /* * get the value with the proper size. * 0 is a special case for strings (even binary) */ switch (fop->op.test.size) { case 0: /* string comparison */ if (cmp_func(memcmp(base + fop->op.test.offset, fop->op.test.string, fop->op.test.slen), 0) ) return FLAG_TRUE; break; case 1: /* char comparison */ if (cmp_func(*(u_int8 *)(base + fop->op.test.offset), (fop->op.test.value & 0xff)) ) return FLAG_TRUE; break; case 2: /* short int comparison */ if (cmp_func(htons(*(u_int16 *)(base + fop->op.test.offset)), (fop->op.test.value & 0xffff)) ) return FLAG_TRUE; break; case 4: /* int comparison */ if (cmp_func(htonl(*(u_int32 *)(base + fop->op.test.offset)), (fop->op.test.value & 0xffffffff)) ) return FLAG_TRUE; break; default: JIT_FAULT("unsupported test size [%d]", fop->op.test.size); break; } return FLAG_FALSE; }
/* * execute a function. * return FLAG_TRUE if the function was successful */ static int execute_func(struct filter_op *fop, struct packet_object *po) { switch (fop->op.func.op) { case FFUNC_SEARCH: /* search the string */ if (func_search(fop, po) == ESUCCESS) return FLAG_TRUE; break; case FFUNC_REGEX: /* search the string with a regex */ if (func_regex(fop, po) == ESUCCESS) return FLAG_TRUE; break; case FFUNC_PCRE: /* evaluate a perl regex */ if (func_pcre(fop, po) == ESUCCESS) return FLAG_TRUE; break; case FFUNC_REPLACE: /* replace the string */ if (func_replace(fop, po) == ESUCCESS) return FLAG_TRUE; break; case FFUNC_INJECT: /* replace the string */ if (func_inject(fop, po) == ESUCCESS) return FLAG_TRUE; break; case FFUNC_LOG: /* log the packet */ if (func_log(fop, po) == ESUCCESS) return FLAG_TRUE; break; case FFUNC_DROP: /* drop the packet */ func_drop(po); return FLAG_TRUE; break; case FFUNC_KILL: /* kill the connection */ func_kill(po); return FLAG_TRUE; break; case FFUNC_MSG: /* display the message to the user */ USER_MSG("%s", fop->op.func.string); return FLAG_TRUE; break; case FFUNC_EXEC: /* execute the command */ if (func_exec(fop) == ESUCCESS) return FLAG_TRUE; break; default: JIT_FAULT("unsupported function [%d]", fop->op.func.op); break; } return FLAG_FALSE; }
/* * evaluate a perl regex and return TRUE if found */ static int func_pcre(struct filter_op *fop, struct packet_object *po) { #ifndef HAVE_PCRE JIT_FAULT("pcre_regex support not compiled in ettercap"); return -ENOTFOUND; #else int ovec[PCRE_OVEC_SIZE]; int ret; DEBUG_MSG("filter engine: func_pcre"); memset(&ovec, 0, sizeof(ovec)); switch (fop->op.func.level) { case 5: /* search in the real packet */ if ( (ret = pcre_exec(fop->op.func.ropt->pregex, fop->op.func.ropt->preg_extra, po->DATA.data, po->DATA.len, 0, 0, ovec, sizeof(ovec) / sizeof(*ovec))) < 0) return -ENOTFOUND; /* the pcre wants to modify the packet */ if (fop->op.func.replace) { u_char *replaced; u_char *q = fop->op.func.replace; size_t i, slen = 0; /* don't modify if in unoffensive mode */ if (GBL_OPTIONS->unoffensive) JIT_FAULT("Cannot modify packets in unoffensive mode"); /* * worst case: the resulting string will need: * (n * |input|) + |subst| bytes * where * |input| is the length of the matched input string (not the regex!) * |subst| is the length of the substition string * n is the number of replacement markers in subst * * therefore, we need to count the number of $ characters first * to get an upper limit of the buffer space needed */ int markers = 0; for (i=0; q[i]; i++) { if (q[i] == '$') markers++; } /* now: i = strlen(q) */ SAFE_CALLOC(replaced, markers*(ovec[1]-ovec[0]) + i + 1, sizeof(char)); po->flags |= PO_MODIFIED; /* make the replacement */ uint8_t escaped = 0; for (i = 0; i < fop->op.func.rlen; i++) { /* we encounter an escape character (\), so the next character is to be taken literally */ if (!escaped && q[i] == '\\') { escaped = 1; } /* there is an unescaped position marker */ else if (!escaped && q[i] == '$') { /* a marker is succeeded by an integer, make sure it is there */ if (q[i+1] == '\0') JIT_FAULT("Incomplete marker at end of substitution string"); /* so now we can safely move on to the next character, our digit */ i++; /* we only support up to 9 markers since we only parse a single digit */ if (q[i] < '0' || q[i] > '9') JIT_FAULT("Incomplete marker without integer in substitution string"); int marker = q[i]-'0'; /* check if the requested marker was found in the pce */ if (marker > ret - 1 || marker == 0) JIT_FAULT("Too many marker for this pcre expression"); int t = ovec[marker * 2]; int r = ovec[marker * 2 + 1]; /* copy the sub-string in place of the marker */ for ( ; t < r; t++) replaced[slen++] = po->DATA.data[t]; } /* anything else either has no special meaning or is escaped, * so we just copy it */ else { replaced[slen++] = q[i]; escaped = 0; } } /* calculate the delta */ int delta = (ovec[0]-ovec[1])+slen; /* check if we are overflowing pcap buffer */ BUG_IF(po->DATA.data < po->packet); BUG_IF((u_int16)(GBL_PCAP->snaplen - (po->DATA.data - po->packet)) <= po->DATA.len+delta); /* if the substitution string has a different length than the * matched original string, we have to move around some data */ int size_left = po->DATA.len - ovec[0] - slen; int data_left = po->DATA.len - ovec[1]; DEBUG_MSG("func_pcre: match from %d to %d, substitution length is %d\n", ovec[0], ovec[1], slen); DEBUG_MSG("func_pcre: packet size changed by %d bytes\n", delta); if (delta != 0) { /* copy everything behind the matched string to the new position */ memcpy(po->DATA.data+ovec[0]+slen, po->DATA.data + ovec[1], size_left < data_left ? size_left : data_left); } /* copy the modified buffer on the original packet */ memcpy(po->DATA.data+ovec[0], replaced, slen); po->DATA.delta += delta; po->DATA.len += delta; SAFE_FREE(replaced); } break; case 6: /* search in the decoded one */ if ( pcre_exec(fop->op.func.ropt->pregex, fop->op.func.ropt->preg_extra, po->DATA.disp_data, po->DATA.disp_len, 0, 0, NULL, 0) < 0) return -ENOTFOUND; break; default: JIT_FAULT("unsupported pcre_regex level [%d]", fop->op.func.level); break; } return ESUCCESS; #endif }