One of the Zeek concepts we discuss in SEC503: Intrusion Detection In-Depth is how scripts are reacting to events, not necessarily packets. Yes, Zeek processes packets and scripts can be written to react to individual packet characteristics but this is through exposed events. A single packet may trigger one event but, more than likely, it is triggering multiple events. Let's look at an example using this packet:
$ tcpdump -n -r ./google.dns.pcap -c1 -X reading from file ./google.dns.pcap, link-type EN10MB (Ethernet)14:29:38.964599 IP 192.168.60.22.52905 > 192.168.50.25.53: 49886+ A? www.google.com. (32) 0x0000: 4500 003c 3203 0000 7f11 1a2e c0a8 3c16 E..<2.........<. 0x0010: c0a8 3219 cea9 0035 0028 efb8 c2de 0100 ..2....5.(...... 0x0020: 0001 0000 0000 0000 0377 7777 0667 6f6f .........www.goo 0x0030: 676c 6503 636f 6d00 0001 0001 gle.com.........
This packet is a DNS query request for www.google.com. Being a UDP packet, it would trigger UDP events. We could write a script to subscribe to a UDP event and process custom functions like:
event udp_request (u: connection ) { if (u$id$resp_p==53/udp){ print fmt("New UDP packet on port 53. Host %s %s --> host %s %s.", u$id$orig_h, u$id$orig_p, u$id$resp_h, u$id$resp_p); } }
The script, named example.zeek
, will print a message to the screen for every new UDP packet with a destination port of 53.
$ zeek -r ./google.dns.pcap ./example.zeek New UDP packet on port 53. Host 192.168.60.22 52905/udp --> host 192.168.50.25 53/udp.
There are other events triggered by this one packet. For example, since this is the DNS request, it is the first packet in a series that will comprise the overall "connection", so new_connection
will be triggered, in addition to new_packet
. Moving up the protocol stack rather than down it, we would also find DNS events being triggered. Let's say we wanted a notice for any Google query requests. We could subscribe to the dns_request
event checking for "google" in the query like:
event dns_request (c: connection , msg: dns_msg , query: string , qtype: count , qclass: count ) { if ("google" in query){ print fmt("Host %s is looking for a Google site --> %s.", c$id$orig_h, query); } }
And the script, example2.zeek
, would run our code if it saw a google query request:
$ zeek -r ./google.dns.pcap ./example2.zeek Host 192.168.60.22 is looking for a Google site --> www.google.com.
Individual packets may also trigger the same event multiple times. Let's use the DNS response to the above www.google.com query to show this behavior. Here's the packet:
14:29:39.004832 IP 192.168.50.25.53 > 192.168.60.22.52905: 49886 16/0/0 A 167.206.252.236, A 167.206.252.221, A 167.206.252.247, A 167.206.252.227, A 167.206.252.212, A 167.206.252.216, A 167.206.252.237, A 167.206.252.232, A 167.206.252.246, A 167.206.252.251, A 167.206.252.226, A 167.206.252.231, A 167.206.252.241, A 167.206.252.242, A 167.206.252.217, A 167.206.252.222 (288) 0x0000: 4500 013c 2f5c 4000 7f11 dbd4 c0a8 3219 E..</\@.......2. 0x0010: c0a8 3c16 0035 cea9 0128 1c4a c2de 8180 ..<..5...(.J.... 0x0020: 0001 0010 0000 0000 0377 7777 0667 6f6f .........www.goo 0x0030: 676c 6503 636f 6d00 0001 0001 c00c 0001 gle.com......... 0x0040: 0001 0000 0044 0004 a7ce fcec c00c 0001 .....D.......... 0x0050: 0001 0000 0044 0004 a7ce fcdd c00c 0001 .....D.......... 0x0060: 0001 0000 0044 0004 a7ce fcf7 c00c 0001 .....D.......... 0x0070: 0001 0000 0044 0004 a7ce fce3 c00c 0001 .....D.......... 0x0080: 0001 0000 0044 0004 a7ce fcd4 c00c 0001 .....D.......... 0x0090: 0001 0000 0044 0004 a7ce fcd8 c00c 0001 .....D.......... 0x00a0: 0001 0000 0044 0004 a7ce fced c00c 0001 .....D.......... 0x00b0: 0001 0000 0044 0004 a7ce fce8 c00c 0001 .....D.......... 0x00c0: 0001 0000 0044 0004 a7ce fcf6 c00c 0001 .....D.......... 0x00d0: 0001 0000 0044 0004 a7ce fcfb c00c 0001 .....D.......... 0x00e0: 0001 0000 0044 0004 a7ce fce2 c00c 0001 .....D.......... 0x00f0: 0001 0000 0044 0004 a7ce fce7 c00c 0001 .....D.......... 0x0100: 0001 0000 0044 0004 a7ce fcf1 c00c 0001 .....D.......... 0x0110: 0001 0000 0044 0004 a7ce fcf2 c00c 0001 .....D.......... 0x0120: 0001 0000 0044 0004 a7ce fcd9 c00c 0001 .....D.......... 0x0130: 0001 0000 0044 0004 a7ce fcde .....D......
We can write a script, example3.zeek
, that looks for any DNS replies that contain "google" and print the IP address provided in the response:
event dns_A_reply (c: connection, msg: dns_msg, ans: dns_answer, a: addr) { if("google" in c$dns$query){ print fmt ("%s is at IP address %s", c$dns$query, a); } }
Notice all the messages when we run this script against our single packet:
$ zeek -r ./google.dns.pcap ./example3.zeek www.google.com is at IP address 167.206.252.236 www.google.com is at IP address 167.206.252.221 www.google.com is at IP address 167.206.252.247 www.google.com is at IP address 167.206.252.227 www.google.com is at IP address 167.206.252.212 www.google.com is at IP address 167.206.252.216 www.google.com is at IP address 167.206.252.237 www.google.com is at IP address 167.206.252.232 www.google.com is at IP address 167.206.252.246 www.google.com is at IP address 167.206.252.251 www.google.com is at IP address 167.206.252.226 www.google.com is at IP address 167.206.252.231 www.google.com is at IP address 167.206.252.241 www.google.com is at IP address 167.206.252.242 www.google.com is at IP address 167.206.252.217 www.google.com is at IP address 167.206.252.222
So, duh Andy, all those IP addresses are in that packet. Yes, but the important part is that this single packet is triggering that event multiple times. Let's look at it a bit differently. We will keep using the dns_A_reply
event but let's match the transaction ID in our packet and print the transaction ID and query in example4.zeek
.
event dns_A_reply (c: connection, msg: dns_msg, ans: dns_answer, a: addr) { if(c$dns$trans_id == 49886){ print fmt ("Transaction ID %s is querying for %s", c$dns$trans_id, c$dns$query); } }
Since there's only one reply in this pcap with the transaction ID of 49886, we should only see one message, right?
$ zeek -r ./google.dns.pcap ./example4.zeek Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com Transaction ID 49886 is querying for www.google.com
Well, it turns out that there are events that may trigger multiple times for the same packet.
It is important to remember that Zeek scripts are reacting to events, not packets. Packets many trigger one or more different events as well as potentially triggering the same event multiple times.
Andy Laman, a principal consultant with A4 InfoSec, has more than 25 years of information technology and security experience in multiple industries. Andy is a course contributor and teaches SEC503: Intrusion Detection In-Depth, for the SANS Institute. In addition to the CISSP, Andy holds multiple GIAC certifications including the prestigious GIAC Security Expert (GSE #142) certification as well as multiple other industry certifications.