|
|
|
|
@ -26,18 +26,177 @@
|
|
|
|
|
#include "suricata-common.h"
|
|
|
|
|
#include "suricata.h"
|
|
|
|
|
#include "detect.h"
|
|
|
|
|
#include "detect-parse.h"
|
|
|
|
|
#include "detect-engine-analyzer.h"
|
|
|
|
|
#include "detect-engine-mpm.h"
|
|
|
|
|
#include "conf.h"
|
|
|
|
|
#include "detect-content.h"
|
|
|
|
|
#include "detect-flow.h"
|
|
|
|
|
#include "detect-flags.h"
|
|
|
|
|
#include "util-print.h"
|
|
|
|
|
|
|
|
|
|
static int rule_warnings_only = 0;
|
|
|
|
|
static FILE *rule_engine_analysis_FD = NULL;
|
|
|
|
|
static FILE *fp_engine_analysis_FD = NULL;
|
|
|
|
|
static pcre *percent_re = NULL;
|
|
|
|
|
static pcre_extra *percent_re_study = NULL;
|
|
|
|
|
static char log_path[PATH_MAX];
|
|
|
|
|
|
|
|
|
|
void EngineAnalysisFP(Signature *s, char *line)
|
|
|
|
|
{
|
|
|
|
|
int fast_pattern_set = 0;
|
|
|
|
|
int fast_pattern_only_set = 0;
|
|
|
|
|
int fast_pattern_chop_set = 0;
|
|
|
|
|
DetectContentData *fp_cd = NULL;
|
|
|
|
|
SigMatch *mpm_sm = s->mpm_sm;
|
|
|
|
|
|
|
|
|
|
if (mpm_sm != NULL) {
|
|
|
|
|
fp_cd = (DetectContentData *)mpm_sm->ctx;
|
|
|
|
|
if (fp_cd->flags & DETECT_CONTENT_FAST_PATTERN) {
|
|
|
|
|
fast_pattern_set = 1;
|
|
|
|
|
if (fp_cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
|
|
|
|
|
fast_pattern_only_set = 1;
|
|
|
|
|
} else if (fp_cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
|
|
|
|
|
fast_pattern_chop_set = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "== Sid: %u ==\n", s->id);
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "%s\n", line);
|
|
|
|
|
|
|
|
|
|
fprintf(fp_engine_analysis_FD, " Fast Pattern analysis:\n");
|
|
|
|
|
if (fp_cd == NULL) {
|
|
|
|
|
fprintf(fp_engine_analysis_FD, " No content present\n");
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(fp_engine_analysis_FD, " Fast pattern matcher: ");
|
|
|
|
|
int list_type = SigMatchListSMBelongsTo(s, mpm_sm);
|
|
|
|
|
if (list_type == DETECT_SM_LIST_PMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "content\n");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_UMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "http uri content\n");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HRUDMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "http raw uri content\n");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HHDMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "http header content\n");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HRHDMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "http raw header content\n");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HMDMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "http method content\n");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HCDMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "http cookie content\n");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HCBDMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "http client body content\n");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HSBDMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "http server body content\n");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HSCDMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "http stat code content\n");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HSMDMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "http stat msg content\n");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HUADMATCH)
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "http user agent content\n");
|
|
|
|
|
|
|
|
|
|
fprintf(fp_engine_analysis_FD, " Fast pattern set: %s\n", fast_pattern_set ? "yes" : "no");
|
|
|
|
|
fprintf(fp_engine_analysis_FD, " Fast pattern only set: %s\n",
|
|
|
|
|
fast_pattern_only_set ? "yes" : "no");
|
|
|
|
|
fprintf(fp_engine_analysis_FD, " Fast pattern chop set: %s\n",
|
|
|
|
|
fast_pattern_chop_set ? "yes" : "no");
|
|
|
|
|
if (fast_pattern_chop_set) {
|
|
|
|
|
fprintf(fp_engine_analysis_FD, " Fast pattern offset, length: %u, %u\n",
|
|
|
|
|
fp_cd->fp_chop_offset, fp_cd->fp_chop_len);
|
|
|
|
|
}
|
|
|
|
|
fprintf(fp_engine_analysis_FD, " Content negated: %s\n",
|
|
|
|
|
(fp_cd->flags & DETECT_CONTENT_NEGATED) ? "yes" : "no");
|
|
|
|
|
|
|
|
|
|
uint16_t patlen = fp_cd->content_len;
|
|
|
|
|
uint8_t *pat = SCMalloc(fp_cd->content_len + 1);
|
|
|
|
|
if (unlikely(pat == NULL)) {
|
|
|
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
memcpy(pat, fp_cd->content, fp_cd->content_len);
|
|
|
|
|
pat[fp_cd->content_len] = '\0';
|
|
|
|
|
fprintf(fp_engine_analysis_FD, " Original content: ");
|
|
|
|
|
PrintRawUriFp(fp_engine_analysis_FD, pat, patlen);
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "\n");
|
|
|
|
|
|
|
|
|
|
if (fast_pattern_chop_set) {
|
|
|
|
|
SCFree(pat);
|
|
|
|
|
patlen = fp_cd->fp_chop_len;
|
|
|
|
|
pat = SCMalloc(fp_cd->fp_chop_len + 1);
|
|
|
|
|
if (unlikely(pat == NULL)) {
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
memcpy(pat, fp_cd->content + fp_cd->fp_chop_offset, fp_cd->fp_chop_len);
|
|
|
|
|
pat[fp_cd->fp_chop_len] = '\0';
|
|
|
|
|
fprintf(fp_engine_analysis_FD, " Final content: ");
|
|
|
|
|
PrintRawUriFp(fp_engine_analysis_FD, pat, patlen);
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "\n");
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(fp_engine_analysis_FD, " Final content: ");
|
|
|
|
|
PrintRawUriFp(fp_engine_analysis_FD, pat, patlen);
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "\n");
|
|
|
|
|
}
|
|
|
|
|
SCFree(pat);
|
|
|
|
|
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Sets up the fast pattern analyzer according to the config.
|
|
|
|
|
*
|
|
|
|
|
* \retval 1 If rule analyzer successfully enabled.
|
|
|
|
|
* \retval 0 If not enabled.
|
|
|
|
|
*/
|
|
|
|
|
int SetupFPAnalyzer(void)
|
|
|
|
|
{
|
|
|
|
|
int fp_engine_analysis_set = 0;
|
|
|
|
|
|
|
|
|
|
if ((ConfGetBool("engine-analysis.rules-fast-pattern",
|
|
|
|
|
&fp_engine_analysis_set)) == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fp_engine_analysis_set == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
char *log_dir;
|
|
|
|
|
if (ConfGet("default-log-dir", &log_dir) != 1)
|
|
|
|
|
log_dir = DEFAULT_LOG_DIR;
|
|
|
|
|
snprintf(log_path, sizeof(log_path), "%s/%s", log_dir,
|
|
|
|
|
"rules_fast_pattern.txt");
|
|
|
|
|
|
|
|
|
|
fp_engine_analysis_FD = fopen(log_path, "w");
|
|
|
|
|
if (fp_engine_analysis_FD == NULL) {
|
|
|
|
|
SCLogError(SC_ERR_FOPEN, "failed to open %s: %s", log_path,
|
|
|
|
|
strerror(errno));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCLogInfo("Engine-Analyis for fast_pattern printed to file - %s",
|
|
|
|
|
log_path);
|
|
|
|
|
|
|
|
|
|
struct timeval tval;
|
|
|
|
|
struct tm *tms;
|
|
|
|
|
gettimeofday(&tval, NULL);
|
|
|
|
|
struct tm local_tm;
|
|
|
|
|
tms = (struct tm *)SCLocalTime(tval.tv_sec, &local_tm);
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "----------------------------------------------"
|
|
|
|
|
"---------------------\n");
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "Date: %" PRId32 "/%" PRId32 "/%04d -- "
|
|
|
|
|
"%02d:%02d:%02d\n",
|
|
|
|
|
tms->tm_mday, tms->tm_mon + 1, tms->tm_year + 1900, tms->tm_hour,
|
|
|
|
|
tms->tm_min, tms->tm_sec);
|
|
|
|
|
fprintf(fp_engine_analysis_FD, "----------------------------------------------"
|
|
|
|
|
"---------------------\n");
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Sets up the rule analyzer according to the config
|
|
|
|
|
* \retval 1 if rule analyzer successfully enabled
|
|
|
|
|
@ -66,6 +225,9 @@ int SetupRuleAnalyzer(void)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCLogInfo("Engine-Analyis for rules printed to file - %s",
|
|
|
|
|
log_path);
|
|
|
|
|
|
|
|
|
|
struct timeval tval;
|
|
|
|
|
struct tm *tms;
|
|
|
|
|
gettimeofday(&tval, NULL);
|
|
|
|
|
@ -97,6 +259,17 @@ int SetupRuleAnalyzer(void)
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CleanupFPAnalyzer(void)
|
|
|
|
|
{
|
|
|
|
|
if (fp_engine_analysis_FD != NULL) {
|
|
|
|
|
fclose(fp_engine_analysis_FD);
|
|
|
|
|
fp_engine_analysis_FD = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CleanupRuleAnalyzer(void) {
|
|
|
|
|
if (rule_engine_analysis_FD != NULL) {
|
|
|
|
|
SCLogInfo("Engine-Analyis for rules printed to file - %s", log_path);
|
|
|
|
|
@ -156,6 +329,86 @@ int PerCentEncodingMatch (uint8_t *content, uint8_t content_len)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void EngineAnalysisRulesPrintFP(Signature *s)
|
|
|
|
|
{
|
|
|
|
|
DetectContentData *fp_cd = NULL;
|
|
|
|
|
SigMatch *mpm_sm = s->mpm_sm;
|
|
|
|
|
|
|
|
|
|
if (mpm_sm != NULL) {
|
|
|
|
|
fp_cd = (DetectContentData *)mpm_sm->ctx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fp_cd == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t patlen = fp_cd->content_len;
|
|
|
|
|
uint8_t *pat = SCMalloc(fp_cd->content_len + 1);
|
|
|
|
|
if (unlikely(pat == NULL)) {
|
|
|
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
memcpy(pat, fp_cd->content, fp_cd->content_len);
|
|
|
|
|
pat[fp_cd->content_len] = '\0';
|
|
|
|
|
|
|
|
|
|
if (fp_cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
|
|
|
|
|
SCFree(pat);
|
|
|
|
|
patlen = fp_cd->fp_chop_len;
|
|
|
|
|
pat = SCMalloc(fp_cd->fp_chop_len + 1);
|
|
|
|
|
if (unlikely(pat == NULL)) {
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
memcpy(pat, fp_cd->content + fp_cd->fp_chop_offset, fp_cd->fp_chop_len);
|
|
|
|
|
pat[fp_cd->fp_chop_len] = '\0';
|
|
|
|
|
fprintf(rule_engine_analysis_FD, " Fast Pattern \"");
|
|
|
|
|
PrintRawUriFp(rule_engine_analysis_FD, pat, patlen);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(rule_engine_analysis_FD, " Fast Pattern \"");
|
|
|
|
|
PrintRawUriFp(rule_engine_analysis_FD, pat, patlen);
|
|
|
|
|
}
|
|
|
|
|
SCFree(pat);
|
|
|
|
|
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "\" on \"");
|
|
|
|
|
|
|
|
|
|
int list_type = SigMatchListSMBelongsTo(s, mpm_sm);
|
|
|
|
|
if (list_type == DETECT_SM_LIST_PMATCH) {
|
|
|
|
|
int payload = 0;
|
|
|
|
|
int stream = 0;
|
|
|
|
|
if (SignatureHasPacketContent(s))
|
|
|
|
|
payload = 1;
|
|
|
|
|
if (SignatureHasStreamContent(s))
|
|
|
|
|
stream = 1;
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "%s",
|
|
|
|
|
payload ? (stream ? "payload and reassembled stream" : "payload") : "reassembled stream");
|
|
|
|
|
}
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_UMATCH)
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "http uri content");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HRUDMATCH)
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "http raw uri content");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HHDMATCH)
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "http header content");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HRHDMATCH)
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "http raw header content");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HMDMATCH)
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "http method content");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HCDMATCH)
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "http cookie content");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HCBDMATCH)
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "http client body content");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HSBDMATCH)
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "http server body content");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HSCDMATCH)
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "http stat code content");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HSMDMATCH)
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "http stat msg content");
|
|
|
|
|
else if (list_type == DETECT_SM_LIST_HUADMATCH)
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "http user agent content");
|
|
|
|
|
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "\" buffer.\n");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Prints analysis of loaded rules.
|
|
|
|
|
*
|
|
|
|
|
@ -214,6 +467,7 @@ void EngineAnalysisRules(Signature *s, char *line)
|
|
|
|
|
uint32_t warn_encoding_norm_http_buf = 0;
|
|
|
|
|
uint32_t warn_offset_depth_pkt_stream = 0;
|
|
|
|
|
uint32_t warn_offset_depth_alproto = 0;
|
|
|
|
|
uint32_t warn_non_alproto_fp_for_alproto_sig = 0;
|
|
|
|
|
|
|
|
|
|
if (s->init_flags & SIG_FLAG_INIT_BIDIREC) {
|
|
|
|
|
rule_bidirectional = 1;
|
|
|
|
|
@ -454,6 +708,11 @@ void EngineAnalysisRules(Signature *s, char *line)
|
|
|
|
|
rule_warning += 1;
|
|
|
|
|
warn_offset_depth_alproto = 1;
|
|
|
|
|
}
|
|
|
|
|
if (s->mpm_sm != NULL && s->alproto == ALPROTO_HTTP &&
|
|
|
|
|
SigMatchListSMBelongsTo(s, s->mpm_sm) == DETECT_SM_LIST_PMATCH) {
|
|
|
|
|
rule_warning += 1;
|
|
|
|
|
warn_non_alproto_fp_for_alproto_sig = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!rule_warnings_only || (rule_warnings_only && rule_warning > 0)) {
|
|
|
|
|
fprintf(rule_engine_analysis_FD, "== Sid: %u ==\n", s->id);
|
|
|
|
|
@ -481,6 +740,11 @@ void EngineAnalysisRules(Signature *s, char *line)
|
|
|
|
|
if (rule_content || rule_content_http || rule_pcre || rule_pcre_http) {
|
|
|
|
|
fprintf(rule_engine_analysis_FD, " Rule contains %d content options, %d http content options, %d pcre options, and %d pcre options with http modifiers.\n", rule_content, rule_content_http, rule_pcre, rule_pcre_http);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* print fast pattern info */
|
|
|
|
|
EngineAnalysisRulesPrintFP(s);
|
|
|
|
|
|
|
|
|
|
/* this is where the warnings start */
|
|
|
|
|
if (warn_pcre_no_content /*rule_pcre > 0 && rule_content == 0 && rule_content_http == 0*/) {
|
|
|
|
|
fprintf(rule_engine_analysis_FD, " Warning: Rule uses pcre without a content option present.\n"
|
|
|
|
|
" -Consider adding a content to improve performance of this rule.\n");
|
|
|
|
|
@ -548,6 +812,12 @@ void EngineAnalysisRules(Signature *s, char *line)
|
|
|
|
|
"before we can detect the app layer protocol for the "
|
|
|
|
|
"flow.\n", s->alproto);
|
|
|
|
|
}
|
|
|
|
|
if (warn_non_alproto_fp_for_alproto_sig) {
|
|
|
|
|
fprintf(rule_engine_analysis_FD, " Warning: Rule app layer "
|
|
|
|
|
"protocol is http, but the fast_pattern is set on the raw "
|
|
|
|
|
"stream. Consider adding fast_pattern over a http "
|
|
|
|
|
"buffer for increased performance.");
|
|
|
|
|
}
|
|
|
|
|
if (rule_warning == 0) {
|
|
|
|
|
fprintf(rule_engine_analysis_FD, " No warnings for this rule.\n");
|
|
|
|
|
}
|
|
|
|
|
|