I’ve been playing with Bro a little bit. No, Bro is not my pet. I am talking about Bro the open source NIDS. So I was scared to death about the prospect of working with Bro, thanks to the hype surrounding its complexity. Another downer was the note on their Wiki page saying “NOTE: This wiki is out of date and no longer maintained”. So my last option was to use their latest workshop material. After going through the workshop, I still felt kind of lost. After remaining on a heavy dose of Bro for about eight days, today I finally feel like I’ve seen light of the day after a long stormy night. I thought I’ll share my understanding of the tool with you to make it easier for you (read: how cool am I ?).
To begin with, it is not so difficult after all. If you come from a programming background, you shouldn’t be worried at all. I’ll take you forward step by step. First, lets look at the basic architecture of Bro (the figure below is mine; it is based on the original figure on Bro’s website).
You have packets flowing in and out of your machine. You need a way to capture those packets for your own use. Bro does this by using libpcap. The nitty gritty details of how it captures network traffic don’t matter from user point-of-view. In some cases, you might not be interested in all the network traffic. Instead, you are interested only in traffic of certain kind, for example, HTTP. You can do this by (optionally) specifiying capture filters. Alternately, you can also set ‘rejection filters’ which essentially means: ‘I am interested in any traffic BUT this kind of traffic’. Basically, filtering enables you to relieve Bro of extra, unnecessary processing by capturing only meaningful network traffic; ‘meaningful’ being subject to user requirements.
Apart from hard-wired filters that indiscriminately apply to all traffic, there can also be some tell-tale signs that indicate ‘fishiness’. This logic is built into analyzers which look for anomalies and known bad-behavior in different protocols. It doesn’t have to be related to protocols necessarily. For example, multiple login failures can hint at breaking into a computer via brute force. In a nutshell, fishy behavior is observed using analyzers. The output of an analyzer is one or more events. An event simply announces to a subscribing module that something has happened. It’s just like telling your secretary to give you a call if she sees a man with black jacket in the lobby. So, the criteria ‘man in the black jacket’ is defined in the analyzer, which is your secretary’s memory in this case. The event is secretary’s call informing you that she has indeed witnessed a man with a black jacket. You are the subscribing module. In Bro’s case, the subscribing module will reside in Scriptland as we’ll see..
If you are merely using Bro, you won’t be required to do anything here. You belong in Scriptland. However, if you are a developer meaning to extend the functionality of Bro, you may tweak existing analyzers or write new ones.
Most users of Bro will find themselves messing around in Scriptland. Bro has a custom language which they call ‘policy scripting language’. By default, Bro is neutral. That is to say, Bro does not impose its own ideas of ‘abnormality in network traffic’ on its users. Instead, users are expected to define abnormality according to their situation. For example, one network administrator might say ‘Alert me if a spam email originates from my network’. Another might say ‘I am kind of lazy. Just let me know when there are a hundred spam emails from my network’. So users have the flexibility to specify what is interesting information to them. This is great. I repeat, this is GREAT. Most state-of-the-art tools lack this level of flexibility.
Most of the scripts in Scriptland merely handle events generated by the Analyzer/Event Engine. For example, Event Engine might tell you that it saw a failed http connection. In Scriptland, you’ll increment the value of a global variable, say, n_failed_conn. Within the next 30 minutes, your script in Scriptland receives about five hundred similar events and obviously, n_failed_conn=500, so by setting up a simple condition, you can do any of the following actions:
Write to a disk (e.g. Between the interval x-y I saw 500 failed http conn originating from w.x.y.z )
Update or maintain some global state data. We already did that with n_failed_conn when it received failed HTTP connection events while it was < 500.
Generate alarms for syslog. This is particularly useful for network forensics.
Call another function e.g. print the ip address of the responsible host
Generate another event.
Invoke shell commands to block flows, or pass information to routers to update their acl etc.
This is enough for now. More, later.