diff --git a/src/app-layer-htp.c b/src/app-layer-htp.c index 0663bbbccf..bd13505164 100644 --- a/src/app-layer-htp.c +++ b/src/app-layer-htp.c @@ -5,6 +5,7 @@ * HTP library. * * \author Gurvinder Singh + * \author Pablo Rincon * */ @@ -36,6 +37,7 @@ static SCMutex htp_state_mem_lock = PTHREAD_MUTEX_INITIALIZER; static uint64_t htp_state_memuse = 0; static uint64_t htp_state_memcnt = 0; #endif +extern uint8_t pcre_need_htp_request_body; /** \brief Function to allocates the HTTP state memory and also creates the HTTP * connection parser to be used by the HTP library @@ -58,6 +60,10 @@ static void *HTPStateAlloc(void) } SCLogDebug("s->connp %p", s->connp); + s->body.nchunks = 0; + s->body.operation = HTP_BODY_NONE; + s->body.pcre_flags = HTP_PCRE_NONE; + /* Create a list_array of size 8 to store the incoming requests, the size of 8 has been chosen as half the size of conn->transactions in the HTP lib. As we are storing only requests here not responses!! */ @@ -105,6 +111,11 @@ static void HTPStateFree(void *state) if (s->recent_in_tx != NULL) { list_destroy(s->recent_in_tx); } + + /* free the list of body chunks */ + if (s->body.nchunks > 0) { + HtpBodyFree(&s->body); + } } free(s); @@ -157,6 +168,10 @@ static int HTPHandleRequestData(Flow *f, void *htp_state, HtpState *hstate = (HtpState *)htp_state; + /* Unset the body inspection (the callback should + * reactivate it if necessary) */ + hstate->flags &= ~HTP_NEW_BODY_SET; + /* Open the HTTP connection on receiving the first request */ if (!(hstate->flags & HTP_FLAG_STATE_OPEN)) { SCLogDebug("opening htp handle at %p", hstate->connp); @@ -185,12 +200,14 @@ static int HTPHandleRequestData(Flow *f, void *htp_state, } hstate->flags |= HTP_FLAG_STATE_ERROR; hstate->flags &= ~HTP_FLAG_STATE_DATA; + hstate->flags &= ~HTP_NEW_BODY_SET; ret = -1; } else if (r == STREAM_STATE_DATA) { hstate->flags |= HTP_FLAG_STATE_DATA; } else { hstate->flags &= ~HTP_FLAG_STATE_DATA; + hstate->flags &= ~HTP_NEW_BODY_SET; } /* if we the TCP connection is closed, then close the HTTP connection */ @@ -230,6 +247,10 @@ static int HTPHandleResponseData(Flow *f, void *htp_state, HtpState *hstate = (HtpState *)htp_state; + /* Unset the body inspection (the callback should + * reactivate it if necessary) */ + hstate->flags &= ~HTP_NEW_BODY_SET; + r = htp_connp_res_data(hstate->connp, 0, input, input_len); if (r == STREAM_STATE_ERROR || r == STREAM_STATE_DATA_OTHER) { @@ -247,12 +268,15 @@ static int HTPHandleResponseData(Flow *f, void *htp_state, } } hstate->flags = HTP_FLAG_STATE_ERROR; + hstate->flags &= ~HTP_FLAG_STATE_DATA; + hstate->flags &= ~HTP_NEW_BODY_SET; ret = -1; } else if (r == STREAM_STATE_DATA) { hstate->flags |= HTP_FLAG_STATE_DATA; } else { hstate->flags &= ~HTP_FLAG_STATE_DATA; + hstate->flags &= ~HTP_NEW_BODY_SET; } /* if we the TCP connection is closed, then close the HTTP connection */ @@ -268,6 +292,148 @@ static int HTPHandleResponseData(Flow *f, void *htp_state, SCReturnInt(ret); } +/** + * \brief Append a chunk of body to the HtpBody struct + * \param body pointer to the HtpBody holding the list + * \param data pointer to the data of the chunk + * \param len length of the chunk pointed by data + * \retval none + */ +void HtpBodyAppendChunk(HtpBody *body, uint8_t *data, uint32_t len) +{ + SCEnter(); + BodyChunk *bd = NULL; + if (body->nchunks == 0) { + /* New chunk */ + bd = (BodyChunk *)malloc(sizeof(BodyChunk)); + if (bd == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Fatal error, error allocationg memory"); + exit(EXIT_FAILURE); + } + bd->len = len; + bd->data = data; + body->first = body->last = bd; + body->nchunks++; + bd->next = NULL; + bd->id = body->nchunks; + } else { + /* New or old, we have to check it.. */ + if (body->last->data == data) { + /* Weird, but sometimes htp lib calls the callback + * more than once for the same chunk, with more + * len, so updating the len */ + body->last->len = len; + bd = body->last; + } else { + bd = (BodyChunk *)malloc(sizeof(BodyChunk)); + bd->len = len; + bd->data = data; + body->last->next = bd; + body->last = bd; + body->nchunks++; + bd->next = NULL; + bd->id = body->nchunks; + } + } + SCLogDebug("Body %p; Chunk id: %"PRIu32", data %p, len %"PRIu32"\n", body, + bd->id, bd->data, (uint32_t)bd->len); +} + +/** + * \brief Print the information and chunks of a Body + * \param body pointer to the HtpBody holding the list + * \retval none + */ +void HtpBodyPrint(HtpBody *body) +{ + if (SCLogDebugEnabled()) { + SCEnter(); + + if (body->nchunks == 0) + return; + + BodyChunk *cur = NULL; + SCLogDebug("--- Start body chunks at %p ---", body); + for (cur = body->first; cur != NULL; cur = cur->next) { + SCLogDebug("Body %p; Chunk id: %"PRIu32", data %p, len %"PRIu32"\n", + body, cur->id, cur->data, (uint32_t)cur->len); + PrintRawDataFp(stdout, (uint8_t*)cur->data, cur->len); + } + SCLogDebug("--- End body chunks at %p ---", body); + } +} + +/** + * \brief Free the information holded of the body request + * \param body pointer to the HtpBody holding the list + * \retval none + */ +void HtpBodyFree(HtpBody *body) +{ + SCEnter(); + if (body->nchunks == 0) + return; + + SCLogDebug("Removing chunks of Body %p; Last Chunk id: %"PRIu32", data %p," + " len %"PRIu32"\n", body, body->last->id, body->last->data, + (uint32_t)body->last->len); + body->nchunks = 0; + + BodyChunk *cur = NULL; + BodyChunk *prev = NULL; + prev = body->first; + while (prev != NULL) { + cur = prev->next; + free(prev); + prev = cur; + } + body->first = body->last = NULL; + body->pcre_flags = HTP_PCRE_NONE; + body->operation = HTP_BODY_NONE; +} + +/** + * \brief Function callback to append chunks for Resquests + * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib) + * \retval int HOOK_OK if all goes well + */ +int HTPCallbackRequestBodyData(htp_tx_data_t *d) +{ + SCEnter(); + HtpState *hstate = (HtpState *)d->tx->connp->user_data; + SCLogDebug("New response body data available at %p -> %p -> %p, bodylen " + "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len); + + //PrintRawDataFp(stdout, d->data, d->len); + + /* If it has been inspected by pcre and there's no match, + * remove this chunks */ + if ( !(hstate->body.pcre_flags & HTP_PCRE_HAS_MATCH) && + (hstate->body.pcre_flags & HTP_PCRE_DONE)) + { + HtpBodyFree(&hstate->body); + } + + /* If its a new operation, remove the old data */ + if (hstate->body.operation == HTP_BODY_RESPONSE) { + HtpBodyFree(&hstate->body); + hstate->body.pcre_flags = HTP_PCRE_NONE; + } + hstate->body.operation = HTP_BODY_REQUEST; + + + HtpBodyAppendChunk(&hstate->body, (uint8_t*)d->data, (uint32_t)d->len); + hstate->body.pcre_flags = HTP_PCRE_NONE; + if (SCLogDebugEnabled()) { + HtpBodyPrint(&hstate->body); + } + + /* set the new chunk flag */ + hstate->flags |= HTP_NEW_BODY_SET; + + SCReturnInt(HOOK_OK); +} + /** * \brief Print the stats of the HTTP requests */ @@ -321,6 +487,12 @@ static int HTPCallbackResponse(htp_connp_t *connp) { SCReturnInt(0); } + /* Free data when we have a response */ + if (hstate->body.nchunks > 0) + HtpBodyFree(&hstate->body); + hstate->body.operation = HTP_BODY_RESPONSE; + hstate->body.pcre_flags = HTP_PCRE_NONE; + while (list_size(hstate->recent_in_tx) > 0) { htp_tx_t *tx = list_pop(hstate->recent_in_tx); if (tx != NULL) { @@ -337,7 +509,6 @@ static int HTPCallbackResponse(htp_connp_t *connp) { */ void RegisterHTPParsers(void) { - AppLayerRegisterStateFuncs(ALPROTO_HTTP, HTPStateAlloc, HTPStateFree); AppLayerRegisterProto("http", ALPROTO_HTTP, STREAM_TOSERVER, @@ -356,6 +527,21 @@ void RegisterHTPParsers(void) htp_config_set_generate_request_uri_normalized(cfg, 1); } +/** + * \brief This function is called at the end of SigLoadSignatures + * pcre_need_htp_request_body is a flag that indicates if we need + * to inspect the body of requests from a pcre keyword. + */ +void AppLayerHtpRegisterExtraCallbacks(void) { + SCLogDebug("Registering extra htp callbacks"); + if (pcre_need_htp_request_body == 1) { + SCLogDebug("Registering callback htp_config_register_request_body_data on htp"); + htp_config_register_request_body_data(cfg, HTPCallbackRequestBodyData); + } else { + SCLogDebug("No htp extra callback needed"); + } +} + #ifdef UNITTESTS /** \test Test case where chunks are sent in smaller chunks and check the diff --git a/src/app-layer-htp.h b/src/app-layer-htp.h index 6618a7700b..17d3403f2b 100644 --- a/src/app-layer-htp.h +++ b/src/app-layer-htp.h @@ -1,6 +1,10 @@ -/* +/** + * Copyright (c) 2009 Open Information Security Foundation + * * \file: app-layer-htp.h + * * \author Gurvinder Singh + * \author Pablo Rincon * * Created on November 14, 2009, 12:48 AM */ @@ -10,21 +14,64 @@ #include -#define HTP_FLAG_STATE_OPEN 0x01 /**< Flag to indicate that HTTP - connection is open */ -#define HTP_FLAG_STATE_CLOSED 0x02 /**< Flag to indicate that HTTP - connection is closed */ -#define HTP_FLAG_STATE_DATA 0x04 /**< Flag to indicate that HTTP - connection needs more data */ -#define HTP_FLAG_STATE_ERROR 0x08 /**< Flag to indicate that an error - has been occured on HTTP - connection */ +#define HTP_FLAG_STATE_OPEN 0x01 /**< Flag to indicate that HTTP + connection is open */ +#define HTP_FLAG_STATE_CLOSED 0x02 /**< Flag to indicate that HTTP + connection is closed */ +#define HTP_FLAG_STATE_DATA 0x04 /**< Flag to indicate that HTTP + connection needs more data */ +#define HTP_FLAG_STATE_ERROR 0x08 /**< Flag to indicate that an error + has been occured on HTTP + connection */ + +#define HTP_NEW_BODY_SET 0x10 /**< Flag to indicate that HTTP + has parsed a new body (for + pcre) */ + + +enum { + HTP_BODY_NONE, /**< Flag to indicate the current + operation */ + HTP_BODY_REQUEST, /**< Flag to indicate that the + current operation is a request */ + HTP_BODY_RESPONSE /**< Flag to indicate that the current + * operation is a response */ +}; + +#define HTP_PCRE_NONE 0x00 /**< No pcre executed yet */ +#define HTP_PCRE_DONE 0x01 /**< Flag to indicate that pcre has + done some inspection in the + chunks */ +#define HTP_PCRE_HAS_MATCH 0x02 /**< Flag to indicate that the chunks + matched on some rule */ + +/** Struct used to hold chunks of a body on a request */ +typedef struct BodyChunk_ { + uint8_t *data; /**< Pointer to the data of the chunk */ + uint32_t len; /**< Length of the chunk */ + struct BodyChunk_ *next; /**< Pointer to the next chunk */ + uint32_t id; /**< number of chunk of the current body */ +} BodyChunk; + +/** Struct used to hold all the chunks of a body on a request */ +typedef struct Body_ { + BodyChunk *first; /**< Pointer to the first chunk */ + BodyChunk *last; /**< Pointer to the last chunk */ + uint32_t nchunks; /**< Number of chunks in the current operation */ + uint8_t operation; /**< This flag indicate if it's a request + or a response */ + uint8_t pcre_flags; /**< This flag indicate if no chunk matched + any pcre (so we can free() without waiting) */ +} HtpBody; typedef struct HtpState_ { - htp_connp_t *connp; /**< Connection parser structure for each connection */ + htp_connp_t *connp; /**< Connection parser structure for + each connection */ uint8_t flags; - list_t *recent_in_tx; /**< Point to the new received HTTP request */ + list_t *recent_in_tx; /**< Point to the new received HTTP request */ + HtpBody body; /**< Body of the request (if any) */ + } HtpState; htp_cfg_t *cfg; /**< Config structure for HTP library */ @@ -34,5 +81,12 @@ void HTPParserRegisterTests(void); void HTPAtExitPrintStats(void); void HTPFreeConfig(void); +htp_tx_t *HTPTransactionMain(const HtpState *); + +int HTPCallbackRequestBodyData(htp_tx_data_t *); +void HtpBodyPrint(HtpBody *); +void HtpBodyFree(HtpBody *); +void AppLayerHtpRegisterExtraCallbacks(void); + #endif /* __APP_LAYER_HTP_H__ */ diff --git a/src/detect-pcre.c b/src/detect-pcre.c index f08783bf58..819169b4b6 100644 --- a/src/detect-pcre.c +++ b/src/detect-pcre.c @@ -7,6 +7,7 @@ #include "pkt-var.h" #include "flow-var.h" +#include "flow-alert-sid.h" #include "detect-pcre.h" @@ -17,7 +18,22 @@ #include "util-var-name.h" #include "util-debug.h" #include "util-unittest.h" +#include "util-print.h" +#include "util-pool.h" + #include "conf.h" +#include "app-layer-htp.h" +#include "stream.h" +#include "stream-tcp.h" +#include "stream-tcp-private.h" +#include "stream-tcp-reassemble.h" +#include "app-layer-protos.h" +#include "app-layer-parser.h" +#include "app-layer-htp.h" + +#include +#include "stream.h" + #define PARSE_CAPTURE_REGEX "\\(\\?P\\<([A-z]+)\\_([A-z0-9_]+)\\>" #define PARSE_REGEX "(?ctx; + if ( !(pe->flags & DETECT_PCRE_HTTP_BODY_AL)) + SCReturnInt(0); + + SCMutexLock(&f->m); + + /** If enabled http body inspection + * TODO: Add more HTTP options here if needed + */ + HtpState *htp_state = (HtpState *)state; + if (htp_state == NULL) { + SCLogDebug("No htp state, no match at http body data"); + goto unlock; + } else if (htp_state->body.nchunks == 0) { + SCLogDebug("No body data to inspect"); + goto unlock; + } else { + pcreret = 0; + int wspace[255]; + int flags = PCRE_PARTIAL; + + BodyChunk *cur = htp_state->body.first; + if (cur == NULL) { + SCLogDebug("No body chunks to inspect"); + goto unlock; + } + htp_state->body.pcre_flags |= HTP_PCRE_DONE; + + while (cur != NULL) { + if (SCLogDebugEnabled()) { + printf("\n"); + PrintRawUriFp(stdout, (uint8_t*)cur->data, cur->len); + printf("\n"); + } + pcreret = pcre_dfa_exec(pe->re, NULL, (char*)cur->data, cur->len, 0, + flags|PCRE_DFA_SHORTEST, ov, MAX_SUBSTRINGS, + wspace, MAX_SUBSTRINGS); + cur = cur->next; + + SCLogDebug("Pcre Ret %d", pcreret); + switch (pcreret) { + case PCRE_ERROR_PARTIAL: + /* make pcre to use the working space of the last partial + * match, (match over multiple chunks) + */ + SCLogDebug("partial match"); + flags |= PCRE_DFA_RESTART; + htp_state->body.pcre_flags |= HTP_PCRE_HAS_MATCH; + break; + case PCRE_ERROR_NOMATCH: + SCLogDebug("no match"); + flags = PCRE_PARTIAL; + break; + case 0: + SCLogDebug("Perfect Match!"); + ret = 1; + goto unlock; + break; + default: + if (pcreret > 0) { + SCLogDebug("Match with captured data"); + ret = 1; + } else { + SCLogDebug("No match, pcre failed"); + ret = 0; + } + goto unlock; + } + } + } + +unlock: + SCMutexUnlock(&f->m); + SCReturnInt(ret ^ pe->negate); +} + +/** + * \brief DetectPcreMatch will try to match a regex on a single packet; + * DetectPcreALMatch is used if we parse the option 'P' + * + * \param t pointer to the threadvars structure + * \param t pointer to the threadvars structure + * + * \retval 1: match ; 0 No Match; -1: error + */ +int DetectPcreMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, + Signature *s, SigMatch *m) +{ + SCEnter(); #define MAX_SUBSTRINGS 30 int ret = 0; int ov[MAX_SUBSTRINGS]; @@ -125,6 +257,11 @@ int DetectPcreMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, S SCReturnInt(0); DetectPcreData *pe = (DetectPcreData *)m->ctx; + + /* If we want to inspect the http body, we will use HTP L7 parser */ + if (pe->flags & DETECT_PCRE_HTTP_BODY_AL) + SCReturnInt(0); + if (s->flags & SIG_FLAG_RECURSIVE) { ptr = det_ctx->pkt_ptr ? det_ctx->pkt_ptr : p->payload; len = p->payload_len - det_ctx->pkt_off; @@ -325,6 +462,10 @@ DetectPcreData *DetectPcreParse (char *regexstr) case 'O': pd->flags |= DETECT_PCRE_MATCH_LIMIT; break; + case 'P': + /* snort's option (http body inspeciton, chunks loaded from HTP) */ + pd->flags |= DETECT_PCRE_HTTP_BODY_AL; + break; default: SCLogError(SC_ERR_UNKNOWN_REGEX_MOD, "unknown regex modifier '%c'", *op); goto error; @@ -422,6 +563,7 @@ DetectPcreData *DetectPcreParseCapture(char *regexstr, DetectEngineCtx *de_ctx, if (capture_str_ptr != NULL) { pd->capname = strdup((char *)capture_str_ptr); } + if (type_str_ptr != NULL) { if (strcmp(type_str_ptr,"pkt") == 0) { pd->flags |= DETECT_PCRE_CAPTURE_PKT; @@ -466,6 +608,14 @@ int DetectPcreSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, char *r sm->type = DETECT_PCRE; sm->ctx = (void *)pd; + if (pd->flags & DETECT_PCRE_HTTP_BODY_AL) { + sm->type = DETECT_PCRE_HTTPBODY; + + SCLogDebug("Body inspection modifier set"); + s->flags |= SIG_FLAG_APPLAYER; + pcre_need_htp_request_body = 1; + } + SigMatchAppend(s,m,sm); return 0; @@ -643,7 +793,9 @@ static int DetectPcreTestSig01Real(int mpm_type) { ThreadVars th_v; DetectEngineThreadCtx *det_ctx; int result = 0; + Flow f; + memset(&f, 0, sizeof(f)); memset(&th_v, 0, sizeof(th_v)); memset(&p, 0, sizeof(p)); p.src.family = AF_INET; @@ -651,6 +803,7 @@ static int DetectPcreTestSig01Real(int mpm_type) { p.payload = buf; p.payload_len = buflen; p.proto = IPPROTO_TCP; + p.flow = &f; DetectEngineCtx *de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { @@ -670,8 +823,9 @@ static int DetectPcreTestSig01Real(int mpm_type) { DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); - if (PacketAlertCheck(&p, 1)) + if (PacketAlertCheck(&p, 1) == 1) { result = 1; + } SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); @@ -692,12 +846,15 @@ static int DetectPcreTestSig02Real(int mpm_type) { "\r\n\r\n"; uint16_t buflen = strlen((char *)buf); Packet p; + Flow f; ThreadVars th_v; DetectEngineThreadCtx *det_ctx; int result = 0; memset(&th_v, 0, sizeof(th_v)); memset(&p, 0, sizeof(p)); + memset(&f, 0, sizeof(f)); + p.flow = &f; p.src.family = AF_INET; p.dst.family = AF_INET; p.payload = buf; @@ -725,8 +882,9 @@ static int DetectPcreTestSig02Real(int mpm_type) { DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); - if (PacketAlertCheck(&p, 2)) + if (PacketAlertCheck(&p, 2) == 1) { result = 1; + } SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); @@ -823,6 +981,275 @@ static int DetectPcreTestSig03B3g (void) { static int DetectPcreTestSig03Wm (void) { return DetectPcreTestSig03Real(MPM_WUMANBER); } + +/** + * \test Check the signature with pcre modifier P (match with L7 to http body data) + */ +static int DetectPcreModifPTest04(void) { + int result = 0; + uint8_t httpbuf1[] = + "GET / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n" + "User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.13) Gecko/2009080315 Ubuntu/8.10 (intrepid) Firefox/3.0.13\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8\r\n" + "Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3\r\n" + "Accept-Encoding: gzip,deflate\r\n" + "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" + "Date: Tue, 22 Sep 2009 19:24:48 GMT\r\n" + "Server: Apache\r\n" + "X-Powered-By: PHP/5.2.5\r\n" + "P3P: CP=\"NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM\"\r\n" + "Expires: Mon, 1 Jan 2001 00:00:00 GMT\r\n" + "Last-Modified: Tue, 22 Sep 2009 19:24:48 GMT\r\n" + "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n" + "Pragma: no-cache\r\n" + "Keep-Alive: timeout=15, max=100\r\n" + "Connection: Keep-Alive\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "\r\n" + "88b7\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n\r\n"; + + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + TcpSession ssn; + Packet p; + Flow f; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + memset(&th_v, 0, sizeof(th_v)); + memset(&p, 0, sizeof(p)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + p.src.family = AF_INET; + p.dst.family = AF_INET; + p.payload = NULL; + p.payload_len = 0; + p.proto = IPPROTO_TCP; + + f.protoctx = (void *)&ssn; + p.flow = &f; + p.flowflags |= FLOW_PKT_TOSERVER; + ssn.alproto = ALPROTO_HTTP; + + StreamTcpInitConfig(TRUE); + StreamL7DataPtrInit(&ssn); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + + de_ctx->flags |= DE_QUIET; + + s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:" + "\"Pcre modifier P\"; pcre:\"/DOCTYPE/P\"; " + "sid:1;)"); + if (s == NULL) { + goto end; + } + + s->next = SigInit(de_ctx,"alert http any any -> any any (msg:\"" + "Pcre modifier P (no match)\"; pcre:\"/blah/P\"; sid:2;)"); + if (s->next == NULL) { + goto end; + } + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + HtpState *http_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (http_state == NULL) { + printf("no http state: "); + result = 0; + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); + + if (!(PacketAlertCheck(&p, 1))) { + printf("sid 1 didn't match but should have: "); + goto end; + } + if (PacketAlertCheck(&p, 2)) { + printf("sid 2 matched but shouldn't: "); + goto end; + } + + result = 1; +end: + if (de_ctx != NULL) SigGroupCleanup(de_ctx); + if (de_ctx != NULL) SigCleanSignatures(de_ctx); + if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); + + StreamL7DataPtrFree(&ssn); + StreamTcpFreeConfig(TRUE); + return result; +} + +/** + * \test Check the signature with pcre modifier P (match with L7 to http body data) + * over fragmented chunks (DOCTYPE fragmented) + */ +static int DetectPcreModifPTest05(void) { + int result = 0; + uint8_t httpbuf1[] = + "GET / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n" + "User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.13) Gecko/2009080315 Ubuntu/8.10 (intrepid) Firefox/3.0.13\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8\r\n" + "Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3\r\n" + "Accept-Encoding: gzip,deflate\r\n" + "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" + "Date: Tue, 22 Sep 2009 19:24:48 GMT\r\n" + "Server: Apache\r\n" + "X-Powered-By: PHP/5.2.5\r\n" + "P3P: CP=\"NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM\"\r\n" + "Expires: Mon, 1 Jan 2001 00:00:00 GMT\r\n" + "Last-Modified: Tue, 22 Sep 2009 19:24:48 GMT\r\n" + "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n" + "Pragma: no-cache\r\n" + "Keep-Alive: timeout=15, max=100\r\n" + "Connection: Keep-Alive\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "\r\n" + "88b7\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n\r\n"; + + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Packet p1; + Packet p2; + Flow f; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + memset(&th_v, 0, sizeof(th_v)); + memset(&p1, 0, sizeof(Packet)); + memset(&p2, 0, sizeof(Packet)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + p1.src.family = AF_INET; + p1.dst.family = AF_INET; + p1.payload = NULL; + p1.payload_len = 0; + p1.proto = IPPROTO_TCP; + + p2.src.family = AF_INET; + p2.dst.family = AF_INET; + p2.payload = NULL; + p2.payload_len = 0; + p2.proto = IPPROTO_TCP; + + f.protoctx = (void *)&ssn; + p1.flow = &f; + p1.flowflags |= FLOW_PKT_TOSERVER; + p2.flow = &f; + p2.flowflags |= FLOW_PKT_TOSERVER; + ssn.alproto = ALPROTO_HTTP; + + StreamTcpInitConfig(TRUE); + StreamL7DataPtrInit(&ssn); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + + de_ctx->flags |= DE_QUIET; + + s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:" + "\"Pcre modifier P\"; pcre:\"/DOC/P\"; " + "sid:1;)"); + if (s == NULL) { + goto end; + } + + s->next = SigInit(de_ctx,"alert http any any -> any any (msg:\"" + "Pcre modifier P (no match)\"; pcre:\"/DOCTYPE/P\"; sid:2;)"); + if (s->next == NULL) { + goto end; + } + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + HtpState *http_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (http_state == NULL) { + printf("no http state: "); + result = 0; + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p1); + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p2); + + if ( !(PacketAlertCheck(&p1, 1))) { + printf("sid 1 didn't match but should have"); + goto end; + } + + if ( !(PacketAlertCheck(&p1, 2))) { + printf("sid 2 didn't match but should have"); + /* It's a partial match over 2 chunks*/ + goto end; + } + + if ( !(PacketAlertCheck(&p2, 2))) { + printf("sid 2 didn't match but should have"); + /* It's a partial match over 2 chunks*/ + goto end; + } + + result = 1; +end: + if (de_ctx != NULL) SigGroupCleanup(de_ctx); + if (de_ctx != NULL) SigCleanSignatures(de_ctx); + if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); + + StreamL7DataPtrFree(&ssn); + StreamTcpFreeConfig(TRUE); + return result; +} + #endif /* UNITTESTS */ /** @@ -847,6 +1274,8 @@ void DetectPcreRegisterTests(void) { UtRegisterTest("DetectPcreTestSig03B2g -- negated pcre test", DetectPcreTestSig03B2g, 1); UtRegisterTest("DetectPcreTestSig03B3g -- negated pcre test", DetectPcreTestSig03B3g, 1); UtRegisterTest("DetectPcreTestSig03Wm -- negated pcre test", DetectPcreTestSig03Wm, 1); + UtRegisterTest("DetectPcreModifPTest04 -- Modifier P", DetectPcreModifPTest04, 1); + UtRegisterTest("DetectPcreModifPTest05 -- Modifier P fragmented", DetectPcreModifPTest05, 1); #endif /* UNITTESTS */ } diff --git a/src/detect-pcre.h b/src/detect-pcre.h index 259f58d8a5..b3cc26f5ac 100644 --- a/src/detect-pcre.h +++ b/src/detect-pcre.h @@ -15,6 +15,8 @@ #define DETECT_PCRE_CAPTURE_FLOW 0x0100 #define DETECT_PCRE_MATCH_LIMIT 0x0200 +#define DETECT_PCRE_HTTP_BODY_AL 0x0400 + typedef struct DetectPcreData_ { /* pcre options */ pcre *re; diff --git a/src/detect.c b/src/detect.c index 1774dc0c7e..faba6f32a2 100644 --- a/src/detect.c +++ b/src/detect.c @@ -80,6 +80,7 @@ #include "util-rule-vars.h" #include "app-layer.h" +#include "app-layer-htp.h" #include "detect-tls-version.h" #include "action-globals.h" @@ -756,6 +757,7 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh if (sigmatch_table[sm->type].AppLayerMatch != NULL && alproto == sigmatch_table[sm->type].alproto && alstate != NULL) { + SCLogDebug("App layer match function has been invoked"); match = sigmatch_table[sm->type].AppLayerMatch(th_v, det_ctx, p->flow, flags, alstate, s, sm); } else if (sigmatch_table[sm->type].Match != NULL) { match = sigmatch_table[sm->type].Match(th_v, det_ctx, p, s, sm); @@ -3449,10 +3451,11 @@ static int SigTest08Real (int mpm_type) { TcpSession ssn; int result = 0; + memset(&f, 0, sizeof(Flow)); memset(&th_v, 0, sizeof(th_v)); memset(&p, 0, sizeof(p)); - memset(&f, 0, sizeof(f)); memset(&ssn, 0, sizeof(ssn)); + p.src.family = AF_INET; p.dst.family = AF_INET; p.payload = buf; @@ -3461,6 +3464,7 @@ static int SigTest08Real (int mpm_type) { f.protoctx = (void *)&ssn; p.flow = &f; p.flowflags |= FLOW_PKT_TOSERVER; + //FlowInit(&f, &p); ssn.alproto = ALPROTO_HTTP; StreamTcpInitConfig(TRUE); @@ -3497,7 +3501,7 @@ static int SigTest08Real (int mpm_type) { } SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); - if (PacketAlertCheck(&p, 1) && PacketAlertCheck(&p, 2)) + if ( (PacketAlertCheck(&p, 1) || FlowAlertSidIsset(&f, 1)) && PacketAlertCheck(&p, 2)) result = 1; else printf("sid:1 %s, sid:2 %s: ", @@ -3716,6 +3720,9 @@ static int SigTest11Real (int mpm_type) { memset(&p, 0, sizeof(p)); memset(&f, 0, sizeof(f)); memset(&ssn, 0, sizeof(ssn)); + p.flow = &f; + FlowInit(&f, &p); + p.src.family = AF_INET; p.dst.family = AF_INET; p.payload = buf; @@ -3787,6 +3794,10 @@ static int SigTest12Real (int mpm_type) { memset(&th_v, 0, sizeof(th_v)); memset(&p, 0, sizeof(p)); + Flow f; + memset(&f, 0, sizeof(Flow)); + p.flow = &f; + FlowInit(&f, &p); p.src.family = AF_INET; p.dst.family = AF_INET; p.payload = buf; @@ -3847,6 +3858,10 @@ static int SigTest13Real (int mpm_type) { memset(&th_v, 0, sizeof(th_v)); memset(&p, 0, sizeof(p)); + Flow f; + memset(&f, 0, sizeof(Flow)); + p.flow = &f; + FlowInit(&f, &p); p.src.family = AF_INET; p.dst.family = AF_INET; p.payload = buf; diff --git a/src/suricata.c b/src/suricata.c index 6bb7846c3f..86799d7eb7 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -115,6 +115,7 @@ static uint8_t sigflags = 0; /* Run mode selected */ int run_mode = MODE_UNKNOWN; +extern uint8_t pcre_need_htp_request_body; /* Maximum packets to simultaneously process. */ intmax_t max_pending_packets; @@ -220,7 +221,6 @@ void GlobalInits() memset(&packet_q,0,sizeof(packet_q)); SCMutexInit(&packet_q.mutex_q, NULL); SCCondInit(&packet_q.cond_q, NULL); - } /* \todo dtv not used. */ @@ -663,6 +663,10 @@ int main(int argc, char **argv) regex_arg = ".*"; UtRunSelftest(regex_arg); /* inits and cleans up again */ } + + pcre_need_htp_request_body = 1; + AppLayerHtpRegisterExtraCallbacks(); + UtInitialize(); UTHRegisterTests(); SCReputationRegisterTests(); @@ -763,6 +767,7 @@ int main(int argc, char **argv) if (de_ctx->failure_fatal) exit(EXIT_FAILURE); } + AppLayerHtpRegisterExtraCallbacks(); struct timeval start_time; memset(&start_time, 0, sizeof(start_time));