Archive for the ‘Sguil’ Category

Sguil: renaming a sensor

Saturday, September 9th, 2006

This might be a no-brainer for some, but today I wanted to rename a sensor in Sguil, and decided to write the steps down.

Preparation

  1. backup the db
  2. stop sancp on the sensor
  3. stop barnyard on the sensor
  4. stop sensor_agent on the sensor
  5. stop snort on the sensor
  6. stop log_packets on the sensor
  7. stop sguild on the server

Steps on the Sguil server

Update the sensor name in the database.

# mysql -p
mysql> use sguildb;
mysql> UPDATE sensor SET hostname = “newname” WHERE hostname = “oldname”;
Query OK, 1 row affected (0.04 sec)
Rows matched: 1 Changed: 1 Warnings: 0

Rename the directory for the rules display

mv /sguild_data/rules/oldname /sguild_data/rules/newname

Steps on the sensor

Change the hostname in /etc/sensor_agent.conf

set HOSTNAME newname

Change the hostname in /etc/barnyard/barnyard.conf

config hostname: newname

Change the hostname in /usr/sbin/log_packets.sh

HOSTNAME=”newname”

Change the directory where sancp writes its files, for my Debian system in /etc/default/sancp

SANCP_ARCHIVE_DIR=”/snort_data/newname/sancp”

Change the Snort prefmonitor stats file location in /etc/snort/snort.conf

preprocessor perfmonitor: time 300 file /snort_data/newname/snort.stats pktcnt 10000

Rename the sensor directory

mv /snort_data/oldname /snort_data/newname

Finally

  1. start sguild on the server
  2. start log_packets.sh on the sensor
  3. start sancp on the sensor
  4. start snort on the sensor
  5. start sensor_agent on the sensor
  6. start barnyard on the sensor

That should be all!

UPDATE: forgot about sancp, snort stats and log_packets.sh

Sguil: adding support for ModSecurity alerts, continued

Friday, September 8th, 2006

After the successful test with the Perl script to add ModSecurity alerts to Sguil, I have been working on a more robust implementation, also in Perl. Let me first describe the basic setup. The setup works with two scripts. The first places links to event files into a special queue directory. The second reads the links from that directory, parses them and sends the alerts among these events to Sguil. After that, the links are removed.

In ModSecurity there is a logging method called concurrent. What it does is that it creates a directory structure based on date and time. For each event a file is created in the directory structure, with the details of the event. It also writes a logfile called index, that adds one line for every event, with location of the file in the date/time directory layout.

ModSecurity also supports using a external script to take care of this indexing task, which is what the script modsec-auditlog-collector in the ModSecurity source tarball does. I have modified this Perl script to create a link to the actual event file in a special queue directory. The links all have a name like: modsec.log.-timestamp-, like Snort uses with unified logging. I have chosen to use links because I think it might be faster than copying, but I have yet to benchmark that.

The second script takes care of processing the links and communicating with Sguil. It basically has the following workflow. It gets a list of all the files in the queue. Each file in the queue is parsed, then it is checked whether it is an alert. If so, the data is preprocessed to make sure it is what Sguil expects. Next it is send to Sguil. Finally, all the files are removed.

ModSecurity has a different rule syntax than Snort and sees a different type of data. Snort sees packets, and thus knows about TCP checksums, sequence numbers, etc. ModSecurity works on a higher level, so it has no access to that data. One thing that ModSecurity alerts do include is the action taken: blocked or redirected and with what HTTP code. If available the rule msg is used for the Event Message, otherwise the ModSecurity message with (a part of) the rule that matched. Below is a screenshot that shows both, and also the HTTP code.

Sguil and Mod_Security msg view

The current implementation acts as a drop-in replacement of Barnyard. This way no changes to Sguil are necessary. Currently this means that Barnyard cannot run on the same sensor, although multiple instances of sensor_agent on a host could fix that. Sguil author Bamm Visscher suggested I can try to connect to the Sguil server directly instead of via the sensor_agent to fix this. I will probably have a look at that later.

Another issue is the way the alert data is presented in the Sguil interface. Currently I attach the entire alert file to the alert that is being send to Sguil, as the packet payload. However the Sguil packet payload view is not very usable for this kind of data, as can be seen on the screenshot below.

Sguil and Mod_Security payload view

So here is room for much improvement. Ideally Sguil would be able to show the uri, the post payload if applicable, useragent, etc. I’ve never worked with TCL/TK, but I did use Perl/TK in the past, so that might help me a little if I decide to give it a shot.

