newer version of code red scanning script

Anne Bennett anne at alcor.concordia.ca
Wed Aug 22 14:20:11 GMT 2001


I append the "code red scanning script" that I modified from David
Dandar's original posting; at this point, several unisoggers have made
improvements.  The script now reports not only on the patch status of
IIS servers (and the type of any web server), but it also checks for a
few well-known defacements, it checks for the presence of root.exe,
and it checks for some of Code Red II's "mappings".  A lot of
debug/detail stuff comes out before the reports.

I also append the script I am using to send mail myself a summary of
the interesting information.  If you use it, make sure you modify it
to scan your own domain and not mine, and mail the report to yourself
and not me!

Improvements are always appreciated, though please post them to the
list, so they can be of use to everyone whether or not I get around to
incorporating them and reissuing the script in a timely manner...


Anne.
-- 
Ms. Anne Bennett, Senior Analyst, IITS, Concordia University, Montreal H3G 1M8
anne at alcor.concordia.ca                                        +1 514 848-7606
----------------------------------------------------------------------------
#!/usr/bin/perl -wT

# This program scans hosts on port 80 to check for unpatched versions
# of the Microsoft IIS web server, which are vulnerable to the
# "Code Red" worm released in July 2001.
#
# On stdin, it accepts a list of lines whose first element is 
# an IP address or a hostname to be scanned -- the rest of each line
# is ignored.

# 2001/07/20 David Dandar <ddandar at odu.edu>
#  - Original as per posting below.
# 2001/07/27 Anne Bennett <anne at alcor.concordia.ca>
#  - Patched for -wT and "use strict"
# 2001/07/30 Anne Bennett <anne at alcor.concordia.ca>
#  - Added mods from David Moore <dmoore at ipn.caida.org>, for
#    multi-lingual recognition of patterns.
# 2001/08/06 Anne Bennett <anne at alcor.concordia.ca>
#  - "/scripts/root.exe" variant
# 2001/08/16 Russell Fulton <r.fulton at auckland.ac.nz>
#  - "C and D mappings" variant
# 2001/08/17 Anne Bennett <anne at alcor.concordia.ca>
#  - Catch defaced machines as well.
#  - Switch to my indentation style.
#  - Combine all tests into one script.

# Date: Fri, 20 Jul 2001 15:45:54 -0400
# From: David Dandar <ddandar at odu.edu>
# To: unisog at sans.org
# Message-ID: <20010720154554.B8261 at kataan.usg.odu.edu>
# Subject: [unisog] IIS vulernerability scanner tool
# 
# Below is a rough-cut perl script I whipped up to test for vulnerable IIS
# servers.  I can offer no guarantees, but it seems to be reporting good info. 
# It's based on a report on Bugtraq that fingerprinted some different
# responses to an exploit attempt.  I know Microsoft has something out there
# that reports patch information, but this is an alternative method.
# 
# I recommend using nmap or some other method of finding hosts listening on
# port 80, then feed the IP addresses in on stdin, or via a file listing on
# the command-line to this script.  The output lines contain the first 4k of
# the response from the server, whitespace squished, so they can be very long. 
# The "cannot determine address" and "bad file descriptor" errors are failed
# connects.
# 
# It shouldn't crash anything, but you have been warned. :-)  Use at your own
# risk.  I recommend trying it on a few known hosts first.
# 
# David

use strict;

use IO::Socket;
use IO::Select;

my ( $host_id, $parallel );
my ( $connect_timeout, $response_timeout, $max_batch_timeout );
my ( $error_string, $out_format );
my ( $codered_query, $ida_file_path, $rootexe_query, $mappings_query );
my ( @vul_patterns, @defaced_patterns, $root_exe_present_pattern );
my ( $root_exe_date_pattern, $mappings_present_pattern );
my ( @hosts, @iis_servers );
my ( %codered_results, %server_types, %defaced_servers, %compromised_servers );


# ----------------------- configuration section ----------------------

# What to put in the "Host:" line of the query:
$host_id           = "IITS-test";

# How many hosts to scan in parallel:
$parallel          = 50;

# How many seconds to wait for a connection to each host:
$connect_timeout   = 15;

