30 October, 2007

Better Work Means More Work

Security is one of those fields where, the better a job you do, the more work you have to do. To some extent, all jobs are like this. If you do a terrible job, nobody is going to want to give you more work. If you do a great job, people will give you as much or more work than you can handle. However, this is not really the type of extra work I am referring to.

One example of what I'm talking about is intrusion detection. The better you get at intrusion detection, the more incident response you will end up doing as a result. Getting better at security operations in general will often lead to the discovery of more intrusions as your knowledge increases, new systems are implemented, and security systems are improved. Someone who is good at penetration testing or application fuzzing may be able to find and exploit more vulnerabilities, and in the end do extra work because of that. I'm sure there are many more examples.

On the other hand, better work also means you can streamline processes or reduce the number of security incidents. Increasing the depth of your defense to reduce security incidents, automating processes, more clearly mapping processes, and more efficiently achieving an objective are all possibilities to reduce the amount of work. Being better at penetration testing may mean finding more useful information about the security of your target, but you may also perform the actual penetration test more quickly.

Richard Bejtlich likes to point out that prevention eventually fails. I agree but would like to add that I think there will always be security incidents that are missed. Getting better at detection means more time spent on incidents, which is a good thing by the way. However, no matter how good you get at detection, I firmly believe that nobody will catch everything worth catching. There are probably exceptions, but in the type of enterprise network I'm used to dealing with, catching every single noteworthy security incident seems unlikely.

Someone in operational security can also improve prevention. Just because prevention eventually fails doesn't mean it never works or should be ignored. You might think that getting better at prevention means you will have less work to do when it comes to detection and response. But better prevention generally means more work on design, testing, configuration, maintenance, documentation, and more.

Anyone that does a good job will be in demand, leading to more work. With security, I also think doing a good job means you may discover more work to do along every step of the way.

23 October, 2007

Upgrading to Snort 2.8.0

I finally upgraded my test sensor from Snort 2.6.1.5 to Snort 2.8.0. David Bianco mentioned some of the new features in his blog a couple of months ago, so I won't get into the differences. I am mainly documenting the things I had to do for the upgrade so I have a reference if needed at a future date.

The first thing I did was look through the documentation in the "docs" directory, reading some of the README files for the preprocessors. The README.variables file was of particular interest since Snort 2.8 allows port lists. I also looked at the snort.conf file in the "etc" directory of the source tree to see how it differed from my current configuration file.

Next, I made a copy of my snort.conf from 2.6.1.5 and edited it for the changes in 2.8.0. I changed the HTTP_PORTS variable to list a few other ports besides 80, including 8080 and 8000. The portvar variable was used in the examples of multiple HTTP_PORTS.

portvar HTTP_PORTS [80,8000,8080,8888]
Although I only run HTTP on port 80, the Snort web-client ruleset and a number of Bleeding rules use the $HTTP_PORTS variable to detect attacks against web clients like Internet Explorer, Mozilla Firefox, media players, and more. After that simple change, I configured and installed Snort.
./configure --enable-dynamicplugin --enable-inline --enable-perfprofiling
make
make install
After installing, I tried to start Snort. The first problem I encountered was with my stream5 configuration. I had previously been using the stream4 and the flow preprocessors, but when changing the configuration to use stream5 I had not removed the flow preprocessor configuration. Stream5 handles everything that used to be handled by the combination of flow and stream4, so I removed the flow configuration. I also had to add the stream5_udp and stream5_icmp options.

A check of the configure help will also show that --enable-dynamicplugin is the default with 2.8.0, so it should not actually be needed in the configuration command.

After fixing stream5, I tried again and had one more problem. I was getting the following errors:
Oct 23 19:18:52 sensor snort[3117]: /etc/snort/snort.conf(206) unknown dynamic preprocessor "ftp_telnet"
Oct 23 19:18:52 sensor snort[3117]: /etc/snort/snort.conf(210) unknown dynamic preprocessor "ftp_telnet_protocol"
Oct 23 19:18:52 sensor snort[3117]: /etc/snort/snort.conf(221) unknown dynamic preprocessor "ftp_telnet_protocol"
Oct 23 19:18:52 sensor snort[3117]: /etc/snort/snort.conf(226) unknown dynamic preprocessor "ftp_telnet_protocol"
Oct 23 19:18:52 sensor snort[3117]: /etc/snort/snort.conf(238) unknown dynamic preprocessor "smtp"
Oct 23 19:18:52 sensor snort[3117]: /etc/snort/snort.conf(307) unknown dynamic preprocessor "dcerpc"
Oct 23 19:18:52 sensor snort[3117]: /etc/snort/snort.conf(313) unknown dynamic preprocessor "dns"
Oct 23 19:18:52 sensor snort[3117]: FATAL ERROR: Misconfigured dynamic preprocessor(s)
This was pretty easy to fix. I just needed the proper path to the dynamic preprocessors.
dynamicpreprocessor directory /usr/local/lib/snort_dynamicpreprocessor/
After I fixed the snort.conf, the next try to start Snort 2.8.0 was successful. Now that I have it installed and running with very similar settings to 2.6.1.5, it's time to dig deeper into the differences and possibly test other configuration changes.

