dns: detect case of request flooding

In the case where DNS requests are sent over the same flow w/o a
reply being received, we now set an event in the flow and refuse
to add more transactions to the state. This protects the DNS
handling from getting overloaded slowing down everything.

A new option to configure this behaviour was added:

app-layer:
  protocols:
    dnsudp:
       enabled: yes
       detection-ports:
         udp:
           toserver: 53
       request-flood: 750

The request-flood parameter can be 0 (disabling this feature) or a
positive integer. It defaults to 500.

This means that if 500 unreplied requests are seen in a row an event
is set. Rule 2240007 was added to dns-events.rules to match on this.
pull/625/merge
Victor Julien 11 years ago
parent c1b9f0e1f4
commit 61cdd9be6b

@ -9,3 +9,5 @@ alert dns any any -> any any (msg:"SURICATA DNS Not a request"; flow:to_server;
alert dns any any -> any any (msg:"SURICATA DNS Not a response"; flow:to_client; app-layer-event:dns.not_a_response; sid:2240005; rev:1;)
# Z flag (reserved) not 0
alert dns any any -> any any (msg:"SURICATA DNS Z flag set"; app-layer-event:dns.z_flag_set; sid:2240006; rev:1;)
# Request Flood Detected
alert dns any any -> any any (msg:"SURICATA DNS request flood detected"; flow:to_server; app-layer-event:dns.flooded; sid:2240007; rev:1;)

@ -28,12 +28,26 @@
#include "util-print.h"
#endif
typedef struct DNSConfig_ {
uint32_t request_flood;
} DNSConfig;
static DNSConfig dns_config;
void DNSConfigInit(void) {
memset(&dns_config, 0x00, sizeof(dns_config));
}
void DNSConfigSetRequestFlood(uint32_t value) {
dns_config.request_flood = value;
}
SCEnumCharMap dns_decoder_event_table[ ] = {
{ "UNSOLLICITED_RESPONSE", DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE, },
{ "MALFORMED_DATA", DNS_DECODER_EVENT_MALFORMED_DATA, },
{ "NOT_A_REQUEST", DNS_DECODER_EVENT_NOT_A_REQUEST, },
{ "NOT_A_RESPONSE", DNS_DECODER_EVENT_NOT_A_RESPONSE, },
{ "Z_FLAG_SET", DNS_DECODER_EVENT_Z_FLAG_SET, },
{ "FLOODED", DNS_DECODER_EVENT_FLOODED, },
{ NULL, -1 },
};
@ -312,8 +326,21 @@ bad_data:
void DNSStoreQueryInState(DNSState *dns_state, const uint8_t *fqdn, const uint16_t fqdn_len,
const uint16_t type, const uint16_t class, const uint16_t tx_id)
{
if (dns_state->curr != NULL && dns_state->curr->replied == 0)
/* flood protection */
if (dns_state->givenup)
return;
if (dns_state->curr != NULL && dns_state->curr->replied == 0) {
dns_state->curr->reply_lost = 1;
dns_state->unreplied_cnt++;
/* check flood limit */
if (dns_config.request_flood != 0 &&
dns_state->unreplied_cnt > dns_config.request_flood) {
DNSSetEvent(dns_state, DNS_DECODER_EVENT_FLOODED);
dns_state->givenup = 1;
}
}
DNSTransaction *tx = DNSTransactionFindByTxId(dns_state, tx_id);
if (tx == NULL) {
@ -353,7 +380,6 @@ void DNSStoreAnswerInState(DNSState *dns_state, const int rtype, const uint8_t *
TAILQ_INSERT_TAIL(&dns_state->tx_list, tx, next);
dns_state->curr = tx;
tx->tx_num = dns_state->transaction_max;
}
DNSAnswerEntry *q = SCMalloc(sizeof(DNSAnswerEntry) + fqdn_len + data_len);
@ -381,6 +407,9 @@ void DNSStoreAnswerInState(DNSState *dns_state, const int rtype, const uint8_t *
/* mark tx is as replied so we can log it */
tx->replied = 1;
/* reset unreplied counter */
dns_state->unreplied_cnt = 0;
}
/** \internal

@ -56,6 +56,7 @@ enum {
DNS_DECODER_EVENT_NOT_A_REQUEST,
DNS_DECODER_EVENT_NOT_A_RESPONSE,
DNS_DECODER_EVENT_Z_FLAG_SET,
DNS_DECODER_EVENT_FLOODED,
};
/** \brief DNS packet header */
@ -143,7 +144,9 @@ typedef struct DNSState_ {
TAILQ_HEAD(, DNSTransaction_) tx_list; /**< transaction list */
DNSTransaction *curr; /**< ptr to current tx */
uint64_t transaction_max;
uint32_t unreplied_cnt; /**< number of unreplied requests in a row */
uint16_t events;
uint16_t givenup;
/* used by TCP only */
uint16_t offset;
@ -151,6 +154,11 @@ typedef struct DNSState_ {
uint8_t *buffer;
} DNSState;
#define DNS_CONFIG_DEFAULT_REQUEST_FLOOD 500
void DNSConfigInit(void);
void DNSConfigSetRequestFlood(uint32_t value);
void RegisterDNSParsers(void);
void DNSParserTests(void);
void DNSParserRegisterTests(void);

@ -359,11 +359,12 @@ static int DNSReponseParseData(Flow *f, DNSState *dns_state, const uint8_t *inpu
DNSTransaction *tx = NULL;
int found = 0;
TAILQ_FOREACH(tx, &dns_state->tx_list, next) {
if (tx->tx_id == ntohs(dns_header->tx_id)) {
found = 1;
break;
}
if ((tx = DNSTransactionFindByTxId(dns_state, ntohs(dns_header->tx_id))) != NULL)
found = 1;
if (!found) {
SCLogDebug("DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE");
DNSSetEvent(dns_state, DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE);
}
uint16_t q;
@ -439,11 +440,6 @@ static int DNSReponseParseData(Flow *f, DNSState *dns_state, const uint8_t *inpu
}
}
if (!found) {
SCLogDebug("DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE");
DNSSetEvent(dns_state, DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE);
}
SCReturnInt(1);
bad_data:
insufficient_data:

@ -23,6 +23,9 @@
#include "suricata-common.h"
#include "suricata.h"
#include "conf.h"
#include "util-misc.h"
#include "debug.h"
#include "decode.h"
@ -179,12 +182,14 @@ static int DNSUDPResponseParse(Flow *f, void *dstate,
DNSTransaction *tx = NULL;
int found = 0;
TAILQ_FOREACH(tx, &dns_state->tx_list, next) {
if (tx->tx_id == ntohs(dns_header->tx_id)) {
found = 1;
break;
}
if ((tx = DNSTransactionFindByTxId(dns_state, ntohs(dns_header->tx_id))) != NULL)
found = 1;
if (!found) {
SCLogDebug("DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE");
DNSSetEvent(dns_state, DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE);
}
if (DNSValidateResponseHeader(dns_state, dns_header) < 0)
goto bad_data;
@ -269,17 +274,12 @@ static int DNSUDPResponseParse(Flow *f, void *dstate,
if (ntohs(dns_header->flags) & 0x0003) {
SCLogDebug("no such name");
if (dns_state->curr != NULL) {
dns_state->curr->no_such_name = 1;
if (tx != NULL) {
tx->no_such_name = 1;
}
}
if (!found) {
SCLogDebug("DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE");
DNSSetEvent(dns_state, DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE);
}
SCReturnInt(1);
SCReturnInt(1);
bad_data:
insufficient_data:
@ -300,6 +300,21 @@ static uint16_t DNSUdpProbingParser(uint8_t *input, uint32_t ilen, uint32_t *off
return ALPROTO_DNS_UDP;
}
static void DNSUDPConfigure(void) {
uint32_t request_flood = DNS_CONFIG_DEFAULT_REQUEST_FLOOD;
ConfNode *p = ConfGetNode("app-layer.protocols.dnsudp.request-flood");
if (p != NULL) {
uint32_t value;
if (ParseSizeStringU32(p->val, &value) < 0) {
SCLogError(SC_ERR_DNS_CONFIG, "invalid value for request-flood %s", p->val);
} else {
request_flood = value;
}
}
SCLogInfo("DNS request flood protection level: %u", request_flood);
DNSConfigSetRequestFlood(request_flood);
}
void RegisterDNSUDPParsers(void) {
char *proto_name = "dnsudp";
@ -349,6 +364,8 @@ void RegisterDNSUDPParsers(void) {
DNSGetAlstateProgressCompletionStatus);
DNSAppLayerRegisterGetEventInfo(ALPROTO_DNS_UDP);
DNSUDPConfigure();
} else {
SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
"still on.", proto_name);

@ -268,6 +268,7 @@ typedef enum {
SC_WARN_XFF_INVALID_MODE,
SC_WARN_XFF_INVALID_HEADER,
SC_ERR_THRESHOLD_SETUP,
SC_ERR_DNS_CONFIG,
} SCError;
const char *SCErrorToString(SCError);

Loading…
Cancel
Save