# How many seconds to wait for an answer to our HTTP query:
$response_timeout  = 20;

# How many seconds to wait, maximum, per batch of tests:
$max_batch_timeout = 30;

# ----------------------- configuration section ends -----------------

$error_string = '@@ERROR@@';
$out_format   = "%-8s %s: %s\n";  # Will map to "result type", "host", "result"

$codered_query = 'GET /NULL.ida?'.("x"x200).'=X HTTP/1.1';

$rootexe_query = 'GET /scripts/root.exe?/c+dir+/tc HTTP/1.0';

$mappings_query = 'GET /c/winnt/system32/cmd.exe?/c+dir HTTP/1.0';

$ida_file_path =
  "(?:[A-Za-z]:\\\\(?:[^\\\\]+\\\\)*)?NULL.ida";

@vul_patterns  = (
        # English
    "The IDQ file $ida_file_path could not be found\.",
    "File $ida_file_path\.    The system cannot find the path specified\.",
    "The file $ida_file_path is on a network share. IDQ, IDA and HTX files cannot be placed on  a network share\.",
        # Chinese
    "\xd5\xd2\xb2\xbb\xb5\xbd IDQ \xce\xc4\xbc\xfe $ida_file_path\xa1\xa3",
        # Korean
    "IDQ \xc6\xc4\xc0\xcf $ida_file_path\xc0\xbb\\(\xb8\xa6\\) \xc3\xa3\xc1\xf6 \xb8\xf8\xc7\xdf\xbd\xc0\xb4\xcf\xb4\xd9\.",
        # Taiwan "\xa7\xe4\xa4\xa3\xa8\xec IDQ \xc0\xc9\xae\xd7 $ida_file_path\xa1C",
        # Japanese
    "IDQ \x83t\x83@\x83C\x83\x8b $ida_file_path \x82\xaa\x8c\xa9\x82\xc2\x82\xa9\x82\xe8\x82\xdc\x82\xb9\x82\xf1\x82\xc5\x82\xb5\x82\xbd\x81B",
        # German
    "IDQ-Datei $ida_file_path nicht gefunden\.",
        # Italian
    "Impossibile trovare il file IDQ $ida_file_path\.",
        # French
    "Le fichier IDQ $ida_file_path n'a pas pu \xeatre trouv\xe9\.",
    "Le fichier IDQ $ida_file_path n'a pas pu \\&ecirc;tre trouv\\&eacute;\.",
        # Spanish ?
    "No se encuentra el archivo IDQ $ida_file_path\.",
        # Dutch ?
    "Kan het IDQ-bestand $ida_file_path niet vinden\.",
        # ?
    "O arquivo IDQ $ida_file_path n\xe3o p\xf4de ser encontrado\.",
    "O arquivo IDQ $ida_file_path n\\&atilde;o p\\&ocirc;de ser encontrado\.",
    "\xcd\xe5 \xf3\xe4\xe0\xeb\xee\xf1\xfc \xed\xe0\xe9\xf2\xe8 IDQ-\xf4\xe0\xe9\xeb $ida_file_path\.",
    "Det gick inte att hitta IDQ-filen $ida_file_path\.",
    "Nie mo\xbfna odnale\x9f\xe6 pliku IDQ $ida_file_path\.",
    "$ida_file_path\.   \xc6\xc4\xc0\xcf  \xc1\xf6\xc1\xa4\xb5\xc8 \xb0\xe6\xb7\xce\xb8\xa6 \xc3\xa3\xc0\xbb \xbc\xf6 \xbe\xf8\xbd\xc0\xb4\xcf\xb4\xd9\.",
    "$ida_file_path IDQ dosyas\xfd bulunamad\xfd\.",
    "Soubor IDQ $ida_file_path nebyl nalezen\.",
    "IDQ \xc6\xc4\xc0\xcf $ida_file_path\xc0\xbb\\(\xb8\xa6\\) \xc3\xa3\xc1\xf6 \xb8\xf8\xc7\xdf\xbd\xc0\xb4\xcf\xb4\xd9\.",
    "Finner ikke IDQ-filen $ida_file_path\.",
    "\xce\xc4\xbc\xfe $ida_file_path\xa1\xa3    \xcf\xb5\xcd\xb3\xd5\xd2\xb2\xbb\xb5\xbd\xd6\xb8\xb6\xa8\xb5\xc4\xc2\xb7\xbe\xb6\xa1\xa3",
    "IDQ \xc6\xc4\xc0\xcf $ida_file_path\xc0\xbb(\xb8\xa6) \xc3\xa3\xc1\xf6 \xb8\xf8\xc7\xdf\xbd\xc0\xb4\xcf\xb4\xd9\.",
    "\xc0\xc9\xae\xd7 $ida_file_path\xa1C    \xa8t\xb2\xce\xa7\xe4\xa4\xa3\xa8\xec\xab\xfc\xa9w\xaa\xba\xb8\xf4\xae|\xa1C",
    "N\xe3o foi poss\xedvel encontrar  ficheiro de IDQ $ida_file_path\.",
    "IDQ-filen $ida_file_path blev ikke fundet\.",
    "Az IDQ f\xe1jl \\($ida_file_path\\) nem tal\xe1lhat\xf3\.",
);