I expect a public beta version to be ready some time next week, although I have not yet decided how to release it. I could create a project on SourceForge or just have a simple webpage. I just haven’t decided yet. Stay tuned…

Sguil: adding support for ModSecurity alerts

Wednesday, August 30th, 2006

I’m a huge fan of both Sguil and ModSecurity, but sadly the alerts generated by ModSecurity can’t show up in Sguil… or… can they? Well, if it all works out, soon they can!

Today I have hacked together a perl script that emulates barnyard for ModSecurity. It very much in a proof-of-concept phase, but it somewhat works already, at least good enough so i can show this screenshot.

Sguil screenshot showing experimental Mod_Security support
Nice huh? It is not ready for release yet, but when it is i’ll announce it here. I plan to release it under the GPL. Sguil author Bamm Visscher told me that the next release of Sguil will have support for having barnyard and PADS on the same sensor. By then, i hope that ModSecurity can be added to that list! :-)

Sguil: detecting ICMP tunnels, continued

Tuesday, August 29th, 2006

A few days ago i wrote about detecting ICMP tunnels. I came to the conclusion that for the two tunnels i tried the properties to detect on were:

  1. Non-standard average packet size. E.g. 81.81 bytes for the first connection is non-standard.
  2. Number of bytes in both directions are unequal.
  3. Average packet size in both directions is unequal.

I believe the third is the most important, because it must mean that 1 is also true and 2 is very likely to be true. If the average packet size is unequal in both directions, one of them should be non-standard. So far i have only seen echo-request and echo-reply being equal in size for pings. Unequal average packet sizes with exactly the same amount of data transfered is very unlikely, so i have ignored that for now.

This is the full report that shows all ICMP with unequal average packet sizes:

SUSPECTICMP||Suspect ICMP LAN->WAN: Top 10 ICMP users in the LAN talking to the WAN, with different packet sizes, with source and destination, volume and average packet size, sorted by packet size (fields: source ip, dest ip, from source MB, from source packet size, from dest MB, from dest packet size, total duration, average duration)||query||SELECT INET_NTOA(src_ip), INET_NTOA(dst_ip), my_src_bytes/1048576 AS my_src_mb, my_avg_src_pktsize, my_dst_bytes/1048576 AS my_dst_mb, my_avg_dst_pktsize, my_tot_duration, AVG(my_tot_duration) AS my_avg_duration FROM ( SELECT src_ip, dst_ip, ip_proto, SUM(src_bytes) AS my_src_bytes, SUM(dst_bytes) AS my_dst_bytes, start_time, end_time, SUM(src_bytes)/sum(src_pkts) AS my_avg_src_pktsize, SUM(dst_bytes)/sum(dst_pkts) AS my_avg_dst_pktsize, SUM(duration) AS my_tot_duration FROM ( SELECT sid AS my_sid, src_ip, dst_ip, ip_proto, src_bytes, dst_bytes, src_pkts, dst_pkts, start_time, end_time, duration FROM sancp IGNORE INDEX (p_key) WHERE ip_proto = 1 AND src_pkts > 0 AND dst_pkts > 0 AND start_time > %%STARTTIME%% AND end_time < %%ENDTIME%% ) AS step1_table INNER JOIN sensor ON my_sid=sensor.sid WHERE %%SENSORS%% AND ((src_ip between INET_ATON("192.168.0.0") and INET_ATON("192.168.255.255")) or (src_ip between INET_ATON("10.0.0.0") and INET_ATON("10.255.255.255")) or (src_ip between INET_ATON("172.16.0.0") and INET_ATON("172.31.255.255"))) and ((dst_ip not between INET_ATON("192.168.0.0") and INET_ATON("192.168.255.255")) and (dst_ip not between INET_ATON("10.0.0.0") and INET_ATON("10.255.255.255")) and (dst_ip not between INET_ATON("172.16.0.0") and INET_ATON("172.31.255.255"))) GROUP BY dst_bytes ) AS step2_table WHERE my_avg_src_pktsize != my_avg_dst_pktsize GROUP BY src_ip,dst_ip ORDER BY my_avg_dst_pktsize DESC, my_avg_src_pktsize DESC LIMIT 10||8||

This query only works on MySQL 4.1 and higher. It results in this report:

Sguil Sensor Report Showing an Suspect ICMP Report

This detects all tunnels I had setup, and shows nothing of the other non-tunnel tests I did, like the ping flood.

Sguil: using advanced queries to get more info on Snort events

Monday, August 28th, 2006

