#!/usr/bin/env perl
# Copyright 2011-2017 Frank Breedijk, Glenn ten Cate, Alex Smirnoff
#
# 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 program converts an NBE file to the IVIL format
# ------------------------------------------------------------------------------

use strict;
use SeccubusV2;
use IVIL;
use Seccubus::Hostnames;

use Getopt::Long;
use Carp;
use XML::Simple;
use Data::Dumper;

my (
	$scanname,
	$scanner,
	$scannerversion,
	$help,
	$verbose,
	$workspace,
	$timestamp,
	$infile,
	$outfile,
	@findings,
   );

$help = 0;

# Create default values

GetOptions(	'scan=s'		=> \$scanname,
		'scanner=s'		=> \$scanner,
		'scannerversion=s'	=> \$scannerversion,
		'help|h!'		=> \$help,
		'verbose|v!'		=> \$verbose,
		'workspace=s'		=> \$workspace,
		'timestamp=s'		=> \$timestamp,
		'infile=s'		=> \$infile,
		'outfile=s'		=> \$outfile,
	  );

help() if $help;
$scanname = $workspace unless $scanname;

if ( ! $infile || ! -e $infile ) {
	print "You must specify the infile parameter";
	help();
} elsif ( ( $workspace && ! $scanname ) || ( ! $workspace && $scanname ) ) {
	print "workspace and scan have to specified both or not at all";
	help();
};

print "Reading in .nessus file\n" if $verbose;
my $nessus = XMLin($infile,
		   forceArray	=> [ 'ReportHost', 'ReportItem', 'see_also', 'xref', 'cve', 'bid' ],
		   KeyAttr	=> undef,
	          );

# Lets clear some memory space
$nessus->{Policy} = undef;
#die Dumper($nessus);

unless ( $outfile ) {
	$outfile = $infile;
	$outfile =~ s/\.nessus$//;
	$outfile .= ".ivil.xml";
}
print "Opening file $outfile for output\n" if $verbose;
open(my $OUT, ">", "$outfile") or die "Unable to open output file $outfile";
print $OUT xml_header();
print $OUT ivil_open();

if ($workspace) {
	print "Creating addressee block\n" if $verbose;
	print $OUT ivil_addressee("Seccubus", {
						"workspace" => $workspace,
						"scan"		=> $scanname,
			 		     });
}

print "Creating findings\n" if $verbose;
print $OUT "<findings>\n";