@defaced_patterns = (
    'http:\/\/www\.worm\.com',
    "Hacked By Chinese!",
);

$root_exe_present_pattern = '^HTTP/1.\d 200';
$root_exe_date_pattern =
  '(^\d\d\/\d\d\/\d{2,4}\s+\d\d:\d\d\w)\s+\d+,\d+\s+root\.exe';

$mappings_present_pattern = '^HTTP/1.\d 200';

# ----------------------- subroutines --------------------------------



# sub dobatch ( @hosts_array, \%results_hash, $querystring )
# Sends the requested HTTP query to each of the hosts specified,
# and puts the responses in the given hash.  If the "response"
# is some kind of failure to connect or timeout, this is indicated
# with an error string prepending the reason for failure.
#
sub dobatch(@%$)
{
  my $hostsref    = shift @_;
  my $resultsref  = shift @_;
  my $querystring = shift @_;

  my ( $next, $fd, @ready, $raw_msg );
  my ( @hosts, %hosts, %timeouts, $s );

  @hosts = @{$hostsref};  # Copy the list of hosts.

  $s=IO::Select->new();


  while ( @hosts )
  {
    while ( ($s->handles)<$parallel && @hosts )
    {
      # Start a bunch of parallel tests:
      # Connect to the given host and send it the given query.  Add it to
      # the list of hosts we'll be checking for a response.
      #
      my $host  = shift @hosts;
      printf $out_format, "DEBUG", $host, "contacting...";

      eval
      {
        local $SIG{ALRM}=sub { die("TIMEOUT on connect\n")};
        alarm($connect_timeout);
        if ( $fd=IO::Socket::INET->new(PeerAddr => "$host",
                                       PeerPort => 80,
                                       Proto=>"TCP")
           )
        {
          $fd->send("$querystring\nHost: $host_id\n\n");
          $fd->autoflush(1);
          alarm(0);
        }
        else
        {
          die("Failed to connect: $!"); # dies inside eval only
        }
      };
    
      unless ( $@ )            # i.e. if the eval above was successful
      {
        $s->add($fd);          # add the resulting filehandle to our list
        $hosts{$fd}=$host;     # remember which host that handle is for
        $timeouts{$fd}=time()+$response_timeout; # the host should respond
                                                 # in the next XX seconds.
      }
      else # if the eval (connect) failed, say why.
      {
        ${$resultsref}{$host} = $error_string."$@";
      }
    } # while; all tests in this parallel sub-batch are now launched.
  
    # Timeout management: our next batch of reads should wait only as long
    # as the earliest of our queued up timeouts.
    $next=time()+$max_batch_timeout;
    foreach $fd ($s->handles)
    {
      $next=$timeouts{$fd} if ( $timeouts{$fd}<$next );
    }
  
    # Read a batch of responses.
    @ready=$s->can_read($next-time());
    if ( @ready )
    {
      foreach $fd (@ready)
      {
        printf $out_format, "DEBUG", $hosts{$fd}, "receiving...";
        $fd->recv($raw_msg,4096);
        ${$resultsref}{$hosts{$fd}} = $raw_msg;
        delete $hosts{$fd}; delete $timeouts{$fd}; $s->remove($fd);
        $fd->close;
      }
    }
    
    # If any handles were not processed in the above read batch,
    # check them for timeouts, and if they are timed out, report and
    # delete them.
    foreach $fd ($s->handles)
    {
      if ( time()>$timeouts{$fd} )
      {
        ${$resultsref}{$hosts{$fd}} = $error_string."TIMEOUT on read";
        delete $hosts{$fd}; delete $timeouts{$fd}; $s->remove($fd);
      }
    }
  } # while
  
  # Make sure we don't get any leftovers after all our batches...
  sleep $max_batch_timeout if $s->handles;

  # Read last batch of responses.
  @ready=$s->can_read($max_batch_timeout);
  if ( @ready )
  {
    foreach $fd (@ready)
    {
      printf $out_format, "DEBUG", $hosts{$fd}, "receiving...";
      $fd->recv($raw_msg,4096);
      ${$resultsref}{$hosts{$fd}} = $raw_msg;
      delete $hosts{$fd}; delete $timeouts{$fd}; $s->remove($fd);
      $fd->close;
    }
  }

  # Deal with last batch (we hope!) of timeouts:
  foreach $fd ($s->handles)
  {
    if ( time()>$timeouts{$fd} )
    {
      ${$resultsref}{$hosts{$fd}} = $error_string."TIMEOUT on read";
      delete $hosts{$fd}; delete $timeouts{$fd}; $s->remove($fd);
    }
  }

  # There should be no more, but...
  foreach $fd ($s->handles)
  {
    ${$resultsref}{$hosts{$fd}} = $error_string."LEFTOVER at end of processing";
    delete $hosts{$fd}; delete $timeouts{$fd}; $s->remove($fd);
  }

} # sub dobatch