Today, David Bianco showed me a way of creating SQL queries that I didn’t even know was possible. This is a technique with which it is possible to query the payload of Snort events in the Sguil database. These payloads are stored by Snort when it alerts and is the payload the actual rule triggered on. David showed a nice example of retrieving url’s for PHP url inclusion attacks.

I have written before about my usage of Mod_Security. I let Mod_Security respond with a 403 Forbidden message. Sadly, the alert generated by Mod_Security can not be displayed in Sguil. As somewhat of a replacement for that, I let Snort alert on the 403 Forbidden messages, so i can see in Sguil that Mod_Security blocked something. The disadvantage of this is that the 403 alert in itself does not contain much info. The sheer number of 403’s makes inspecting every single one with requesting a transcript a bit to much work.

This is where the new query comes in. The query creates a list of url’s that the server reported to be 403 Forbidden. Interesting is that we are not looking at an attack, but at an attack response. This means the the attackers IP is actually the destination IP.

SELECT COUNT(*) AS cnt, INET_NTOA(dst_ip) AS “Dest IP”, trim(LEADING “access ” FROM substring_index(substr(unhex(data_payload),locate(’access ‘,unhex(data_payload))), ‘\n’, 1)) AS url FROM event INNER JOIN data ON event.sid = data.sid and event.cid = data.cid where (signature like “%ATTACK-RESPONSES 403 Forbidden%”) GROUP BY dst_ip,url ORDER BY cnt DESC LIMIT 10;

The result is this:

MySQL result for query for 403 Forbidden event url's

As you can see, the first six results are from the comment spam I wrote about earlier. I left the source IP out because there is only one webserver. The query can easily be extended to show source IP’s as well.

Sguil: detecting ICMP tunnels

Friday, August 25th, 2006

My earlier post about detecting ICMP anomolies was based on dry theory combined with experimenting with the ping command. The last couple of days I have been playing with real ICMP tunnels, to see how detection of those would work. This was easier said than done. Sure, running PingTunnel or itun between two hosts in my LAN worked fine. However, being an inline guy, i have Sancp looking at traffic passing my firewall only. And getting the ICMP tunnels to pass the firewall was the real trick. I won’t bore you with that now, because i intend to look at counter measures later, so i’ll handle that then. For now I will just assume that these ping tunnels will not be blocked by the firewall.

The query is largely the same as in my earlier post about detecting ICMP anomolies. However, i made one addition. Sancp also stores session duration information. In other words, how long did a session last. I figured that ICMP tunnels may have longer than average duractions, since they are likely to be used for hiding interactive sessions like ssh, irc or msn. One should remember however, that a continues ping command that (accidently) runs for a long period of time will also have a long duration.

Sguil Sensor Report Showing an ICMP Report

Five results. Three of which are (partly) ICMP tunnel traffic. Spotting them is easy. They all have the following properties:

  1. Non-standard average packet size. E.g. 81.81 bytes for the first connection is non-standard.
  2. Number of bytes in both directions are unequal.
  3. Average packet size in both directions is unequal.

The number of bytes transfered seems to be not a really good indicator in itself. The second result transfered 29 MB, the biggest number from these results, but was no tunnel. It was a ‘ping -f 192.168.0.102′ for a few minutes. The same goes for the duration. Sure the first tunnel has by far the longest duration, but if i keep a ping session running for a week it won’t misteriously turn into a tunnel ;-)

Still, i think it is useful to have the information around because once you suspect a tunnel exists, volume and duration might be further indicators something is wrong.

This is the Sguil report:

TOPTENICMP||Top 10 ICMP LAN->WAN: Top 10 ICMP users in the LAN talking to the WAN, with source and destination, volume and average packet size, sorted by packet size (fields: source ip, dest ip, from source MB, from source packet size, from dest MB, from dest packet size, total duration, average duration)||query||select INET_NTOA(src_ip), INET_NTOA(dst_ip), sum(src_bytes)/1048576 as from_src_bytes, sum(src_bytes)/sum(src_pkts) as from_src_avg_pktsize, sum(dst_bytes)/1048576 as from_dst_bytes, sum(dst_bytes)/sum(dst_pkts) as from_dst_avg_pktsize, SUM(duration) as my_total_duration, SUM(duration)/COUNT(*) as my_avg_duration FROM sancp IGNORE INDEX (p_key) INNER JOIN sensor ON sancp.sid=sensor.sid WHERE start_time > %%STARTTIME%% AND end_time < %%ENDTIME%% AND %%SENSORS%% AND ip_proto = 1 AND ((src_ip between INET_ATON("192.168.0.0") and INET_ATON("192.168.255.255")) or (src_ip between INET_ATON("10.0.0.0") and INET_ATON("10.255.255.255")) or (src_ip between INET_ATON("172.16.0.0") and INET_ATON("172.31.255.255"))) and ((dst_ip not between INET_ATON("192.168.0.0") and INET_ATON("192.168.255.255")) and (dst_ip not between INET_ATON("10.0.0.0") and INET_ATON("10.255.255.255")) and (dst_ip not between INET_ATON("172.16.0.0") and INET_ATON("172.31.255.255"))) GROUP BY src_ip,dst_ip ORDER BY from_src_avg_pktsize DESC,from_src_bytes DESC LIMIT 10||8||

