The Weekly Zeek: Events, not packets
By Andy on 2019-10-12 20:32:51

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.