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)