I also added ‘IGNORE INDEX (p_key)’. I saw default queries in Sguil use that, and it seems to improve performance. On my data these queries might return results in a few seconds, one user working with a bigger set of data reported it running an hour and a half… oops! Let’s see if this works.

A logical next step will be to create a query that actually compares the to_src and to_dst values average packet sizes and total transfered volume. I hope to get to that in a few days.

Btw, if you find this stuff interesting, i suggest you take a look at Extrusion Detection by Richard Bejtlich.

Sguil: detecting ICMP anomolies

Friday, August 18th, 2006

Recently Websense discovered malware that caused quite a lot of media attention, because it used ICMP to send personal data out of the victims pc or network. In many networks, outgoing ICMP is unrestricted because it can aid in solving connectivity and routing problems. While (partially) blocking outgoing ICMP would certainly solve this problem, not everone will be willing to do so. Note however that the idea of using ICMP as a covert channel is hardly new.

Yesterday I wrote about creating custom Sancp queries and adding them to Sguil as canned reports. Today i tried to create a query that can be used to detect anomolies in ICMP traffic leaving your network. Like yesterday, i did this in the form of a ‘canned report’. One limitation is that there seems to be no way to get any information on exact ICMP codes and types.

I tried to create a query that would do the following. I assumed that there are at least two ways the traffic can stand out. Either by it’s volume, or by non-default packet sizes. Normally ping uses a fixed packet size of 64 bytes on my Linux system, Windows 2000 defaults to 32 bytes. If the packet size is not what you would expect, something fishy might be going on. So the query tries to find the execptional cases in this respect.

Volume is another factor. To tranfser substantial amounts of data through ICMP, the volume of the packets will need to be quite high as well. Note however that Linux’ ping utility for example pings until you tell it to stop, and it happened to me before that i accidently left it running an entire night, so a large volume doesn’t have to mean there is something wrong. The target IP is also very interesting. If it is IP belonging to Google there is a big chance that it is just a test. In the example below most volume is to 194.109.21.51 which is the server in my ISP’s network that can be reached under ‘ping.xs4all.nl’.

Sguil Sensor Report Showing an ICMP Report

In this report you can see that 192.168.1.2 pinged 145.97.193.148 with an average packet size of 1480 bytes, which is certainly not normal. It transfered 0.08MB to this IP. Another amount of 2.24MB was transfered to the IP 194.109.21.51, with a average packet size of 64.33 bytes. This is weird because it is not exactly 64 bytes, what I would expect. This can be explained by me running a default ping for a long time and after that doing a few tests with larger sizes.

(more…)

Sguil: creating custom reports

Thursday, August 17th, 2006

In the Sguil NSM system, Sancp plays a vital role. Sancp records session data, in which all connections are recorded. For all connections (and pseudo connections, think udp, icmp), Sancp records the number of bytes transfered, number of packets, start and end time, etc. This is very much useful information, of which Sguil only makes a subset accessable. Because the information is stored in a MySQL database, nothing prevents you from querying the database manually, which is what i did. However, David Bianco suggested that i could also add them as ‘canned reports’ to Sguil, which i did.

At this stage i am mostly interested in the information from Sancp about the traffic volume. Which host(s) use the most bandwidth? Questions like this. Below i explain one of the ‘canned reports’ i created.

Report View

This is the output of the query below. What it shows is that 192.168.1.2 in my lan downloaded 367MB from 145.97.193.148, and that it did this via http (protocol 6 is tcp, port is 80). Let’s have a look at the query.

