Network Security Monitoring With Bro IDS, TCPDump And MongoDB

Bro IDS is a powerful open source network security monitoring framework which I have had the opportunity to experiment with on a network monitoring server. It can log metadata for well known protocols such as HTTP, DNS and SMTP, as well as extract files it sees being transferred in these protocols. It logs all its results to CSV files and provides a useful tool called ‘bro-cut’ to enable analysts to search through these results. Bro-cut is a great tool but I wanted to store my data in MongoDB to enable useful queries to be run regularly and also so a Graphical User Interface (GUI) could be built on top of it. I also wanted full packet capture to be stored so I could trace any suspicious files or events back to the network activity that created it.

Why MongoDB?

  •  At this stage, I’m not sure  what data I want going into the database, so creating database schemas at this point may prove to be wasted effort, as I may have to modify or delete them in the future.
  • I have a lot of different inputs for the database which I don’t want to write database schemas for.
  • If I add new inputs to the database, minimum effort should be required.
  • All data stored in one database reduces dependencies and complexity.
  • We can consolidate all the Bro data in one place rather than having different files for each day which we would have to append to each other to use with bro-cut.
  • It should be possible to add more database servers if the need should arise due to MongoDB’s scalability.

Packet Capture
Full packet capture is useful when investigating incidents within the network. TCPdump can sniff packets and write them to disk. Another useful option for TCPdump is the ability to rotate the log file when it reaches a certain size and specify a post capture script to run when this rotation occurs. The below command will rotate the log file when it reaches 256 MB and then call post_capture.py:

[cpp]
tcpdump -i eth1 -s0 -nn -C 256 -w ‘/pcap/nettitude.pcap’ -z
/opt/nettitude/capture/post_capture.py  -Z root
[/cpp]

The post capture script basically generates some metadata about the pcap file (path, start and end times, size, duration, number of packets) and then inserts them into MongoDB.

Metadata extraction
Bro logs metadata in CSV files by default but we can set it to log to files in JSON format by moving by:

[cpp]
$BRO_INSTALL/share/bro/policy/tuning/json-logs.bro to
$BRO_INSTALL/share/bro/policy/tuning/defaults/json-logs.bro
[/cpp]

As with the snort logs, we can set up a script to watch the bro log directory and insert any JSON files created into MongoDB. This is easy to do since MongoDB basically stores JSON documents, and can be achieved with various programming languages. My choice is python and I use the pymongo library to import the data:

[cpp]
#!/usr/bin/python
import json
import pymongo
import sys</pre>
file = sys.argv[1]
colname = sys.argv[2]
client = pymongo.MongoClient(‘localhost’)
db = client[‘nettitude’]
collection = db[colname]
with open(file) as f:
for line in f.readlines():
collection.insert(json.loads(line))
[/cpp]

Once the data is in the database we can perform queries in mongo shell to get useful information. For example, the below query will list the top 20 recognised services observed.

[cpp]
sam@broserver:~$ mongo nettitude
MongoDB shell version: 2.6.7
db.conn.aggregate([
 {$match: {‘service’: { $ne: null }}},
 {$group: {‘_id’: ‘$service’,’sum’: { $sum: 1 }}},
 {$sort: {‘sum’: -1}},
 {$limit: 20}
])
[/cpp]

File Extraction
By default, Bro will only extract executables but you can change this by editing the Bro file extraction script located at $BRO_INSTALL/share/bro/file-extraction/extract.bro (In the below file you can see that I have enabled PDF and EXE extraction)

Figure 1

Figure 1

From here on you can set up another script to analyse these files when they are created.

Tracing the file origin
Say, for example, you may have analysed a suspicious file which Bro has extracted to HTTP-FEUq8i13A67Tp9VYw3.exe, we can use mongo shell to retrieve all the metadata and packet capture that relates to this file since its protocol and unique file ID (highlighted in red) are saved in the file name.

