pcre P modifier support (pcre match over http body requests)

remotes/origin/master-1.0.x
Pablo Rincon 15 years ago committed by Victor Julien
parent ba6d807a6e
commit 0165b3f0d8

@ -5,6 +5,7 @@
* HTP library.
*
* \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
* \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
*
*/
@ -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

@ -1,6 +1,10 @@
/*
/**
* Copyright (c) 2009 Open Information Security Foundation
*
* \file: app-layer-htp.h
*
* \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
* \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
*
* Created on November 14, 2009, 12:48 AM
*/
@ -10,21 +14,64 @@
#include <htp/htp.h>
#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__ */

@ -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 <htp/htp.h>
#include "stream.h"
#define PARSE_CAPTURE_REGEX "\\(\\?P\\<([A-z]+)\\_([A-z0-9_]+)\\>"
#define PARSE_REGEX "(?<!\\\\)/(.*)(?<!\\\\)/([^\"]*)"
@ -35,7 +51,10 @@ static pcre_extra *parse_regex_study;
static pcre *parse_capture_regex;
static pcre_extra *parse_capture_regex_study;
uint8_t pcre_need_htp_request_body = 0;
int DetectPcreMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, SigMatch *);
int DetectPcreALMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m);
int DetectPcreSetup (DetectEngineCtx *, Signature *, SigMatch *, char *);
void DetectPcreFree(void *);
void DetectPcreRegisterTests(void);
@ -43,12 +62,27 @@ void DetectPcreRegisterTests(void);
void DetectPcreRegister (void) {
sigmatch_table[DETECT_PCRE].name = "pcre";
sigmatch_table[DETECT_PCRE].Match = DetectPcreMatch;
sigmatch_table[DETECT_PCRE].AppLayerMatch = NULL;
sigmatch_table[DETECT_PCRE].alproto = ALPROTO_HTTP;
sigmatch_table[DETECT_PCRE].Setup = DetectPcreSetup;
sigmatch_table[DETECT_PCRE].Free = DetectPcreFree;
sigmatch_table[DETECT_PCRE].RegisterTests = DetectPcreRegisterTests;
sigmatch_table[DETECT_PCRE].flags |= SIGMATCH_PAYLOAD;
/* register a separate sm type for the httpbody stuff
* because then we don't need to figure out if we need
* the match or AppLayerMatch function in Detect */
sigmatch_table[DETECT_PCRE_HTTPBODY].name = "__pcre_http_body__"; /* not a real keyword */
sigmatch_table[DETECT_PCRE_HTTPBODY].Match = NULL;
sigmatch_table[DETECT_PCRE_HTTPBODY].AppLayerMatch = DetectPcreALMatch;
sigmatch_table[DETECT_PCRE_HTTPBODY].alproto = ALPROTO_HTTP;
sigmatch_table[DETECT_PCRE_HTTPBODY].Setup = NULL;
sigmatch_table[DETECT_PCRE_HTTPBODY].Free = DetectPcreFree;
sigmatch_table[DETECT_PCRE_HTTPBODY].RegisterTests = NULL;
sigmatch_table[DETECT_PCRE_HTTPBODY].flags |= SIGMATCH_PAYLOAD;
const char *eb;
int eo;
int opts = 0;
@ -105,16 +139,114 @@ error:
return;
}
/*
* returns 0: no match
* 1: match
* -1: error
/**
* \brief match the specified pcre at http body, requesting it from htp/L7
*
* \param t pointer to thread vars
* \param det_ctx pointer to the pattern matcher thread
* \param p pointer to the current packet
* \param m pointer to the sigmatch that we will cast into DetectPcreData
*
* \retval int 0 no match; 1 match
*/
int DetectPcreMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, SigMatch *m)
int DetectPcreALMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f,
uint8_t flags, void *state, Signature *s, SigMatch *m)
{
#define MAX_SUBSTRINGS 30
SCEnter();
int ret = 0;
int pcreret = 0;
int ov[MAX_SUBSTRINGS];
DetectPcreData *pe = (DetectPcreData *)m->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"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n"
"\r\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en-gb\" lang=\"en-gb\">\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"
"<!DOC";
uint8_t httpbuf2[] = "TYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n"
"\r\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en-gb\" lang=\"en-gb\">\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 */
}

@ -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;

@ -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;

@ -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));

Loading…
Cancel
Save