#!/usr/bin/env perl
# Copyright 2017 Frank Breedijk, Dan McGinn-Combs
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ------------------------------------------------------------------------------
# This script will call the Nmap scanner and import the results as IVIL
# ------------------------------------------------------------------------------

use strict;
use SeccubusV2;
use Seccubus::IVIL;
use Seccubus::Helpers;

use Getopt::Long;
use Carp;

my (
	$nmap_path,
	$nmap_options,
	$hosts_file,
	$workspace,
	$scan,
	$sudo,
	$help,
	$verbose,
	$quiet,
	$username,
	$nodelete,
	$remote,
   );

$help = 0;

# Create default values

GetOptions(
		'nmap_path|p=s'		=> \$nmap_path,
		'nmap_options|o=s'	=> \$nmap_options,
		'hosts=s'		=> \$hosts_file,
		'workspace=s'		=> \$workspace,
		'scan=s'		=> \$scan,
		'sudo'			=> \$sudo,
		'verbose|v+'		=> \$verbose,
		'quiet|q!'		=> \$quiet,
		'help|h'		=> \$help,
		'nodelete'		=> \$nodelete,
		'remote|r=s'		=> \$remote,
	  );

help() if $help;
$verbose = 0 if $quiet;
$nodelete = 0 unless $nodelete;

my $config = get_config();
my $load_ivil = "perl -I$config->{paths}->{modules} $config->{paths}->{bindir}\/load_ivil";
my $attach_file = "perl -I$config->{paths}->{modules} $config->{paths}->{bindir}\/attach_file";

print "Hosts file specified $hosts_file\n" if $verbose;
if ( ! $hosts_file || ! -e $hosts_file ) {
	print "You must specify a valid hosts file";
	help();
} elsif ( ! $workspace ) {
	print "You must specify a workspace name";
	help();
} elsif ( ! $scan ){
	print "You must specify a scan name";
	help();
};

$nmap_path = get_nmap_path() unless $nmap_path;
if ( ! $nmap_path && -e $nmap_path ) {
	print "Unable to find Nmap on your system. I suggest you use the --nmap_path option\nto provide a path to the nmap executable\n";
	help();
}
print "Nmap found in $nmap_path\n" if $verbose;
my $nmap_version = get_nmap_version($nmap_path);

my $tempfile = "/tmp/seccubus.$$";

my $nmap_options = "-iL $hosts_file -oA '$tempfile' $nmap_options";
print "Nmap options: $nmap_options\n" if $verbose;

my $timestamp = make_timestamp();
print "Timestamp = $timestamp\n" if $verbose;
my $load_ivil = "perl -I$config->{paths}->{modules} $config->{paths}->{bindir}\/load_ivil";

my $cmd = "$nmap_path $nmap_options";
if ( $sudo ) {
	print "Sudo mode on\n" if $verbose;
	$cmd = "sudo $cmd";
}
if ( $sudo ) {
	$username = run_cmd("whoami",0,$remote);
	$username =~ s/^\n*//;
	$username =~ s/[\n\r]*$//;
}

print "Execuing $cmd\n" unless $quiet;
run_cmd($cmd,$verbose,$remote,[ "$tempfile.*" ],[ $hosts_file ]);

if ( $sudo ) {
	# Fixes issue #52
	run_cmd("sudo chown $username \"$tempfile.*\"",$verbose,$remote);
}


print "Scanning done, converting .xml to ivil\n" unless $quiet;
$cmd = "perl -I$config->{paths}->{modules} $config->{paths}->{bindir}\/nmap2ivil --scannerversion=$nmap_version --workspace '$workspace' --scan '$scan' --timestamp=$timestamp --infile '$tempfile.xml' ";
$cmd .= "-v" if $verbose > 1;
run_cmd($cmd,$verbose);

print "Importing ivil\n" unless $quiet;
$cmd = "$load_ivil --workspace '$workspace' --scan '$scan' --scanner Nmap --scannerversion $nmap_version --timestamp $timestamp";
$cmd .= " -v" if $verbose > 1;
$cmd .= " '$tempfile.ivil.xml'";
run_cmd($cmd,$verbose);