# ------------ main program ------------

# --- set up list of hosts to scan ---

# autoflush on stdout:
$|=1;

while (<>) # Take a list of hosts to scan on stdin.
{
  chomp;
  my $host;
  # Keep only the first element of the line, then check it properly.
  if (  /^\s*(\S+)(\s+.*$|$)/  )
  {
    $host = $1;  # WARNING: this is not correctly untainted yet!
    if (  $host =~ /^([\d\.]+)$/  ) # Looks like an IP address
    {
      if (  $host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/  )
      {
        if (  ( $1 >= 0 ) && ( $1 <= 255 ) &&
              ( $2 >= 0 ) && ( $2 <= 255 ) &&
              ( $3 >= 0 ) && ( $3 <= 255 ) &&
              ( $4 >= 0 ) && ( $4 <= 255 )    )
        {
          $host = "$1.$2.$3.$4";  # untainted
        }
        else
        {
          printf $out_format, "ERROR", $host,
            "line $.: IP addr element out of range 0-155";
          next;
        }
      }
      else
      {
          printf $out_format, "ERROR", $host,
            "line $.: malformatted IP address";
        next;
      }
    }
    else
    {                       # Looks liek a hostname
      if (  $host =~ /^([\w\-\.]+)$/  )
      {
        $host = $1;  # untainted
      }
      else
      {
        printf $out_format, "ERROR", $host,
          "line $.: bad characters in hostname";
        next;
      }
    }
  }
  else
  {
    printf $out_format, "ERROR", "UNKNOWN",
      "line $.: unparsable line: $_";
    next;
  }
  push(@hosts, $host);
} # while