foreach my $host ( @{$nessus->{Report}->{ReportHost}} ) {
	my ($hostname, $ip);

	$ip = $host->{name};
	foreach my $tag ( @{$host->{HostProperties}->{tag}} ) {
		if ($tag->{name} eq 'host-fqdn') {
			$hostname = $tag->{content};
		} elsif ($tag->{name} eq 'host-ip') {
			$ip = $tag->{content};
		} elsif ($tag->{name} eq 'netbios-name') {
			$hostname = $tag->{content} unless $hostname;
		}
	}

	foreach my $item ( @{$host->{ReportItem}} ) {
		# O.K. lets go through all findings
		my $finding = {};
		$finding->{ip} = $ip;
		$finding->{hostname} = $hostname;
		if ( $item->{pluginID} eq '0' && $item->{pluginName} eq '' && $item->{pluginFamily} eq '' ) {
			# We are dealing with portscanner output
			$item->{pluginID} = 'portscanner';
			$item->{synopsis} = "Port $item->{port}/$item->{protocol} is open.\nService was identified as $item->{svc_name}";
		} elsif ( $item->{pluginFamily} eq 'Policy Compliance' ) {
			# Compliance plugins behave funny. More findings per
			# PluginID
			$item->{description} =~ /^\"?([^\"\:]+)/;
			$item->{pluginID} .= "_$1";
			print("\'$item->{description}\'\n\'$item->{pluginID}\'\n");
		}
		foreach my $key ( keys %{$item} ) {
			if ( $key eq 'protocol' ) {
			} elsif ( $key eq 'port' ) {
			} elsif ( $key eq 'pluginID' ) {
				$finding->{id} = $item->{pluginID};
			} elsif ( $key eq 'pluginName' ) {
			} elsif ( $key eq 'pluginFamily' ) {
			} elsif ( $key eq 'svc_name' ) {
			} elsif ( $key eq 'synopsis' ) {
			} elsif ( $key eq 'plugin_output' ) {
			} elsif ( $key eq 'description' ) {
			} elsif ( $key eq 'risk_factor' ) {
			} elsif ( $key eq 'solution' ) {
			} elsif ( $key eq 'exploit_available' ) {
			} elsif ( $key eq 'exploitability_ease' ) {
			} elsif ( $key eq 'patch_publication_date' ) {
			} elsif ( $key eq 'cvss_vector' ) {
			} elsif ( $key eq 'cvss_base_score' ) {
			} elsif ( $key eq 'cvss_temporal_vector' ) {
			} elsif ( $key eq 'cvss_temporal_score' ) {
			} elsif ( $key eq 'see_also' ) {
			} elsif ( $key eq 'xref' ) {
			} elsif ( $key eq 'cve' ) {
			} elsif ( $key eq 'bid' ) {
			} elsif ( $key eq 'severity' ) {
				if ( $item->{severity} >= 3 ) {
					$finding->{severity} = 1;
				} elsif ( $item->{severity} > 0 ) {
					$finding->{severity} = 4 - $item->{severity};
				} else {
					$finding->{severity} = $item->{severity};
				}
			} elsif ( $key eq 'plugin_publication_date' ) {
			} elsif ( $key eq 'plugin_modification_date' ) {
			} elsif ( $key eq 'plugin_version' ) {
			} elsif ( $key eq 'plugin_type' ) {
			} elsif ( $key eq 'vuln_publication_date' ) {
			} else {
				#print Dumper($item);
				#print "\nUnknown attribute $key";
				#die "Unknown attribute $key";
			}
		}
		$item->{port} = 'generic' if ( $item->{port} eq '0' );
		$finding->{port} = "$item->{port}/$item->{protocol}";

		my $txt = "";
		# Bug #35 - ivil does not import title of Nessus finiding
		if ( $item->{pluginName} ) {
			$txt =  $item->{pluginName};
		}
		if ( $item->{synopsis} ) {
			$txt = join ( "\n", $txt, $item->{synopsis} );
		}
		if ( $item->{plugin_output} ) {
			$txt = join ( "\n", $txt, "\nPlugin output:",  $item->{plugin_output} );
		}
		if ( $item->{description} ) {
			$txt = join( "\n", $txt, "\nDescription:", $item->{description});
		}
		$txt .= "\n\n";
		if ( $item->{solution} ) {
			$txt .= "Solution:\n$item->{solution}\n\n";
		}
		if ( $item->{severity} ) {
			$txt .= "Severity: $item->{severity}\n\n";
		}
		if ( $item->{risk_factor} ) {
			$txt .= "Risk factor: $item->{risk_factor}\n\n";
		}
		if ( $item->{exploitability_ease} ) {
			$txt .= "Ease of exploit: $item->{exploitability_ease}\n\n";
		}
		if ( $item->{patch_publication_date} ) {
			$txt .= "Patch available since: $item->{patch_publication_date}\n\n";
		}
		if ( $item->{cvss_vector} ) {
			$txt .= "CVSS base vector: $item->{cvss_vector}\n";
		}
		if ( $item->{cvss_base_score} ) {
			$txt .= "CVSS base score: $item->{cvss_base_score}\n\n";
		}
		if ( $item->{cvss_temporal_vector} ) {
			$txt .= "CVSS temporal vector: $item->{cvss_temporal_vector}\n";
		}
		if ( $item->{cvss_temporal_score} ) {
			$txt .= "CVSS temporal score: $item->{cvss_temporal_score}\n\n";
		}
		if ( $item->{see_also} ) {
			$txt .= "See also:\n" . join("\n", @{$item->{see_also}});
			$txt .= "\n\n";
		}
		if ( $item->{cve} ) {
			$txt .= "CVE references: ". join(" ", @{$item->{cve}});
			$txt .= "\n\n";
		}
		if ( $item->{bid} ) {
			$txt .= "BID references: ". join(" ", @{$item->{bid}});
			$txt .= "\n\n";
		}
		if ( $item->{xref} ) {
			$txt .= "Other references: ". join(" ", @{$item->{xref}});
			$txt .= "\n\n";
		}
		$finding->{finding} = $txt;
		print $OUT ivil_finding($finding);
	}
}

print $OUT "</findings>\n";

#print "Creating sender block\n" if $verbose;
#print $OUT ivil_sender($scanner, $scannerversion, $timestamp);


print $OUT ivil_close();

close $OUT;

exit();

sub help() {
	print "

Usage: nessus2ivil --scanner <scanner> [--scannerversion <versionstring>] \\
                   --timestamp <timestamp> [--workspace <workspacename>] \\
		   [--scan <scanname>] --infile <filename input> \\
		   [--outfile <filename output>] [--verbose] [--help]

Arguments:
--scanner	- The name of the scanner used to create the .nessus file
			Logical options are Nessus, OpenVAS or Nikto
--scannerversion- Optional: the version of the scanner used to create the
		  .nessus file
--timestamp	- Timestamp of when the file was created in the format
		  YYYYMMDDhhmmss or YYYYMMDDhhmm so 11 december 2011 1:14:00 pm
		  is 20111211131400 or 201112111314
--workspace	- Optional: Which Seccubus workspace do you want to load this
		  in, this informaiton is used to create the addressee block.
		  If not value is given for workspace no addressee block is
		  generated
--scan		- Optional: Which Seccubus scan do you want to load this in,
		  this informaiton is used to create the addressee block. If
		  scan is not specified then the value for workspace is used.
--infile	- This defines the .nessus file that will be converted to IVIL
--outfile	- Optional: This defines the name of the file used to output
		  IVIL. If no filename is given, the infile value is used,
		  a trailing .nessus is removed (if it exists) and .ivil.xml is
		  appended
--verbose (-v)	- Be verbose
--help (-h)	- Print this message
";
	exit();
}


