DNS: Add support for SPF response records

SPF response records are identical to TXT records in structure, and
can be parsed and interpreted the same way. However, they have a
different RR type, so they would generate weird events and not be
parsed by Zeek before this change.

Even though they're the same as TXT records from a protocol stance, I
created a new event type (dns_SPF_reply), and call the records out as
SPF in the logs, instead of as TXT records, since the distinction
could be important for detection purposes.

SPF records have been obsoleted, but continue to be seen in the wild.
This commit is contained in:
Vlad Grigorescu 2019-06-14 10:18:37 -05:00
parent fcceba5ece
commit 5f0023b3b0
7 changed files with 112 additions and 19 deletions

View file

@ -456,6 +456,21 @@ event dns_TXT_reply(c: connection, msg: dns_msg, ans: dns_answer, strs: string_v
hook DNS::do_reply(c, msg, ans, txt_strings);
}
event dns_SPF_reply(c: connection, msg: dns_msg, ans: dns_answer, strs: string_vec) &priority=5
{
local spf_strings: string = "";
for ( i in strs )
{
if ( i > 0 )
spf_strings += " ";
spf_strings += fmt("SPF %d %s", |strs[i]|, strs[i]);
}
hook DNS::do_reply(c, msg, ans, spf_strings);
}
event dns_AAAA_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) &priority=5
{
hook DNS::do_reply(c, msg, ans, fmt("%s", a));

View file

@ -281,6 +281,10 @@ int DNS_Interpreter::ParseAnswer(DNS_MsgInfo* msg,
status = ParseRR_TXT(msg, data, len, rdlength, msg_start);
break;
case TYPE_SPF:
status = ParseRR_SPF(msg, data, len, rdlength, msg_start);
break;
case TYPE_CAA:
status = ParseRR_CAA(msg, data, len, rdlength, msg_start);
break;
@ -1321,6 +1325,36 @@ int DNS_Interpreter::ParseRR_TXT(DNS_MsgInfo* msg,
return rdlength == 0;
}
int DNS_Interpreter::ParseRR_SPF(DNS_MsgInfo* msg,
const u_char*& data, int& len, int rdlength,
const u_char* msg_start)
{
if ( ! dns_SPF_reply || msg->skip_event )
{
data += rdlength;
len -= rdlength;
return 1;
}
VectorVal* char_strings = new VectorVal(string_vec);
StringVal* char_string;
while ( (char_string = extract_char_string(analyzer, data, len, rdlength)) )
char_strings->Assign(char_strings->Size(), char_string);
if ( dns_SPF_reply )
analyzer->ConnectionEventFast(dns_SPF_reply, {
analyzer->BuildConnVal(),
msg->BuildHdrVal(),
msg->BuildAnswerVal(),
char_strings,
});
else
Unref(char_strings);
return rdlength == 0;
}
int DNS_Interpreter::ParseRR_CAA(DNS_MsgInfo* msg,
const u_char*& data, int& len, int rdlength,
const u_char* msg_start)

View file

@ -63,6 +63,8 @@ typedef enum {
TYPE_DNSKEY = 48, ///< DNS Key record (RFC 4034)
TYPE_DS = 43, ///< Delegation signer (RFC 4034)
TYPE_NSEC3 = 50,
// Obsoleted
TYPE_SPF = 99, ///< Alternative: storing SPF data in TXT records, using the same format (RFC 4408). Support for it was discontinued in RFC 7208
// The following are only valid in queries.
TYPE_AXFR = 252,
TYPE_ALL = 255,
@ -282,6 +284,9 @@ protected:
int ParseRR_TXT(DNS_MsgInfo* msg,
const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
int ParseRR_SPF(DNS_MsgInfo* msg,
const u_char*& data, int& len, int rdlength,
const u_char* msg_start);
int ParseRR_CAA(DNS_MsgInfo* msg,
const u_char*& data, int& len, int rdlength,
const u_char* msg_start);

View file

@ -15,7 +15,7 @@
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end
## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_query_reply dns_rejected
## dns_request non_dns_request dns_max_queries dns_session_timeout dns_skip_addl
@ -42,7 +42,7 @@ event dns_message%(c: connection, is_orig: bool, msg: dns_msg, len: count%);
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end
## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_rejected non_dns_request dns_max_queries dns_session_timeout dns_skip_addl
@ -71,7 +71,7 @@ event dns_request%(c: connection, msg: dns_msg, query: string, qtype: count, qcl
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end
## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_request non_dns_request dns_max_queries dns_session_timeout dns_skip_addl
@ -97,7 +97,7 @@ event dns_rejected%(c: connection, msg: dns_msg, query: string, qtype: count, qc
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end
## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_rejected
## dns_request non_dns_request dns_max_queries dns_session_timeout dns_skip_addl
@ -123,7 +123,7 @@ event dns_query_reply%(c: connection, msg: dns_msg, query: string,
##
## .. zeek:see:: dns_AAAA_reply dns_A6_reply dns_CNAME_reply dns_EDNS_addl dns_HINFO_reply
## dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply dns_SRV_reply
## dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end dns_full_request
## dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request
## dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout
@ -148,7 +148,7 @@ event dns_A_reply%(c: connection, msg: dns_msg, ans: dns_answer, a: addr%);
##
## .. zeek:see:: dns_A_reply dns_A6_reply dns_CNAME_reply dns_EDNS_addl dns_HINFO_reply dns_MX_reply
## dns_NS_reply dns_PTR_reply dns_SOA_reply dns_SRV_reply dns_TSIG_addl
## dns_TXT_reply dns_WKS_reply dns_end dns_full_request dns_mapping_altered
## dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request dns_mapping_altered
## dns_mapping_lost_name dns_mapping_new_name dns_mapping_unverified
## dns_mapping_valid dns_message dns_query_reply dns_rejected dns_request
## non_dns_request dns_max_queries dns_session_timeout dns_skip_addl
@ -173,7 +173,7 @@ event dns_AAAA_reply%(c: connection, msg: dns_msg, ans: dns_answer, a: addr%);
##
## .. zeek:see:: dns_A_reply dns_AAAA_reply dns_CNAME_reply dns_EDNS_addl dns_HINFO_reply dns_MX_reply
## dns_NS_reply dns_PTR_reply dns_SOA_reply dns_SRV_reply dns_TSIG_addl
## dns_TXT_reply dns_WKS_reply dns_end dns_full_request dns_mapping_altered
## dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request dns_mapping_altered
## dns_mapping_lost_name dns_mapping_new_name dns_mapping_unverified
## dns_mapping_valid dns_message dns_query_reply dns_rejected dns_request
## non_dns_request dns_max_queries dns_session_timeout dns_skip_addl
@ -198,7 +198,7 @@ event dns_A6_reply%(c: connection, msg: dns_msg, ans: dns_answer, a: addr%);
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_PTR_reply dns_SOA_reply dns_SRV_reply
## dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end dns_full_request
## dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request
## dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout
@ -223,7 +223,7 @@ event dns_NS_reply%(c: connection, msg: dns_msg, ans: dns_answer, name: string%)
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_EDNS_addl dns_HINFO_reply dns_MX_reply
## dns_NS_reply dns_PTR_reply dns_SOA_reply dns_SRV_reply dns_TSIG_addl
## dns_TXT_reply dns_WKS_reply dns_end dns_full_request dns_mapping_altered
## dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request dns_mapping_altered
## dns_mapping_lost_name dns_mapping_new_name dns_mapping_unverified
## dns_mapping_valid dns_message dns_query_reply dns_rejected dns_request
## non_dns_request dns_max_queries dns_session_timeout dns_skip_addl
@ -248,7 +248,7 @@ event dns_CNAME_reply%(c: connection, msg: dns_msg, ans: dns_answer, name: strin
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_SOA_reply dns_SRV_reply
## dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end dns_full_request
## dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request
## dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout
@ -273,7 +273,7 @@ event dns_PTR_reply%(c: connection, msg: dns_msg, ans: dns_answer, name: string%
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SRV_reply
## dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end dns_full_request
## dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request
## dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout
@ -296,7 +296,7 @@ event dns_SOA_reply%(c: connection, msg: dns_msg, ans: dns_answer, soa: dns_soa%
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_end dns_full_request
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_end dns_full_request
## dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout
@ -319,7 +319,7 @@ event dns_WKS_reply%(c: connection, msg: dns_msg, ans: dns_answer%);
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl dns_MX_reply
## dns_NS_reply dns_PTR_reply dns_SOA_reply dns_SRV_reply dns_TSIG_addl
## dns_TXT_reply dns_WKS_reply dns_end dns_full_request dns_mapping_altered
## dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request dns_mapping_altered
## dns_mapping_lost_name dns_mapping_new_name dns_mapping_unverified
## dns_mapping_valid dns_message dns_query_reply dns_rejected dns_request
## non_dns_request dns_max_queries dns_session_timeout dns_skip_addl
@ -346,7 +346,7 @@ event dns_HINFO_reply%(c: connection, msg: dns_msg, ans: dns_answer%);
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_NS_reply dns_PTR_reply dns_SOA_reply dns_SRV_reply
## dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end dns_full_request
## dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request
## dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout
@ -378,6 +378,31 @@ event dns_MX_reply%(c: connection, msg: dns_msg, ans: dns_answer, name: string,
## dns_skip_addl dns_skip_all_addl dns_skip_all_auth dns_skip_auth
event dns_TXT_reply%(c: connection, msg: dns_msg, ans: dns_answer, strs: string_vec%);
## Generated for DNS replies of type *SPF*. For replies with multiple answers,
## an individual event of the corresponding type is raised for each.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Domain_Name_System>`__ for more
## information about the DNS protocol. Zeek analyzes both UDP and TCP DNS
## sessions.
##
## c: The connection, which may be UDP or TCP depending on the type of the
## transport-layer session being analyzed.
##
## msg: The parsed DNS message header.
##
## ans: The type-independent part of the parsed answer record.
##
## strs: The textual information returned by the reply.
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply
## dns_SRV_reply dns_TSIG_addl dns_WKS_reply dns_end dns_full_request
## dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout
## dns_skip_addl dns_skip_all_addl dns_skip_all_auth dns_skip_auth
event dns_SPF_reply%(c: connection, msg: dns_msg, ans: dns_answer, strs: string_vec%);
## Generated for DNS replies of type *CAA* (Certification Authority Authorization).
## For replies with multiple answers, an individual event of the corresponding type
## is raised for each.
@ -425,7 +450,7 @@ event dns_CAA_reply%(c: connection, msg: dns_msg, ans: dns_answer, flags: count,
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply
## dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end dns_full_request
## dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request
## dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout
@ -444,7 +469,7 @@ event dns_SRV_reply%(c: connection, msg: dns_msg, ans: dns_answer, target: strin
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply
## dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_SRV_reply dns_end
## dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_SRV_reply dns_end
event dns_unknown_reply%(c: connection, msg: dns_msg, ans: dns_answer%);
## Generated for DNS replies of type *EDNS*. For replies with multiple answers,
@ -463,7 +488,7 @@ event dns_unknown_reply%(c: connection, msg: dns_msg, ans: dns_answer%);
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_HINFO_reply dns_MX_reply
## dns_NS_reply dns_PTR_reply dns_SOA_reply dns_SRV_reply dns_TSIG_addl
## dns_TXT_reply dns_WKS_reply dns_end dns_full_request dns_mapping_altered
## dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request dns_mapping_altered
## dns_mapping_lost_name dns_mapping_new_name dns_mapping_unverified
## dns_mapping_valid dns_message dns_query_reply dns_rejected dns_request
## non_dns_request dns_max_queries dns_session_timeout dns_skip_addl
@ -486,7 +511,7 @@ event dns_EDNS_addl%(c: connection, msg: dns_msg, ans: dns_edns_additional%);
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply
## dns_SRV_reply dns_TXT_reply dns_WKS_reply dns_end dns_full_request
## dns_SRV_reply dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_full_request
## dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout
@ -575,7 +600,7 @@ event dns_DS%(c: connection, msg: dns_msg, ans: dns_answer, ds: dns_ds_rr%);
##
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_full_request
## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_full_request
## dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name
## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply
## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout

View file

@ -0,0 +1,10 @@
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path dns
#open 2019-06-14-15-15-00
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto trans_id rtt query qclass qclass_name qtype qtype_name rcode rcode_name AA TC RD RA Z answers TTLs rejected
#types time string addr port addr port enum count interval string count string count string count string bool bool bool bool count vector[string] vector[interval] bool
1560524739.386971 CHhAvVGS1DHFjwGM9 10.91.0.62 57806 10.91.1.59 53 udp 64161 - mail.vladg.net - - - - 0 NOERROR F F F T 0 SPF 19 v=spf1 mx -all test,SPF 14 v=spf1 mx -all 300.000000,300.000000 F
#close 2019-06-14-15-15-00

Binary file not shown.

View file

@ -0,0 +1,4 @@
# @TEST-EXEC: zeek -b -r $TRACES/dns-spf.pcap %INPUT
# @TEST-EXEC: btest-diff dns.log
@load base/protocols/dns