TOPTENWAN2LAN_SRC_DST_SER||Top b/w per serv. from WAN to LAN (downloads), flow from WAN-IPs to LAN-IPs, volume in MB||query||select sum(dst_bytes)/1048576 as my_dst_bytes, INET_NTOA(dst_ip), INET_NTOA(src_ip), ip_proto, dst_port from sancp INNER JOIN sensor ON sancp.sid=sensor.sid WHERE start_time > %%STARTTIME%% AND end_time < %%ENDTIME%% AND dst_bytes > 0 AND %%SENSORS%% and ((src_ip between INET_ATON(”192.168.0.0″) and INET_ATON(”192.168.255.255″)) or (src_ip between INET_ATON(”10.0.0.0″) and INET_ATON(”10.255.255.255″)) or (src_ip between INET_ATON(”172.16.0.0″) and INET_ATON(”172.31.255.255″))) and ((dst_ip not between INET_ATON(”192.168.0.0″) and INET_ATON(”192.168.255.255″)) and (dst_ip not between INET_ATON(”10.0.0.0″) and INET_ATON(”10.255.255.255″)) and (dst_ip not between INET_ATON(”172.16.0.0″) and INET_ATON(”172.31.255.255″))) GROUP BY src_ip,dst_ip,ip_proto,dst_port ORDER BY my_dst_bytes DESC LIMIT 10||5||

Copy-paste this into your /etc/sguild/sguild.reports and restart sguild. It should be available in the Sensor Reports window. The parts between percent signs are Sguil variables. The double pipes are separators between different sections of the line. The line starts with a sort of internal name for Sguil. What follows is the description as it will be show in the report selection screen and in the report itself. Next it is indicated that his is a query. After that the query itself. After the query in the last field it is indicated how many columns of data Sguil can expect from MySQL.

Now the query. First, i select dst_bytes, dst_ip and src_ip, ip_proto and dst_port. The dst_bytes field contains the number of bytes flowing from dst_ip to src_ip. Because we use group later, we SUM dst_bytes. And because it is in bytes, we divide it by 1024*1024(=1048576) so the result will be megabytes. The result is stored in my_dst_bytes. We then make sure that the src_ip is in a private ip range and that dst_ip is not in a private ip-range. Then the grouping is done, followed by the ordering by my_dst_bytes. Easy huh!

Naturally, this only works for networks that actually use private ip-ranges, but it should be easy to adapt if you use something else. Below, i have added another seven of these reports.

(more…)

Sguil: full content logging in combination with Snort_inline, revisited *again*

Thursday, August 10th, 2006

Note to self: never assume something works, instead, test it.

Yesterday there was some discussion in the #snort channel over whether or not passing multiple interface to snort works or not. As a reminder, some time ago i noted that passing two interfaces to snort like this: ’snort -i eth0:eth1′ worked just fine. However, common mentioned in irc that he could not imagine it to be working. Determined to proof him wrong, i decided to run a few test. On my gateway, i ran ’snort -v -i eth0:eth1 ip proto 1′. This should print all ICMP packets to the screen for both interfaces. The first clue that something wasn’t right was this message:

OpenPcap() device eth0:eth1 network lookup:
eth0:eth1: no IPv4 address assigned

Anyhow, i continued a pinged the gateway from the eth0 network. Worked fine. Then from the eth1 network. No dice. Damn. So i switched the interfaces like this ’snort -v -i eth1:eth0 ip proto 1′. Guess what? Now eth1 worked and eth0 didn’t.

This was about the time i also remembered that in Sguil i had a few cases where i didn’t get any data in a transscript, although it should have been there. Because i am very busy, i had not yet investigated it.

On #snort a few more people said that it didn’t work for them, but for Joel Esler it still does. Weird. Anyhow, i am now back to running sancp and the full content logger on the ‘any’ pseudo interface, both with a BPF filter excluding local loopback from being recorded. So that look like this ’snort -v -i any not host 127.0.0.1′.

What remains is the nat issue, but i have decided that i also want the full content logging on the wan side, so i think there is no solution for the double recording of natted connections.

Sguil: full content logging in combination with Snort_inline, revisited

Sunday, July 30th, 2006

A few days ago i wrote about some challenges that my Snort_inline presented. Especially the full content logging wasn’t working quite as i would have liked. Logging on pseudo device ‘any’ didn’t work right because then the traffic that was NAT-ted was both recorded before NAT and after NAT. The solution I (with help of #snort-gui) came up with was using ‘-i any’ anyway, but exclude my public ip using a BPF filter. Later i saw Joel Esler write the solution in a unrelated problem to someone else. Sometimes solutions can be so simple!

Solution: passing -i eth0:eth1 to snort… Duh! Thanks Joel!