# Perform a batch of code red queries, and analyze the responses.
# We'll look for defacements and note server types here too.
#
printf $out_format, "DEBUG", "-----", "starting codered batch";
{
  my ( $host, %query_responses );
  my ( $raw_msg, $msg, $pattern );

  dobatch ( \@hosts, \%query_responses, $codered_query );
  foreach $host ( keys %query_responses )
  {
    if ( $query_responses{$host} =~ /^$error_string(.*)$/ )
    {
      $codered_results{$host} = $query_responses{$host};
    }
    else
    {
      $raw_msg = $query_responses{$host};

      # Print out raw message for debugging.
      $msg=$raw_msg;
      $msg=~s/\s/ /g;
      chomp $msg;
      printf($out_format, "DETAIL-CODERED", $host, $msg);

      # Grab server type if available.
      #
      if ($raw_msg=~/^Server: ([\S\t ]+).*$/m)
      {
        $server_types{$host} = $1;
      }
    
      # Check for Code Red vulnerability for IIS servers only.
      #
      if ( $raw_msg=~/Microsoft-IIS\/(\d+\.\d+)/ )
      {
        my $version = $1;
    
        push @iis_servers, $host;
    
        # Figure out whether we have a patched or unpatched system based on
        # the response to the crafted "code red" query.
        #
        if ( $raw_msg=~/0x80040e14/ )
        {
          $codered_results{$host} = "patched IIS ($version)";
        }
        unless ( $codered_results{$host} )
        {
          foreach $pattern ( @vul_patterns )
          {
            if ( $raw_msg =~ /$pattern/ )
            {
              $codered_results{$host} = "UNPATCHED IIS ($version)";
            }
          }
        }
        unless ( $codered_results{$host} )
        {
          if    ( $raw_msg=~/404 Object Not Found/ && ($version ne "5.0") )
          {
            $codered_results{$host} = "Possibly UNPATCHED IIS ($version)";
          }
          elsif ( $raw_msg=~/HTTP\/1\.[01] 401 Access Denied/ )
          {
            $codered_results{$host} = "UNKNOWN IIS (401 Access Denied) ($version)";
          }
          else
          {
            $codered_results{$host} = "UNKNOWN IIS ($version)";
          }
        }
      } # if IIS, check vulnerability

      # Check for defaced servers.
      #
      foreach $pattern ( @defaced_patterns )
      {
        if ( $raw_msg =~ /$pattern/ )
        {
          $defaced_servers{$host} = $pattern;
        }
      }

    } # else (i.e., got a response)
  } # foreach host we tested
} # code red batch
printf $out_format, "DEBUG", "-----", "end of codered batch";


# Perform a batch of "root.exe" checks, only on identified IIS
# servers and on known defaced servers.  We'll look for further
# defacements here too, and server types for hosts whose type is
# missing.
printf $out_format, "DEBUG", "-----", "starting root.exe batch";
{
  my ( %combined_hosts, @combined_hosts );
  my ( $host, %query_responses, %rootexe_results );
  my ( $raw_msg, $msg, $pattern );

  foreach $host ( @iis_servers, keys(%defaced_servers) )
  {
    $combined_hosts{$host} = 1;
  }
  @combined_hosts = keys(%combined_hosts);

  # Perform a batch of code red queries, and analyze the responses.
  # We'll look for defacements and note server types here too.
  #
  dobatch ( \@combined_hosts, \%query_responses, $rootexe_query );
  foreach $host ( keys %query_responses )
  {
    if ( $query_responses{$host} =~ /^$error_string(.*)$/ )
    {
      $rootexe_results{$host} = $query_responses{$host};
    }
    else
    {
      $raw_msg = $query_responses{$host};

      # Print out raw message for debugging.
      $msg=$raw_msg;
      $msg=~s/\s/ /g;
      chomp $msg;
      printf($out_format, "DETAIL-ROOTEXE", $host, $msg);

      # Grab server type if available, and if needed.
      #
      unless ( $server_types{$host} )
      {
        if ($raw_msg=~/^Server: ([\S\t ]+).*$/m)
        {
          $server_types{$host} = $1;
        }
      }
    
      # Check for presence of root.exe.
      #
      if ( $raw_msg =~ /$root_exe_present_pattern/m )
      {
        if ( $compromised_servers{$host} )
        {
          $compromised_servers{$host} .= "; ";
        }
        else
        {
          $compromised_servers{$host} = "";
        }

        if ( $raw_msg =~ /$root_exe_date_pattern/m )
        {
          # $1 is the date
          $compromised_servers{$host} .= "root.exe ($1)";
        }
        else
        {
          $compromised_servers{$host} .= "root.exe (no date available)";
        }
      }

      # Check for defaced servers, if not already flagged.
      #
      unless ( $defaced_servers{$host} )
      {
        foreach $pattern ( @defaced_patterns )
        {
          if ( $raw_msg =~ /$pattern/ )
          {
            $defaced_servers{$host} = $pattern;
            # Yes, we're overwriting any previous.  One defacement
            # report is sufficient!
          }
        }
      }

    } # else (i.e., got a response)
  } # foreach host we tested
} # root.exe batch
printf $out_format, "DEBUG", "-----", "end of root.exe batch";


