1 Getting to know Bro
Bro is a lot of things but one of its primary strengths is providing a programming language for network analysis. Out of the box, it's more than likely that Bro does more than you know and you'll get to spend a significant amount of time working with the logs to better understand your network and gain the skills to identify actions that require further investigation. If you're interested in some basic Bro scripting tutorials, the Bro team posted the tutorials from the Bro 2011 Workshop here.
If you've never used Bro before, a great way to get it up and running is to install Doug Burk's SecurityOnion Linux Distribution in a VM and work from there. Everything I've done in this post was done in a SecurityOnion VM.
2 Why use Bro?
Bro isn't a signature based IDS. In fact, calling Bro an IDS does it something of a disservice. It's more aptly described as a Network Security Monitoring application or framework. Bro's detections are based primarily on heuristics and when combined with a robust built-in programming language, it becomes a tool you can't ignore.
As for using Bro to solve an old SANS Network Forensics Challenge? While Bro's programming language is not very difficult it does require understanding a lot of underlying capability. I had the pleasure of hanging out with Seth Hall from the Bro project and often I'd hear him talk about how it only took him an hour or so to add some incredible functionality to Bro but it tends to overshadow the fact that Seth has spent years on Bro. Watching Seth whip something up in Bro is indistinguishable from wizardry and my hope is that, by pulling back a little bit of the curtain, I can better understand how to utilize Bro. Getting up to speed with Bro is a daunting task, especially if you avoid things that could be described as navel gazing! Using Bro to solve a forensics challenge's networking based questions, is navel gazing, but it forced me to dig into a solid amount of source code and documentation.
3 The SANS Forensic Challenge
The challenge I worked with had a basic set up. The victim (10.10.10.70/32) is exploited using a client-side spear phishing attack. You can read the full challenge as well as get the trace file on the SANS.org site.
The questions that stuck out as opportunities to use Bro were:
- When was the TCP session on port 4444 opened? (Provide the number of seconds since the beginning of the packet capture, rounded to tenths of a second. ie, 49.5 seconds)
- When was the TCP session on port 4444 closed? (Provide the number of seconds since the beginning of the packet capture, rounded to tenths of a second. ie, 49.5 seconds)
- Vick's computer repeatedly tried to connect back to the malicious server on port 4445, even after the original connection on port 4444 was closed. With respect to these repeated failed connection attempts:
a. How often does the TCP initial sequence number (ISN) change? (Choose one.)
- Every packet
- Every third packet
- Every 10-15 seconds
- Every 30-35 seconds
- Every 60 seconds
b. How often does the IP ID change? (Choose one.)
- Every packet
- Every third packet
- Every 10-15 seconds
- Every 30-35 seconds
- Every 60 seconds
c. How often does the source port change? (Choose one.)
- Every third packet
- Every packet
- Every 30-35 seconds
- Every 10-15 seconds
- Every 60 seconds
- Eventually, the malicious server responded and opened a new connection. When was the TCP connection on port 4445 first successfully completed? (Provide the number of seconds since the beginning of the packet capture, rounded to tenths of a second. ie, 49.5 seconds)
- Subsequently, the malicious server sent an executable file to the client on port 4445. What was the MD5 sum of this executable file?
- When was the TCP connection on port 4445 closed? (Provide the number of seconds since the beginning of the packet capture, rounded to tenths of a second. ie, 49.5 seconds)
The challenge included finding the MD5 and filename of files downloaded, but as you'll see later a specific aspect of the tracefile prevents us from doing that.
4 Learning by failing
The primary reason for challenging myself to solve a network forensics challenge with Bro was to develop both a better understanding of how Bro works and to push myself to write more bro scripts. To wit, I spent a good deal of time failing and much of this post is going to include the process I used to find the information I needed to re-orient myself and make headway.
4.1 Finding Connections
The command line utility tshark is wireshark for CLI lovers and one of my most loved tools. Whenever, I'm looking at pcaps, I try to get a bird's eye view of the what is happening using tshark. For example, a common tshark command I use is to print the source ip and port and the destination ip and port by using the '-T fields' command line option. To save space, I've applied the common 'sort | uniq -c | sort -n' command to tell my terminal to show and count the unique entires. For reference, when run without the sort commands, the output is 2554 lines long.
tshark -r evidence06.pcap -T fields -e ip.src -e tcp.srcport -e ip.dst -e tcp.dstport | sort | uniq -c | sort -n 1 10.10.10.70 10.10.10.255 5 10.10.10.70 1035 10.10.10.10 8080 8 10.10.10.10 8080 10.10.10.70 1035 15 10.10.10.10 4445 10.10.10.70 1037 15 10.10.10.10 4445 10.10.10.70 1038 15 10.10.10.10 4445 10.10.10.70 1039 15 10.10.10.10 4445 10.10.10.70 1040 15 10.10.10.10 4445 10.10.10.70 1041 15 10.10.10.10 4445 10.10.10.70 1042 15 10.10.10.10 4445 10.10.10.70 1043 15 10.10.10.70 1037 10.10.10.10 4445 15 10.10.10.70 1038 10.10.10.10 4445 15 10.10.10.70 1039 10.10.10.10 4445 15 10.10.10.70 1040 10.10.10.10 4445 15 10.10.10.70 1041 10.10.10.10 4445 15 10.10.10.70 1042 10.10.10.10 4445 15 10.10.10.70 1043 10.10.10.10 4445 263 10.10.10.70 1044 10.10.10.10 4445 424 10.10.10.70 1036 10.10.10.10 4444 664 10.10.10.10 4445 10.10.10.70 1044 979 10.10.10.10 4444 10.10.10.70 1036
When I had started to look at using Bro for this, I tried to cast a wide net and started with the event "connection_established" which is exported from Bro's event.bif.bro file.
187 ## Generated for an established TCP connection. The event is raised when the 188 ## initial 3-way TCP handshake has successfully finished for a connection. 189 ## 190 ## c: The connection. ...snip... 198 global connection_established: event(c: connection);
Anytime the three way TCP handshake (SYN -> SYN/ACK -> ACK) has completed, this event should fire and since Bro is stream based, we should be able to produce a list of connections from the libpcap file provided in the challenge.
Bro uses the connection as a datatype, if you search through your base/init-bare.bro file you'll find the documentation for this type.
188 # A connection. This is Bro's basic connection type describing IP- and 189 # transport-layer information about the conversation. Note that Bro uses a 190 # liberal interpreation of "connection" and associates instances of this type 191 # also with UDP and ICMP flows. 192 type connection: record { 193 id: conn_id; ##< The connection's identifying 4-tuple. 194 orig: endpoint; ##< Statistics about originator side. 195 resp: endpoint; ##< Statistics about responder side. 196 start_time: time; ##< The timestamp of the connection's first packet. 197 ## The duration of the conversation. Roughly speaking, this is the interval between 198 ## first and last data packet (low-level TCP details may adjust it somewhat in 199 ## ambigious cases). 200 duration: interval; 201 ## The set of services the connection is using as determined by Bro's dynamic 202 ## protocol detection. Each entry is the label of an analyzer that confirmed that 203 ## it could parse the connection payload. While typically, there will be at 204 ## most one entry for each connection, in principle it is possible that more than 205 ## one protocol analyzer is able to parse the same data. If so, all will 206 ## be recorded. Also note that the recorced services are independent of any 207 ## transport-level protocols. 208 service: set[string]; 209 addl: string; ##< Deprecated. 210 hot: count; ##< Deprecated. 211 history: string; ##< State history of TCP connections. See *history* in :bro:see:`Conn::Info`. 212 ## A globally unique connection identifier. For each connection, Bro creates an ID 213 ## that is very likely unique across independent Bro runs. These IDs can thus be 214 ## used to tag and locate information associated with that connection. 215 uid: string; 216 };
You can check out the documentaiton from the Bro site if you want to explore the connection data type further. As you can see each connection is itself a collection of other datatypes to include endpoints, time strings, count. Bro gives us access to the whole fire hose of network information even in script land! Let's take a look at what we can see with the new_connection() event.
mac@securityonion-Analyst:~/challenges/SANS Forensic$ bro -r evidence06.pcap challenge2.bro [id=[orig_h=10.10.10.70, orig_p=1036/tcp, resp_h=10.10.10.10, resp_p=4444/tcp], orig=[size=0, state=4, num_pkts=1, num_bytes_ip=48], resp=[size=0, state=4, num_pkts=0, num_bytes_ip=0], start_time=1272498000.577135, duration=0.000071, service={ }, addl=, hot=0, history=Sh, uid=XRD3DR2rr51, dpd=<uninitialized>, conn=<uninitialized>, extract_orig=F, extract_resp=F, dns=<uninitialized>, dns_state=<uninitialized>, ftp=<uninitialized>, http=<uninitialized>, http_state=<uninitialized>, irc=<uninitialized>, smtp=<uninitialized>, smtp_state=<uninitialized>, ssh=<uninitialized>, ssl=<uninitialized>, syslog=<uninitialized>] [id=[orig_h=10.10.10.70, orig_p=1044/tcp, resp_h=10.10.10.10, resp_p=4445/tcp], orig=[size=0, state=4, num_pkts=1, num_bytes_ip=48], resp=[size=0, state=4, num_pkts=0, num_bytes_ip=0], start_time=1272498122.985483, duration=0.000097, service={ }, addl=, hot=0, history=Sh, uid=LGk3mPtc00b, dpd=<uninitialized>, conn=<uninitialized>, extract_orig=F, extract_resp=F, dns=<uninitialized>, dns_state=<uninitialized>, ftp=<uninitialized>, http=<uninitialized>, http_state=<uninitialized>, irc=<uninitialized>, smtp=<uninitialized>, smtp_state=<uninitialized>, ssh=<uninitialized>, ssl=<uninitialized>, syslog=<uninitialized>]
Even a cursory glance shows that while we're seeing a lot of data from Bro we're not seeing as many connections as we should! Not only did the challenge's description tell us that there was HTTP traffic but compared to the tshark output, we're looking at significantly less entries than we should be seeing even given the disparity between connection and stream based analyzers. Given the documentation from the connection_established event above, it should be somewhat obvious as to why some of the streams are missing. If connection_established only fires when three way handshake is present then we're missing the three way handshake for the HTTP connection. Let's check the first three packets of the trace file and see if they match up with a TCP 3-way handshake.
tshark -r evidence06.pcap -c 3 -T fields -e tcp.flags 0x18 0x10 0x10
That is certainly not a TCP handshake, so it looks like the trace file starts in the middle of a stream. These values show us a PSH,ACK and two ACK flags, instead of the standard 3-way handshake.
For reference, a TCP handshake should look like this:
tshark -r browse.pcap -c 3 -T fields -e tcp.flags 0x02 0x12 0x10
If you're curious as to how the hex values above map to TCP flags, it's a binary to hex conversion using the following table.
CWR | ECE | URG | ACK | PSH | RST | SYN | FIN |
---|---|---|---|---|---|---|---|
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
We can double check our findings with a little bit more abuse of tshark and look for any SYN/ACK flags set which would indicate a response in the TCP 3-way handshake.
tshark -r evidence06.pcap -T fields -e ip.src -e ip.dst -e tcp.flags | awk '{if ($3 == "0x12") print $0}' 10.10.10.10 10.10.10.70 0x12 10.10.10.10 10.10.10.70 0x12
So, not only will we miss the HTTP session that is already started, we're also not going to see any traffic that doesn't have a 3-way handshake. Since the challenge references rejected connections, we're definitely going to need to back to the base.bif.bro file and find a better solution.
Some quick perusing and searching for significant terms lead me to the new_connection() event.
133 ## Generated for every new connection. The event is raised with the first packet 134 ## of a previously unknown connection. Bro uses a flow-based definition of 135 ## "connection" here that includes not only TCP sessions but also UDP and ICMP 136 ## flows. 137 ## 138 ## c: The connection. ...snip... 149 ## Handling this event is potentially expensive. For example, during a SYN 150 ## flooding attack, every spoofed SYN packet will lead to a new 151 ## event. 152 global new_connection: event(c: connection); 153
This event is right up our alley! It doesn't care about the 3-way handshake, if it sees a packet it hasn't seen before it fires. Let's change our initial bro script to replace connection_established() with new_connection().
event new_connection(c: connection) { print c; }
[id=[orig_h=10.10.10.70, orig_p=1035/tcp, resp_h=10.10.10.10, resp_p=8080/tcp], orig=[size=0, state=0, num_pkts=0, num_bytes_ip=0], resp=[size=0, state=0, num_pkts=0, num_bytes_ip=0], start_time=1272497999.311284, duration=0.0, service={ }, addl=, hot=0, history=, uid=P0lEbfBMXFh, dpd=<uninitialized>, conn=<uninitialized>, extract_orig=F, extract_resp=F, dns=<uninitialized>, dns_state=<uninitialized>, ftp=<uninitialized>, http=<uninitialized>, http_state=<uninitialized>, irc=<uninitialized>, smtp=<uninitialized>, smtp_state=<uninitialized>, ssh=<uninitialized>, ssl=<uninitialized>, syslog=<uninitialized>] [id=[orig_h=10.10.10.70, orig_p=1036/tcp, resp_h=10.10.10.10, resp_p=4444/tcp], orig=[size=0, state=0, num_pkts=0, num_bytes_ip=0], resp=[size=0, state=0, num_pkts=0, num_bytes_ip=0], start_time=1272498000.577135, duration=0.0, service={ }, addl=, hot=0, history=, uid=y4plS32M81e, dpd=<uninitialized>, conn=<uninitialized>, extract_orig=F, extract_resp=F, dns=<uninitialized>, dns_state=<uninitialized>, ftp=<uninitialized>, http=<uninitialized>, http_state=<uninitialized>, irc=<uninitialized>, smtp=<uninitialized>, smtp_state=<uninitialized>, ssh=<uninitialized>, ssl=<uninitialized>, syslog=<uninitialized>]
Now our output is more reasonable and there are 369 lines of it! That's more like it!
Let's make some formatting changes to our script so it's little more easy to quickly parse the output. We'll make it suggestive of the tshark output above.
In Bro, we can use the '$' to dereference, so if we wanted the origh, we could walk the output above and build our expression: c$id$origh. Bro also provides an fmt() conversion that operates much like printf so we can build a nicely formatted output with four string placeholders (%s) and the data we'd like to see(c$id$orig_h, c$id$orig_p, c$id$resp_h, and finally c$id$resp_p).
event new_connection(c: connection) { print fmt("New Connection => orig: %s %s resp: %s %s", c$id$orig_h, c$id$orig_p, c$id$resp_h, c$id$resp_p); }
New Connection => orig: 10.10.10.70 1035/tcp resp: 10.10.10.10 8080/tcp New Connection => orig: 10.10.10.70 1036/tcp resp: 10.10.10.10 4444/tcp New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp ...snip... New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp
Now, we have the event fired for each new connection and we can see the originator port and the responder port. Just having this information we can start building an answer to question 9c from the challenge. The question is specific about the originating host and the responder's port so let's be specific as well. A simple logical if statement will give us the ability to only print our nicely formatted output if the originating host is the victim machine (10.10.10.70) and the responder's port is 4445/tcp. The question is also specific about how long it takes for the originator to switch ports so we'll add the start_time to our output.
if (c$id$orig_h == 10.10.10.70 && c$id$resp_p == 4445/tcp) print fmt("New Connection => orig: %s %s resp: %s %s time: %s", c$id$orig_h, c$id$orig_p, c$id$resp_h, c$id$resp_p, c$start_time);
New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498035.258314 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498035.594943 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498036.141827 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498036.142471 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498036.6887 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498037.235554 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498037.23652 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498037.782456 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498038.329315 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498038.329973 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498038.876194 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498039.313691 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498039.314346 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498039.860571 New Connection => orig: 10.10.10.70 1037/tcp resp: 10.10.10.10 4445/tcp time: 1272498040.298079 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498047.043801 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498047.40741 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498047.954312 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498047.954969 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498048.391806 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498048.938686 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498048.939329 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498049.485544 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498050.032408 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498050.033078 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498050.579291 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498051.016808 New Connection => orig: 10.10.10.70 1038/tcp resp: 10.10.10.10 4445/tcp time: 1272498051.017456 ...snip... New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498107.236156 New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498107.782395 New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498108.329244 New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498108.329911 New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498108.876468 New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498109.313638 New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498109.314295 New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498109.860522 New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498110.298011 New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498110.298669 New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498110.844862 New Connection => orig: 10.10.10.70 1043/tcp resp: 10.10.10.10 4445/tcp time: 1272498111.282386 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498118.057545 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498118.391735 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498118.938626 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498118.939275 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498119.485504 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498120.032355 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498120.033013 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498120.579247 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498121.016727 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498121.01738 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498121.563621 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498122.001099 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498122.001752 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498122.548001 New Connection => orig: 10.10.10.70 1044/tcp resp: 10.10.10.10 4445/tcp time: 1272498122.985483
Now we've got just the traffic we're interested in for question 9c. However, counting is still boring, let's have Bro count for us! To do this we'll use a table to map a originator port to the time when we first see that port attempting to connect to the attackers 4445/tcp port. As each new_connection() event is handled, if we haven't seen the originator's port before, we create a new entry in the table for the originator port and map it to the c$start_time. Once we've processed each new_connection() event, we still need to create some valuable output from the data set we've created. The best way to do this is to use the event bro_done().
global source_ports: table[port] of time; event new_connection(c: connection) { if (c$id$orig_h == 10.10.10.70 && c$id$resp_p == 4445/tcp) { if (c$id$orig_p !in source_ports) source_ports[c$id$orig_p] = c$start_time; } } event bro_done() { local ptime: set[time]; local sports: vector of port; local stime: vector of time; local inc: int = 0; for (p in source_ports) { sports[inc] = p; stime[inc] = source_ports[p]; inc+=1; } sort(stime); sort(sports); for (j in stime) { print fmt("Delta Time: %s", stime[j+1] - stime[j]); } }
mac@securityonion-Analyst:~/challenges/SANS Forensic$ bro -r evidence06.pcap challenge2.bro Delta Time: 11.0 secs 785.0 msecs 487.0 usecs Delta Time: 11.0 secs 730.0 msecs 439.0 usecs Delta Time: 11.0 secs 795.0 msecs 35.0 usecs Delta Time: 11.0 secs 735.0 msecs 993.0 usecs Delta Time: 11.0 secs 884.0 msecs 180.0 usecs Delta Time: 11.0 secs 960.0 msecs 521.0 usecs Delta Time: 11.0 secs 907.0 msecs 572.0 usecs
Once we've run the script we get an output of the differences in time between port changes. The originator attempted to contact port 4445/tcp every 11.7 to 11.9 seconds. Which falls in the range of the 10-15 second option for question 9c!
Given what we've worked through in this blog post alone, it's actually rather simple to answer question ten as well!
global first_contact: time; event connection_established(c: connection) { if (c$id$resp_p == 4445/tcp) first_contact = c$start_time; } event bro_done() { print strftime("Successful connection to 4445/tcp at %Y/%m/%d %H:%M:%S", first_contact); }
Successful connection to 4445/tcp at 2010/04/28 19:42:02
5 Wrapping up
Hopefully, this post has gotten you interested in looking at the Bro programming language. There are a lot of posts online about how great Bro is, but scarce few covering how to go about learning the scripting language. Bro's scripting language holds a lot of surprises in store for the freshly minted Bro acolyte and the most efficient way to go from acolyte to journeyman is to spend your time looking at the scripts already being used by Bro. As I worked through the challenge I would use grep to search through the scripts directory ( /usr/local/share/bro on Security Onion) for any relevant terms and read the documentation is the files returned. Think of the default scripts distributed with Bro as a pool of collective knowledge to dip your feet into from time to time.
In part two of this series we will pick up where we've left off and solve more of the questions from the SANs Network Forensics challenge. We'll also take another look at some of the code we used in this post as it /may/ not be the most "bro-ish" way to solve the problem. While we got the answers we needed, I suspect there are ways to do so in a way that fits more in line with how we will eventually write code to run in production.
6 Resources
Resource | Link |
---|---|
Bro Homepage | http://www.bro-ids.org/ |
Bro Language Cheat Sheet | http://blog.bro-ids.org/2011/11/bro-language-cheat-sheet.html |
Bro Online Documentaiton | http://www.bro-ids.org/documentation/index.html |
Bro Online 2011 Workshop | http://bro-ids.org/bro-workshop-2011/index.html |
Security Onion | http://securityonion.blogspot.com |
Hi Scott,
ReplyDeleteI love this article and the practical approach to learning a new tool chain with familiar data! Thanks for posting this!
Excellent article.. I'm a beginner to Bro language, and this was amazing.
ReplyDeleteI have a question. I wish to analyze every packet's payload in a pcap file. (I'm implementing an anomaly detection system in Bro). Which event(s) should I use for that?
Marcos, Thanks!
ReplyDeleteAsma, I'm planning to cover packet analysis in Part 2, but you can probably start with new_pkt(c: connection, p: pkt_hdr). Check out the documentation for it. It's suitable for pcap analysis, but for live stream analysis it will probably bring your sensor to its knees!
Isn't your final example going to give you last contact? I.e. don't you want to check if fist_contact is set and only set if not already set?
ReplyDeleteThank you so much for writing this!
ReplyDeleteI guess I am not only one having all the enjoyment right here!
ReplyDeleteNetworking Basics
This comment has been removed by the author.
ReplyDelete