[cpp]
sam@broserver:~$ mongo nettitude
MongoDB shell version: 2.6.7
connecting to: nettitude
> db.files.findOne({fuid: ‘FEUq8i13A67Tp9VYw3’})
{
"_id" : ObjectId("5506d64d9ba3842590777d8e"),
"rx_hosts" : [
"1.1.1.1"
],
"tx_hosts" : [
"2.2.2.2"
],
"fuid" : "FEUq8i13A67Tp9VYw3",
"total_bytes" : 4171576,
"is_orig" : false,
"duration" : 8.225032,
"source" : "HTTP",
"analyzers" : [
"MD5",
"SHA1",
"EXTRACT"
],
"ts" : ISODate("2015-03-16T12:24:14.094Z"),
"filename" : "filename.exe",
"extracted" : "/bro/extracted/HTTP-FEUq8i13A67Tp9VYw3.exe",
"mime_type" : "application/x-dosexec",
"conn_uids" : [
"CKyCfA4l20KKnMr7n1"
],
"timedout" : false,
"local_orig" : false,
"missing_bytes" : 0,
"seen_bytes" : 4171576,
"md5" : "824f7ba4e6a1f56e1c70b835af43c301",
"sha1" : "1c7ac412d3bb2bd3ecf24c6f86a80ed3d48732cf",
"depth" : 0,
"overflow_bytes" : 0
}
> db.http.findOne({uid: "CKyCfA4l20KKnMr7n1"})
{
"_id" : ObjectId("5506d6449ba3842558e446f3"),
"id_resp_h" : "2.2.2.2",
"uid" : "CKyCfA4l20KKnMr7n1",
"status_code" : 200,
"orig_mime_types" : [
"text/plain"
],
"id_resp_p" : 80,
"trans_depth" : 1,
"request_body_len" : 68,
"orig_fuids" : [
"FcwBwWZxEVOsTsScc"
],
"ts" : ISODate("2015-03-16T12:24:13.736Z"),
"resp_mime_types" : [
"application/x-dosexec"
],
"method" : "GET",
"id_orig_h" : "1.1.1.1",
"tags" : [ ],
"resp_fuids" : [
"FEUq8i13A67Tp9VYw3"
],
"response_body_len" : 4171576,
"host" : "www.website.com",
"id_orig_p" : 23056,
"status_msg" : "OK",
"uri" : "/filename.exe",
"user_agent" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36",
"referrer" : "https://www.nettitude.com/"
}
> db.conn.findOne({uid: "CKyCfA4l20KKnMr7n1"})
{
"_id" : ObjectId("5506d6369ba3842512e05e55"),
"resp_bytes" : 4171839 CKyCfA4l20KKnMr7n1,
"uid" : "",
"resp_cc" : "US",
"tunnel_parents" : [ ],
"duration" : 8.786603,
"id_resp_h" : "2.2.2.2",
"id_resp_p" : 80,
"sensorname" : "eth1",
"service" : "http",
"proto" : "tcp",
"resp_pkts" : 3058,
"orig_pkts" : 962,
"ts" : ISODate("2015-03-16T12:24:13.566Z"),
"resp_ip_bytes" : 4334191,
"orig_cc" : "GB",
"id_orig_h" : "1.1.1.1",
"orig_ip_bytes" : 48307,
"local_orig" : true,
"missed_bytes" : 0,
"orig_bytes" : 723,
"id_orig_p" : 23056,
"conn_state" : "SF",
"history" : "ShADadfF"
}
[/cpp]

With the details from the conn collection, we can ascertain the time frame for this connection which allows us to find the pcap file in which the session is stored. The connection started at 2015-03-16T12:24:13.566Z for a duration of 8.78 seconds. It is probably better to add a second or so either side of the date range.

[cpp]
> db.pcap.findOne({sensor: ‘eth1’, start_ts : {$lte :  ISODate("2015-03-16T12:24:12.000Z")},  end_ts : {$gte :  ISODate("2015-03-16T12:24:21.000Z")}})
{
"_id" : "/pcap/2015-03-16/20150316123157.eth1.pcap",
"end_ts" : ISODate("2015-03-16T12:31:57Z"),
"start_ts" : ISODate("2015-03-16T12:22:32Z"),
"num_packets" : "311998",
"file_size" : "256000821 bytes",
"duration" : "565 seconds",
"sensor" : "eth1",
"data_size" : "251008829 bytes"
}
[/cpp]

Now we know the pcap file and source/desination IP addresses and ports, we can quickly create a tpcdump command to view the packets.

[cpp]
tcpdump –nn –r /pcap/2015-03-16/20150316123157.eth1.pcap ‘tcp and src host 1.1.1.1 and src port 23056 and dst host 2.2.2.2 and dst port 80’
[/cpp]

Conclusion
This framework provides a good starting point for network security monitoring and allows us to create additional analysis tools on top of it, for example, automatic report generation, dashboard style GUIs or advanced querying.

 

To contact Nettitude’s editor, please contact media@nettitude.com.