# Perform a batch of Code Red II mappings checks, only on
# identified IIS servers and on known defaced servers.  We'll
# look for further defacements here too, and server types for
# hosts whose type is missing.
printf $out_format, "DEBUG", "-----", "starting mappings batch";
{
  my ( %combined_hosts, @combined_hosts );
  my ( $host, %query_responses, %mappings_results );
  my ( $raw_msg, $msg, $pattern );

  foreach $host ( @iis_servers, keys(%defaced_servers) )
  {
    $combined_hosts{$host} = 1;
  }
  @combined_hosts = keys(%combined_hosts);

  # Perform a batch of code red queries, and analyze the responses.
  # We'll look for defacements and note server types here too.
  #
  dobatch ( \@combined_hosts, \%query_responses, $mappings_query );
  foreach $host ( keys %query_responses )
  {
    if ( $query_responses{$host} =~ /^$error_string(.*)$/ )
    {
      $mappings_results{$host} = $query_responses{$host};
    }
    else
    {
      $raw_msg = $query_responses{$host};

      # Print out raw message for debugging.
      $msg=$raw_msg;
      $msg=~s/\s/ /g;
      chomp $msg;
      printf($out_format, "DETAIL-MAPPINGS", $host, $msg);

      # Grab server type if available, and if needed.
      #
      unless ( $server_types{$host} )
      {
        if ($raw_msg=~/^Server: ([\S\t ]+).*$/m)
        {
          $server_types{$host} = $1;
        }
      }
    
      # Check for presence of mappings.
      #
      if ( $raw_msg =~ /$mappings_present_pattern/m )
      {
        if ( $compromised_servers{$host} )
        {
          $compromised_servers{$host} .= "; ";
        }
        else
        {
          $compromised_servers{$host} = "";
        }

          $compromised_servers{$host} .= "Code Red II mappings present";
      }

      # Check for defaced servers, if not already flagged.
      #
      unless ( $defaced_servers{$host} )
      {
        foreach $pattern ( @defaced_patterns )
        {
          if ( $raw_msg =~ /$pattern/ )
          {
            $defaced_servers{$host} = $pattern;
            # Yes, we're overwriting any previous.  One defacement
            # report is sufficient!
          }
        }
      }

    } # else (i.e., got a response)
  } # foreach host we tested
} # mappings batch
printf $out_format, "DEBUG", "-----", "end of mappings batch";

# XXX XXX XXX INSERT OTHER BATCHES HERE

# Now print out the results.
#
{
  my ( $host );

  foreach $host ( sort @hosts )
  {
    printf($out_format, "TYPE", $host, 
           $server_types{$host} ?
             $server_types{$host} : "UNKNOWN" );

    printf($out_format, "CODERED", $host,
           $codered_results{$host} ?
             "TESTED: ".$codered_results{$host} : "n/a" );
    
    printf($out_format, "DEFACED", $host,
           $defaced_servers{$host} ?
             "YES: ".$defaced_servers{$host} : "no" );

    printf($out_format, "COMPR", $host,
           $compromised_servers{$host} ?
             "YES: ".$compromised_servers{$host} : "no" );

  }

}

exit 0;
----------------------------------------------------------------------------
#!/bin/sh
#
# RUN THIS FROM A SUBDIRECTORY of "~anne/codered"

# our domain (addresses to scan, sed pattern to remove domain from hostnames)
addresses="132.205.1-254.1-254"
domain_pat='\.[Cc]oncordia\.[Cc][Aa]'
mail_to="anne at alcor.concordia.ca"

