2015-01-22

Scan and Compare Versions of Your Network Services

One of the most effective, although often neglected, ways to keep your servers secure are regular updates of running services (daemons). If you are running more than one server, it might be useful to compare the version of the services running on the hosts. If you find any differences you should consider upgrading to the newest version.

To achieve this, we are going to use the extremely useful scanning tool called nmap. We will call it and parse its output from a Perl script using Nmap::Parser module.

Here goes the script:

#!/usr/bin/perl
use strict;
use warnings;
use Nmap::Parser;

die "Usage: $0 host1 [host2 host3 ...]\n" unless @ARGV;

listNetServices(@ARGV);

=head2 listNetServices( @hosts )

Are we running different versions of network services (ex. SSH) on different
hosts? Useful for identifying unpatched (old) versions of network services but
we have to include a host with patched services into @hosts.

=cut

sub listNetServices {
    my @hosts = @_;

    my $services;    # HoH

    # Anonymous subroutine
    my $nmap = sub {
        my $host = shift;    #Nmap::Parser::Host object, just parsed

        for my $port ( $host->tcp_ports('open') ) {

            # Nmap::Parser::Host::Service object
            my $svc = $host->tcp_service($port);

            my $service = join( ' | ',
                $svc->name    // '',
                $svc->product // '',
                $svc->version // '' );

            push @{ $services->{$port}{$service} },
              $host->hostname . ' (' . $host->addr . ')';

        }
    };

    my $np = new Nmap::Parser;
    $np->callback($nmap);
    $np->parsescan( '/usr/bin/nmap', '-sV', @hosts );

    # Print report
    for my $port ( sort keys %$services ) {

        my $n_versions = keys %{ $services->{$port} };
        next unless $n_versions > 1;

        print "$port - $n_versions different versions on this port\n";

        for my $version ( sort keys %{ $services->{$port} } ) {
            print ' ' x 4 . $version . "\n";
            for my $host ( sort @{ $services->{$port}{$version} } ) {
                print ' ' x 8 . $host . "\n";
            }
        }
    }

    return;
}

(Up-to-date source of the ListNetServices function can be found in MyUtils.)

And this is a sample output suggesting that host1 is running older versions of both ssh and http daemons:

$ perl script-listed-above host1 host2
22 - 2 different versions on this port
    ssh | OpenSSH | 5.5p1 Debian 6+squeeze5
        host1 (1.2.3.4)
    ssh | OpenSSH | 6.7
        host2 (5.6.7.8)
80 - 2 different versions on this port
    http | Apache httpd | 2.2.16
        host1 (1.2.3.4)
    http | Apache httpd | 2.4.10
        host2 (5.6.7.8)