If you tell enough stories, perhaps the moral will show up.


Die Daylight Saving Die! Die! Die!

Hurrah! The log files make sense again, and the gods of time have punished the impious humans for pretending that the solar zenith is 13:00....


Physical Insecurity

A frisson walking across the fields on my way home this evening -- that lively sound of bullets wheeling past my head. It wasn't a demented assassin emerging from my ugly past -- the faint red light gave it away as an incompetent lamper with a silenced rifle killing rabbits behind Forstal farm. He carried on firing as I walked out of danger even though I was shining my torch at his likely location. Once I'd got to the safety of the lane I walked along to find out what was going on, and encountered a man claiming to be Shay Harbour(?) He knew about the footpath, he said, and thought his line of fire would be OK.

Any more of that sort of thing, and I'm getting a 50mW green laser and a night vision scope -- after this shone down his bins he'd be hard put to tell up from down, let alone fire his weapon.


Read a Text File with Comments

This is a little Perl idiom to open a text file and read all the non-comment lines.

Key parts are

  • <LINELIST> yields all the lines in the filehandle into the default scalar $_
  • s/^\s*//; s/\#.*$//; s/\s*$//; removes (==substitutes nothing in place of) first any space at the front of the line second any trailing comment and third any trailing space in $_
  • chomp strips the newline from $_
  • next if /^$/ skips past the rest of the loop processing if $_ is empty (it matches beginning of line immediately followed by end of line)
