HOWTO: Monitor the Windows XP Firewall

Introduction

Most Windows users are blissfully unaware that Windows XP has a perfectly serviceable (and actually pretty damn good) firewall, and when pressed for security, will actually go out and buy (or download) some trashy piece of software that boasts real-time monitoring, alert popups or such similar nonsense that they'll never actually use and that they switch off after the first half hour.

However, even techies who know about the XP firewall (and curse Microsoft by not switching it on by default on all interfaces) often ignore the fact that it provides pretty comprehensive logging. Oddly enough, most techies are averse to logging, on the spurious grounds that they are only (and even then seldom) useful post facto.

Well, that's not quite the case with the XP firewall. With a few lines of Perl and some ANSI magic, it can be turned quite easily into a comprehensive (and near-real-time) monitor of host activity you can leave running on a console window.

Setting Things Up

Well, I assume you already know how to turn on your firewall. You actually have it on this very minute, right?

(pause for effect)

OK. What you probably didn't do is turn on logging. To do that, open your network connection properties and go to the Advanced tab. On my SP1 XP box, there is a Settings button on the Advanced tab that causes a second property sheet to appear with three tabs:

  • Services - where you pick which TCP or UDP ports you want open (if any)
  • Security Logging - the only place you actually want to go today
  • ICMP - ICMP handling (ping request handling for you less technical folk)

Go into Security Logging and switch on logging for both dropped packets and successful connections. Why? Because you want to see what your own machine is doing, that's why.

And that's it. Your machine should be logging to C:\WINDOWS\pfirewall.log, and the file (which you can look at with notepad) has the following format:

date time action protocol src-ip dst-ip src-port dst-port size tcpflags tcpsyn tcpack tcpwin icmptype icmpcode info

The format is (in Microsoft fashion) written acrosss the first line in the log file, so if this changes with SP2 it should be enough to just restart the firewall and look at the new log file to figure out any changes to the script.

As to log rotation, it's handled automatically for you. And you don't need to worry about the log file size - I have had mine at 4096MB for a year now with no ill effects, and the extra information has proven quite interesting every time I visit new networks.

The Script

Now for the actual code. Since I use Cygwin, I used the built-in Perl distribution with the tail -f command (which takes advantage of native file update notifications) to follow log file updates and format them with ANSI Escape Codes:

#!/bin/perl
# $Id: fw.pl,v 1.4 2004/03/17 11:11:38 rcarmo Exp $
#
# ANSI pretty-printer for the Windows XP firewall log file.

# Known ports, color-coded:

my %gaPorts = ( 80 => "http",
             53 => "\033[32mdns\033[0m",
             22 => "\033[31mssh\033[0m",
             23 => "\033[1;33;41mtelnet\033[0m",
             25 => "\033[33msmtp\033[0m",
             67 => "\033[31mdhcp\033[0m",
             110 => "\033[31mpop3\033[0m",
             135 => "\033[32mms-rpc\033[0m",
             137 => "\033[32mnbt-ns\033[0m",
             139 => "\033[32mnbt-ses\033[0m",
             143 => "\033[31mimap4\033[0m",
             161 => "snmp",
             445 => "\033[32mms-ds\033[0m",
             993 => "\033[31mimap4s\033[0m",
             1080 => "socks",
             1900 => "\033[33mupnp\033[0m",
             5101 => "\033[33myahoo\033[0m",
             6000 => "\033[1;33;41mX\033[0m",
             8245 => "no-ip" );

# Log Fields:
# date time action protocol src-ip dst-ip src-port dst-port size tcpflags tcpsyn tcpack tcpwin icmptype icmpcode info

# Set the terminal title
print "\033]0;Firewall log\007\033[0m";

# Open the log file - use tail to follow (native filesystem locks)
open( TAIL, "tail -f /mnt/c/WINDOWS/pfirewall.log|" );

while(<TAIL>) {
  if( /DROP UDP/ ) { next; } # too many to be meaningful
  s/DROP\ UDP/\033[34mDROP\033[0m\ UDP/;
  s/DROP\ TCP/\007\033[5;1;36;44mDROP\033[0m\ TCP/;
  s/DROP\ ICMP/\007\033[5;1;36;44mDROP\033[0m\ \033[36mICMP\033[0m/;
  # Logging failed for a few packets:
  s/INFO-EVENTS-LOST - - - - - - - - - - - - ([0-9]+)/\007\033[5;1;33;41mLOST\033[0m\ $1/;
  s/OPEN\ /\033[32mOPEN\033[0m\ /;
  # Someone is connecting to us:
  s/OPEN-INBOUND TCP/\007\033[5;1;33;41mOPEN\033[0m\ TCP/;
  s/CLOSE/\033[31mCLOSE\033[0m/;
  s/TCP/\033[33mTCP\033[0m/;
  @aFields = split();
  $szSource      = $aFields[4] . ":" . $aFields[6];
  $szDestination = $aFields[5] . ":" . $aFields[7];
  if( !/LOST/ ) {
    $szBuffer  = sprintf( "%s %s %s\t%21s -> %21s", $aFields[1], $aFields[2], $aFields[3], $szSource, $szDestination );
  }
  else {
    $szBuffer  = sprintf( "%s %s %s", $aFields[1], $aFields[2], $aFields[3] );
  }

  print $szBuffer;
  # Lookup known protocols from the hash table above
  if( $gaPorts{$aFields[7]} ) {
    print " (" . $gaPorts{$aFields[7]} . ")";
  }
  print "\n";
}

The output looks pretty much like this (but with pretty colors):

11:00:27 DROP TCP             10.0.0.10:2232 ->  192.168.0.21:3281
11:00:28 OPEN TCP          192.168.0.21:3149 ->       10.0.0.10:80 (http)
11:00:28 CLOSE TCP         192.168.0.21:3163 ->      10.0.0.50:135 (ms-rpc)
11:00:28 CLOSE TCP         192.168.0.21:3164 ->     10.0.0.50:1059
11:00:28 CLOSE TCP         192.168.0.21:3165 ->      10.0.0.32:135 (ms-rpc)
11:00:28 CLOSE TCP         192.168.0.21:3166 ->    10.0.0.32:37645
11:01:29 CLOSE TCP         192.168.0.21:3150 ->       10.0.0.10:80 (http)
11:01:29 CLOSE TCP         192.168.0.21:3189 ->       10.0.0.10:80 (http)

Notes

The above is mostly a hack and can be refactored in a number of ways, but here are some notes:

  • I ignore DROP UDP log lines, because they are dime a dozen (mostly due to Microsoft file sharing, which tends to clog up any bandwidth you throw at it). The formatting regexp for it is there for the sake of completeness.
  • The firewall occasionally loses (or fails to log) packets. This is normal under heavy CPU load, and isn't really Microsoft's fault. Don't complain.
  • You really should customize the above for what you want/need to see. tail the log file by hand for a while to get a feel for things. I have added most protocols I'm interested in (for instance, I like to know when the No-IP client sends updates, and it has a fixed port number), but I ignore a lot of stuff.

Further Reading

Odd Notes

If you need to flush the log and temporarily stop the firewall, net stop sharedaccess will stop the NAT/firewall subsystem.

Disclaimer

Please note that this does not necessarily make your machine more secure (although just switching the firewall on is a big help). There is no guarantee (express or implied) these instructions will work for you, nor will I be able to help you if anything goes wrong with your computer, your dog has kittens, etc.

Oh, and yeah, you can probably do pretty much the same with the Mac OS X firewall (it is, after all, BSD ipf), but I couldn't be bothered yet.