# executables
nmap="/usr/pkg/bin/nmap"
codered="/home/staff/anne/codered/codered.pl"

# read-only files; this one contains a list of machines whose status
#   we are specifically tracking.  It should contain the IP address of
#   each, with possibly other stuff that won't be looked for.
tracking_list="../tracking"

# result/progress files
report="./NOTES"
nmap_progress="./nmap.progress"
nmap_output="./port80.out"
nmap_80open="./80open"
nmap_80open_by_ip="./80open.byip"
codered_all_output="./codered.out"
codered_output="./results"
iis_servers="./iis"
new_iis_servers="./new-iis-servers"


# We'll need resources!
ulimit -n 4096
ulimit -d 1048576
ulimit -s 32768

# Collect our output for later mailing.
exec 3>&1 1>${report} 2>&1

# ---------------------- nmap to find open 80 -----------------------

# Scan for port 80.
(echo -n "nmap starts: "; date)
${nmap} -sT -p 80 --host_timeout 5000 -v -oM ${nmap_output} \
     ${addresses} > ${nmap_progress}
(echo -n "nmap ends:   "; date)
echo ""

# Count hosts with port 80 filtered and open.
echo `grep '80/filt' ${nmap_output} | wc -l` \
  "hosts with port 80 filtered"
grep '80/open' ${nmap_output} \
  | sed -e 's/^Host: //'  -e 's/ ().*//' \
        -e 's/.* (\(.*\)'${domain_pat}'.*/\1/' \
  | sort -f \
  > ${nmap_80open}
echo `< ${nmap_80open} wc -l` \
  "hosts are listening on port 80"
grep '80/open' ${nmap_output} \
  | sed -e 's/^Host: //'  -e 's/ .*//' \
  > ${nmap_80open_by_ip}
echo ""

# ------------- check all port-80-opens for unpatched IIS -----------
# ------------- check all IIS servers for root.exe ------------------
# ------------- check all IIS servers for Code Red II mappings ------

(echo -n "codered starts: "; date)
${codered} < ${nmap_80open} > ${codered_all_output}
(echo -n "codered ends:   "; date)
echo ""


# Make a smaller file of just interesting results:
< ${codered_all_output} egrep -v '(^DEBUG|DETAIL)' \
  | egrep -v '^(DEFACED|COMPR) .*no$' \
  | egrep -v '^CODERED .*n/a$' \
  > ${codered_output}

# Count IIS servers, and make a file of them.
echo ""
< ${codered_output} egrep '^TYPE .*IIS' \
  | sed -e 's/^TYPE *//' -e 's/:.*//' \
  > ${iis_servers}
echo `< ${iis_servers} wc -l` \
  "hosts are running iis"

# ------------- report results of check -----------------------------

echo ""
echo "==== " `egrep '^CODERED .*: TESTED: UNPATCHED' ${codered_output} |wc -l` \
     "host(s) definitely unpatched: =================="
< ${codered_output} egrep '^CODERED .*: TESTED: UNPATCHED'
echo "========================================================"
echo ""


echo ""
echo "==== " `egrep '^COMPR' ${codered_output} |wc -l` \
     "host(s) compromised with root.exe: =================="
< ${codered_output} egrep '^COMPR'
echo "========================================================"
echo ""


# ------------- report on any new IIS servers -----------------------

touch ${new_iis_servers}
for i in `cat ${iis_servers}`; do
  if fgrep -w `host -ta $i 2>&1 \
     | sed -e 's/.*address //' -e "s/Host not found./$i/" | tail -1` \
    ${tracking_list} > /dev/null
  then
    : # we know about this one
  else
    echo $i >> ${new_iis_servers}
  fi
done
echo ""
echo "==== " `< new-iis-servers wc -l` \
     "new host(s) running IIS: =================="
cat ${new_iis_servers}
echo "========================================================"
echo ""

# ------------- mail the results! -----------------------------------

# Recover our output.
exec 1>&3 2>&1
(
  echo "To: ${mail_to}"
  echo "Subject: code red scan report `date`"
  echo ""
  cat ${report}
) | sendmail -t -oi
----------------------------------------------------------------------------



More information about the unisog mailing list