All of this could be directed to a named scalar variable, but using the default pays off in compact code that can be cut and pasted elsewhere.
my $listfn="linelist.txt";
open LINELIST, "<$listfn" or die "Open $listfn $!";
while (<LINELIST>) {
    s/^\s*//; s/\#.*$//; s/\s*$//; chomp; next if /^\s*$/;
    print "$_\n"; # Or whatever else you want to do.....
Obviously printing is a bit dull -- instead you could drop the lines into an array:
push @linelist, $_;
Use grep to search it:
@results = grep((/$mypatt/i), @linelist);
The i after the pattern makes for case insensitivity, and you have to
use locale;
to get it right for all character sets.



This is a perl script that shows what perl does well. It takes a couple of programs which provide useful output -- nmap and nbtstat -- and combines them into a single daily archive showing what's on your network. Key features are:

  • The input is a list of named IP ranges.
  • The output is written to hosts.txt and yyyy-mm-dd.hosts.txt automatically building an archive of what's up in the networks you care about.
To run it
  1. Install nmap and make sure it's in the path. nbtstat is part of any Windows installation.
  2. Install Perl, which means Active State.
  3. Create a range file that's just a text file with a few lines like these: (Obviously with your network ranges. You can use any range specification that nmap will accept.)
      # my range.txt 
  4. Make sure your reverse DNS is working, or you won't get any DNS names.
  5. Run it with a command like c:\perl\bin\perl nmap.pl range.txt
  6. check the output in hosts.txt to see server names, and, for windows servers, domain and server names.
To get the benefit, set it to run every day using a scheduled task (I find this is easiest with a .CMD file containing full path names for everything) and in a few months you'll have some worthwhile history.

# Start of nmap.pl by umacf24
# This program takes a single parameter -- a file containing  pairs.
# Example (but leave off the #)
# servers
# perimeter
use strict;
use warnings;

# NBTSTAT codes from Jim Halfpenny
my %group  = (  hex("00"), "00 Dom",
  hex("01"), "01 M Browser",
  hex("1C"), "1C Domain Controller",
  hex("1E"), "1E S Browser", # Browser Elections
my %unique = (  hex("00"), "00 WS",    # Workstation service
  hex("01"), "01 Msgr", # Messenger Service
  hex("03"), "03 Msgr", # Messenger Service
  hex("06"), "06 RAS Server Service",
  hex("1B"), "1B Domain Master Browser",
  hex("1D"), "1D Master Browser",
  hex("1F"), "1F NetDDE Service",
  hex("20"), "20 SVR", # File Server Service
  hex("21"), "21 RAS Client Service",
  hex("22"), "22 MS EXC Interchange(MSMail Connector)",
  hex("23"), "23 MS EXC Store",
  hex("24"), "24 MS EXC Directory",
  hex("30"), "30 Modem Sharing Server Service",
  hex("31"), "31 Modem Sharing Client Service",
  hex("43"), "43 SMS Clients Remote Control",
  hex("44"), "44 SMS Administrators Remote Control Tool",
  hex("45"), "45 SMS Clients Remote Chat",
  hex("46"), "46 SMS Clients Remote Transfer",
  hex("4C"), "4C DEC Pathworks TCPIP service on Windows NT",
  hex("42"), "42 McAfee AV",
  hex("52"), "52 DEC Pathworks TCPIP service on Windows NT",
  hex("87"), "87 MS EXC MTA",
  hex("6A"), "6A MS EXC IMC",
  hex("BE"), "BE Netmon Agent",
  hex("BF"), "BF Netmon Application",

# results stored in an array for re-use
my @nmapout;

my $ofn='hosts.txt';
my $opath='.\\ip\\';

my $header = "Key\t++ Up but unnamed, \n\t** Named but not responding to ping.\n\t?? Improper NBT names\n";
$header .= "Ping scan run between ".localtime(time())." ";

my $ipall; my $ipup; my $ipdown; # Address counters

# Process the file of network names. Run NMAP against the network spec (iprange).
while (<>)
  next if (/^\s*#/) ;
  my @line = split /\s+/;
  if (2==@line)
      my $ipname = sprintf "%-15.15s ", $line[0]; # Fixed width
      my $iprange = $line[1];      

      my $cmd = ".\\bin\\nmap -sP -R -oG - $iprange |";    # local nmap
      print STDERR "$cmd\n";
      open (NMAP, $cmd);
      while ()
          next if /Smurf/;
          if (/^Host: /)
              my $hostline=$_;
              $ipdown++ if ($hostline =~ /Status: Down$/);
              $ipup++ if ($hostline =~ /Status: Up$/);
              my $flag ='   ';
              $flag = '++ ' if ($hostline =~ /\(\)\tStatus: Up/) ;# Un-named
              $flag = '** ' if ($hostline =~ /\([^)]+\)\tStatus: Down/) ;# Named, but not responding
              my $hostip = $1 if ($hostline =~ /^Host: ([0-9\.]+) .+ Up$/);
              my $prefix=$ipname . $flag;
              my $postfix=nbtstat($hostip) if ($hostip);
              $hostline =~ s/^Host: /$prefix/;
              $hostline =~ s/Status: Up$/NBT: $postfix/ if ($postfix);
              # print STDERR "Host Line $hostline | $postfix | $hostip\n";
              push @nmapout, $hostline;
$header .= "and ".localtime(time())."\n";

if ($ipall)
{    # Some addresses seen
  $header .= "$ipall addressses pinged:- Up: $ipup, Down: $ipdown\n";
  $header .= "No addresses scanned. NMAP in path?\n";

# Preserve an archive Version
my ($sec, $min, $hr, $day, $mon, $yr, $wday, $yday, $isdst) = localtime(time());
$yr = $yr + 1900;
$mon = $mon + 1;
my $prefix = sprintf "%04d-%02d-%02d.", $yr, $mon, $day ;
my $afn="$opath$prefix$ofn";
my $hfn="$opath$ofn";

#Don't overwrite existing hosts file until we have finished scanning.
open (AHOSTS, ">$afn") or die "could not open $afn $!";
open (HOSTS, ">$hfn") or die "could not open $hfn $!";
print AHOSTS $header; print HOSTS $header;
foreach my $hostline (@nmapout)
  print HOSTS "$hostline\n";print AHOSTS "$hostline\n";
close HOSTS; close AHOSTS;

sub nbtstat
  my ($hostip) = @_;
  my $cmd = "nbtstat -a $hostip |";
  my %survey; my $ret;
  my $domain; my $server;

  # print STDERR "survey $cmd\n";
  open (NBTSTAT, $cmd) or die "can't run $cmd $!";
  while ()
      # print STDERR;
      # The meat of the NBTNAME command is in this format: name  type
      if (/^\s+(\S+)\s*<([0-9A-F]{2})>\s+(GROUP|UNIQUE)/i )
          my $nbtname = $1;
          my $nbtclass = $2;
          my $nbttype = $3;
          if ('00' eq $nbtclass)
              $domain = $nbtname if ('GROUP' eq $nbttype);
              $server = $nbtname if (('UNIQUE' eq $nbttype) && ($nbtname !~ /(\~)|(MSBROWSE)/));
          my $desc = ('GROUP' eq $nbttype)? $group{hex($nbtclass)} : $unique{hex($nbtclass)};
          $desc = "$nbtclass $nbttype Unknown" unless $desc;
          # print STDERR "$nbtname $nbtclass $nbttype $desc \n";
          push (@{$survey{$nbtname}}, $desc);
  close NBTSTAT;
  # We should have a good domain name and a good server if we have anything
  if (%survey)
      $ret = ($domain and $server) ?  "$domain\\$server" : '??';
  foreach my $nbtname (keys %survey)
      $ret .= " $nbtname: ("  . join (', ', @{$survey{$nbtname}} ). ")" unless ($nbtname =~ /(\~)|(MSBROWSE)/) ;
  return $ret;
# End of nmap.pl



Here's the Interpol (rare to see a .int domain) release on operation Vico. That's impressive work by the image experts, but I think the credit lies with the investigator who had the imagination to see that information was still there and the thing was worth trying. That's the leap I would have failed to take, and it's the difference between a proper investigator and the guy who looks at proxy logs.

After red-eye reduction, obscuring faces is probably the second most popular use for home image processing, the home porners generally botch it, and I've often wondered why packages don't include a better tool. Perhaps they can't find a suitably euphemistic name. Anyway, I suppose we'll now see a slew of unswirl tools.


The Approver as a Conceptual Bottleneck

I'm looking at a list of domains groups which control access to removable devices and media through Pointsec Protector (which used to be Reflex Magnetics Disknet Pro). We've had groups for various types of devices and now I'm trying to simplify -- to operate a much cruder level of control.

I'd prefer to leave it as it is, but the membership of the current groups is a mess. The technology is fine, but the control environment stinks. At present we allow or deny access to the groups based on a managers approval: "he has a business need to use a USB key" -- and that makes a good deal of sense. Who else can make that choice?

Who else indeed? Because the managers aren't technical -- so they don't understand what they're approving -- and they don't see any downside from insecure access. Essentially every request gets approved. And in six months time when the the lists have to be recertified, it's probably a different manager, or the original justification is forgotten, and it's easier just to agree. We've got adequate technology, and a process that the auditors think is just fine, and there's no real control at all.

Of course, this isn't just a problem for USB devices. It very easy to fail at this last hurdle by asking approvers to use a discretion that they just can't understand.

I've thought about making these accesses part of the permissions attached to the job description global groups. But that doesn't reduce the problem unless IT security can engage with the role definition approvers, and we don't.

So this is my plan: I'm going to name the groups after the risk, with alarming group names and descriptions:
Risk In -- "Trusted to read data from unknown sources"
Risk Out -- "Trusted to send corporate data to unknown destinations"
Device Risk -- "Trusted to attach untrusted and untested devices"

Let's see whether that gets the message across.


War Against the Aliens

In Arthur C. Clarke's Rendevous With Rama, the world of the 23rd century prepares for a visit by potentially hostile aliens. A minor character, a general, justifies military preparations by claiming that even though humans have complete dominion over a wasps nest, we still leave it alone if we possibly can. So even though the aliens may be vastly more powerful than we are, they would still care enough about nuclear missiles to leave us alone.

I suppose that's comforting. But it's not very comforting. The wasps nest I destroyed on Sunday afternoon had little idea who I was, and no idea why I was attacking. Alas! they didn't know I was afraid of their stings and I would have left them alone if only they weren't so dangerous. Even worse, the means chosen by the superior alien -- me -- was an organophosphorus agent: nerve gas. We might be better off without our stings.