$cmd = "$attach_file --workspace '$workspace' --scan '$scan' --timestamp $timestamp --file '$tempfile.nmap' --description 'Command output'";
$cmd .= " -v" if $verbose > 1;
run_cmd($cmd,$verbose);

$cmd = "$attach_file --workspace '$workspace' --scan '$scan' --timestamp $timestamp --file '$tempfile.gnmap' --description 'Greppable output'";
$cmd .= " -v" if $verbose > 1;
run_cmd($cmd,$verbose);

$cmd = "$attach_file --workspace '$workspace' --scan '$scan' --timestamp $timestamp --file '$tempfile.xml' --description 'XML output'";
$cmd .= " -v" if $verbose > 1;
run_cmd($cmd,$verbose);

$cmd = "$attach_file --workspace '$workspace' --scan '$scan' --timestamp $timestamp --file '$tempfile.ivil.xml' --description 'IVIL output'";
$cmd .= " -v" if $verbose > 1;
run_cmd($cmd,$verbose);

# Cleanup
unless( $nodelete ) {
	unlink "$tempfile.gnmap" if -e "$tempfile.gnmap";
	unlink "$tempfile.nmap" if -e "$tempfile.nmap";
	unlink "$tempfile.xml" if -e "$tempfile.xml";
	unlink "$tempfile.ivil.xml" if -e "$tempfile.ivil.xml";
}
if ( $remote ) {
	run_cmd("rm -f $tempfile.*",$verbose,$remote);;
}

exit(0);

sub help() {
	print "
Usage: scan 	[--nmap_path|p <path to nmap>] \\
		[--nmap_options <additional Nmap options>]] \\
		--hosts <hosts file> [--remote|r <host,user,keyfile>]
		[--verbose|v] [--quiet|q] [--help|h]

Arguments:
--nmap_path	- You can use this optional parameter to provide the script with
(-p)		  the path to nmap. If you do not provide this the
		  script tries to find the files itself and fail if it cannot
		  find them.
--nmap_options	- Additional command line options to provide to Nmap see
(-o)		  'nmap -Help' for more information. Please quote the options so that they are
                  passed to Nikto as a single block. E.g. -o '-sS -Pn'
--hosts		- The file containing the 'hosts' to scan
--sudo		- Use sudo to elevate privileges (needed for certain scans)
--workspace	- Name of the workspace to load the findings into
--scan		- Name of the scan to load the findings into
--remote (-r)	- Comma separated list of host,username,keyfile representing the
		  host and credetials which should be used to execute the scan
--verbose (-v)	- Be verbose during execution
--quiet (-q)	- Don't print output
--nodelete      - Don't delete temporary files (use -v to find out which)
--help (-h)	- Print this message
";
	exit(1);
}

sub get_nmap_path() {
	my $path = run_cmd("which nmap",0,$remote);
	if ( $path =~ /(^\/.*nmap)/ ) {
		return $1;
	}
	$path = run_cmd("which nmap.pl",0,$remote);
	if ( $path =~ /(^\/.*nmap\.pl)/ ) {
		return $1;
	}
	if ( run_cmd("ls /opt/nmap/nmap.pl",0,$remote) ) {
		return '/opt/nmap/nmap.pl';
	}
	return;
}

sub get_nmap_version() {
	my $nmap_path = shift;

	my $version=run_cmd("$nmap_path -V",$verbose,$remote);
	$version =~ /Nmap version\s+(\d+\.\d+)/;
	return $1;
}

sub make_timestamp() {
	my ($second, $minute, $hour, $day, $month, $year) = localtime();
	$month++;
	$second = "0" . $second if $second < 10;
	$minute = "0" . $minute if $minute <10;
	$hour = "0". $hour if $hour < 10;
	$day = "0". $day if $day <10;
	$month = "0" . $month if $month <10;
	$year += 1900;

	return "$year$month$day$hour$minute$second";
}