16 October, 2007

Sguil 0.7.0 Client and NetBIOS Names

The previous versions of Sguil clients used a different method when doing a reverse DNS lookup. This method must have fallen back to the operating system's name lookup methods at some point, because I used to get NetBIOS names on systems that had them when there was no DNS name returned. The Sguil 0.7.0 client uses tcllib's DNS client to resolve names, which is used to allow DNS proxying through OpenDNS or other DNS servers. However, this method is purely DNS, so any Windows system without a DNS entry would return "Unknown" as the hostname. I decided to play with the client and add NetBIOS name queries in the event the reverse DNS came up empty.

The file that contains the GetHostbyAddr proc in Sguil is extdata.tcl and lives in the lib directory. From the description in the file:

#
# GetHostbyAddr: uses extended tcl (wishx) to get an ips hostname
# May move to a server func in the future
#

The proc does a few things. First, it checks to see whether external DNS is enabled and whether an external server has been configured in sguil.conf. If an external DNS server is set, then the process will see if a HOME_NET is set. If the HOME_NET is set, it is compared to the IP being resolved. A match means that the nameserver is set to the local rather than the external. If HOME_NET is not set or the IP does not match HOME_NET, then the external nameserver is used. If external DNS is not selected in the client, then the local nameserver is used.

If the name resolution returns no value, then the client displays "Unknown" as a result. Just prior to that is where I added the NetBIOS name lookup. Here is the whole GetHostbyAddr proc after I modified it, with a few comments to follow:

proc GetHostbyAddr { ip } {

global EXT_DNS EXT_DNS_SERVER HOME_NET NETBIOS_NET

if { $EXT_DNS } {

if { ![info exists EXT_DNS_SERVER] } {

ErrorMessage "An external name server has not been configured in sguil.conf. Resolution aborted."
return

} else {

set nameserver $EXT_DNS_SERVER

if { [info exists HOME_NET] } {

# Loop thru HOME_NET. If ip matches any networks than use a the locally configured
# name server
foreach homeNet $HOME_NET {

set netMask [ip::mask $homeNet]
if { [ip::equal ${ip}/${netMask} $homeNet] } { set nameserver local }

}

}

}

} else {

set nameserver local

}

if { $nameserver == "local" } {

set tok [dns::resolve $ip]

} else {

set tok [dns::resolve $ip -nameserver $nameserver]

}

set hostname [dns::name $tok]
dns::cleanup $tok

# Added hack to use NetBIOS name lookups if no DNS entry
if { $hostname == "" } {

# Only check NETBIOS_NET addresses for NetBIOS names
if { [info exists NETBIOS_NET] } {

# Loop thru NETBIOS_NET. If ip matches then do NetBIOS lookup
foreach netbiosNet $NETBIOS_NET {

set netMask [ip::mask $netbiosNet]
if { [ip::equal ${ip}/${netMask} $netbiosNet] } {

# NetBIOS for Windows client
if { $::tcl_platform(platform) == "windows" } {

regexp {.+?(.{15})<00>} [ exec nbtstat -a $ip ] \
dummyvar hostname }

# NetBIOS for Unix client but you need samba client tools
if { $::tcl_platform(platform) == "unix" } {

# Match 16 chars because of trailing space w/nmblookup
regexp {.+?(.{16})<00>} [ exec nmblookup -A $ip ] \
dummyvar hostname }

}

}

}

}

if { $hostname == "" } { set hostname "Unknown" }
return $hostname

}

I took Bamm's check for HOME_NET and changed it to a check for NETBIOS_NET. I think people will definitely not want every IP to get a NetBIOS query if it has no DNS entry, nor will they want every IP in HOME_NET to be checked. This change means you need to add a NETBIOS_NET to sguil.conf if you want to resolve NetBIOS names.

Next, I checked for the platform and either perform an nbtstat from a Windows client or an nmblookup from a Unix client.

The regular expression I used to grab the result of the query is sort of weak, but functional. It simply finds the first instance of "<00>" and grabs the preceding 15 characters on Windows or 16 characters when doing a nmblookup on Unix. This is because the output of nmblookup has an extra space after the NetBIOS name, which can be up to 15 characters, while a 15 character name on Windows will have no trailing space. I would like to cut out any trailing whitespace using the regular expression, but I'm not sure about the best way to do it. A word boundary won't work because NetBIOS names can have characters like dashes and dots.

Suggestions and feedback welcome. Is something like this useful? How can it be improved? Is there some much easier way to do this? I can barely spell TCL let alone code it, so be gentle!

Edit: giovani from #snort-gui gave me this regexp that should grab the NetBIOS name without including trailing whitespace. He also pointed out that having spaces between the braces and the regexp, as I did, can alter the regexp.
{([^\s\n\t]+)\s*<00>}