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>}

No comments:

Post a Comment