#!/usr/bin/perl
# Purpose:
# 1. Take a Nessus output file (*.nbe) as input and generate a port list spreadsheet (*.csv) with the following
# column labels: Host Name, Port, Service Detected.
# 2. Take a Nessus output file (*.nbe) as input and generate a vulnerability list spreadsheet (*.csv) with the
# following column labels: Vulnerability, CVE, Risk, False Positive.
# Created: 2008-10-20
# Date Last Modified: 2008-11-03
use strict;
use Getopt::Std;
my %opts;
getopts('i:o:hs', \%opts);
my ($inputFile, $outputFile, $ignoreMe, $status, $fileLength);
if (-f $opts{i} && $opts{o} && !$opts{h}) {
$inputFile = $opts{i};
$outputFile = $opts{o};
if ($opts{s}) {
$status = 1;
}
} else {
print STDERR "./NessusPBE.pl -i -o \n";
exit;
}
# Ignore traceroute information
$ignoreMe = '(For\syour\sinformation,\shere\sis\sthe\straceroute';
# Ignore resolving (for now)
$ignoreMe .= '|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\sresolves\sas\S+)';
if ($status == 1) {
open(INPUT, "< $inputFile") || die "Could not open file for input\n";
for ($fileLength = 0; ; $fileLength++) {}
close INPUT;
}
# Open the Nessus file for reading
open(INPUT, $inputFile) || die "Could not open file for input\n";
# Hash of all open ports
# Key = IP Address
# Value = Port->Service Name
my (%OpenPorts, %UnknownPorts);
# Array of hashes of all open ports where Nessus couldn't enumerate the running service
#my (%UnknownPorts);
my (@tmpA, $file);
# Arrays for vulnerabilities
####### Each entry in the array is a String with the following format:
####### IP Address|Synopsis|Description|Solution|Risk Factor|CVSS Base Factor|Plugin Output|CVE|BID|Other Reference
####### Use the parse_vertical_bar subroutine to obtain an array of the values
# a[0][0] = Synopsis
# a[0][1] = Description
# a[0][2] = Solution
# a[0][3] = Risk Factor
# a[0][4] = CVSS Base Score
# a[0][5] = Plugin Output
# a[0][6] = CVE
# a[0][7] = BID
# a[0][8] = Other References
my (@none, @low, @medium, @high, @all);
my $statusCount;
# Read in each line from the Nessus input file.
while ($file = ){
# Show progress status
if ($status == 1) {
my $perc = ($./$fileLength) * 100;
$perc =~ s/\.\d+//g;
print "\b\b\b\b%$perc";
}
# Ignore these things
next if ($file =~ /$ignoreMe/);
chomp($file);
my @field = parse_vertical_bar($file);
# Parse out the lines that are not results
# This should parse out the Timestamps
if ($field[0] eq 'results'){
# Handle Port Scan Results
if (!($field[3] eq 'general/tcp') && !($field[3] eq 'general/udp') && !($field[3] eq 'general/icmp')){
# Parse the element in index 3 to extract service name,
# port number, and protocol
# $element_3[0] = Service Name
# $element_3[1] = Port Number
# $element_3[2] = Protocol (tcp or udp)
my @element_3 = parse_port_element($field[3]);
my $fixedfield;
($fixedfield = $field[2]) =~ s/\./_/g;
# Hash of hashes
# Hash is OpenPorts
# OpenPorts -> IP Address -> Port Number -> Service Name
# UnknownPorts -> IP Address -> Port Number -> Unknown
if ($element_3[0] eq 'unknown'){
$UnknownPorts{$fixedfield}{$element_3[1] . "/" . $element_3[2]} = $element_3[0];
}
else {
$OpenPorts{$fixedfield}{$element_3[1] . "/" . $element_3[2]} = $element_3[0];
}
}
# If there is a vulnerability and not just an identified open port
# Add the array of strings
if ($field[6] ne ""){
# parsed6 contains:
# 0: IP Address
# 1: Port Number
# 2: Synopsis
# 3: Description
# 4: Solution
# 5: Risk factor
# 6: CVSS Base factor
# 7: Plugin Output
# 8: CVE
# 9: BID
# 10: Other references
# 11: Nessus Plugin ID
my @parsed6 = parse_paragraph_test($field[2],$field[3],$field[6]);
$parsed6[11] = $field[4];
# Insert the port
my @cleaned6 = cleanup(@parsed6);
if ($parsed6[5] =~ m/none/i){
push @none, [@cleaned6];
}
elsif ($parsed6[5] =~ m/low/i){
push @low, [@cleaned6];
}
elsif ($parsed6[5] =~ m/medium/i){
push @medium, [@cleaned6];
}
elsif ($parsed6[5] =~ m/high/i){
push @high, [@cleaned6];
}
push @all, [@cleaned6];
}
} # Ends big if statement that parses out non-results
} # Ends: while ($file = )
# Close the input file
close(INPUT);
# Assign Services
for my $i (0..$#all){
if ($all[$i][7] =~ m/server is running on this port/i){
my @pList = parse_port_element($all[$i][1]);
# Replace existing
$OpenPorts{$all[$i][0]}{$pList[1] . "/" . $pList[2]} = $pList[0];
# Also need to implement taking out of 'unknown ports if it is there
delete($UnknownPorts{$all[$i][0]}{$pList[1] . "/" . $pList[2]});
}
}
# Print out the hash function of host names, open ports, and running services
open(OPENPORTLIST, ">>" . $outputFile . "-OpenPorts.csv") || die "Could not open $outputFile-OpenPorts.csv for output\n";
print OPENPORTLIST "Host Name,Port,Service Detected\n";
my (@openList, $tmpA);
for my $k1 (sort keys %OpenPorts){
for my $k2 (sort keys %{$OpenPorts{$k1}}) {
$tmpA = $k1 . "," . $k2 . "," . $OpenPorts{$k1}{$k2};
push @openList, $tmpA;
}
}
# IPs get sorted with spreadsheet program
#my @sortedKnownServices = sortIPs(@openList);
foreach my $lineService (@openList){
$lineService =~ s/_/\./g;
print OPENPORTLIST "$lineService\n";
}
close(OPENPORTLIST);
# Print out the hash of host names and open ports whose service is unknown
open(OPENUNKNOWNLIST, ">>" . $outputFile . "-UnknownPorts.csv") || die "Could not open $outputFile-UnknownPorts.csv for output\n";
print OPENUNKNOWNLIST "Host Name,Port\n";
my (@openList2, $tmpA2, $UnknownPorts);
for my $k3 (sort keys %$UnknownPorts){
for my $k4 (sort keys %{$UnknownPorts->{$k3}}) {
$tmpA2 = $k3 . "," . $k4; # . "," . $UnknownPorts->{$k3}{$k4};
push @openList2, $tmpA2;
}
}
# IPs get sorted with spreadsheet program
#my @sortedUnknownServices = sortIPs(@openList2);
foreach my $lineService2 (@openList2){
$lineService2 =~ s/_/\./g;
print OPENUNKNOWNLIST "$lineService2\n";
}
close(OPENUNKNOWNLIST);
# Print out the vulnerabilities
open(OPENVULNFILE, ">>" . $outputFile . "-VulnList.tsv") || die "Could not open $outputFile-VulnList.tsv for output\n";
# 0: IP Address
# 1: Port Number
# 2: Synopsis
# 3: Description
# 4: Solution
# 5: Risk factor
# 6: CVSS Base factor
# 7: Plugin Output
# 8: CVE
# 8: BID
# 10: Other references
# 11: Nessus Plugin ID
print OPENVULNFILE "Host Name\tPort Number\tSynopsis\tDescription\tSolution\tRisk factor\tCVSS Base Score\tPlugin Output\tCVE\tBID\tOther References\tNessus Plugin ID\tFalse Positive\n";
# Convert the array of arrays of strings into an array of strings
# seperated by the vertical bar
my (@allList, $tmpAll, $a1, $j1);
for $a1 (0..$#all){
for $j1 (0..10){
if ($all[$a1][$j1] ne ""){
$tmpAll = $tmpAll . $all[$a1][$j1] . "|";
}
else{
$tmpAll = $tmpAll . " " . "|";
}
}
$tmpAll = $tmpAll . $all[$a1][11];
push @allList, $tmpAll;
$tmpAll = "";
}
my @sortedAll = sortIPAndRisk(@allList);
for my $i (0..$#sortedAll){
my @toPrint = parse_vertical_bar($sortedAll[$i]);
for my $j (0..$#toPrint-1){
print OPENVULNFILE "$toPrint[$j]\t";
}
print OPENVULNFILE "$toPrint[$#toPrint]\n";
}
close(OPENVULNFILE);
sub cleanup{
my @toClean = @_;
for my $c (0..$#toClean){
$toClean[$c] =~ s/^\s+//;
$toClean[$c] =~ s/\r//g;
$toClean[$c] =~ s/\n//g;
$toClean[$c] =~ s/^\\n|\\n$//g;
$toClean[$c] =~ s/^\\n|\\n$//g;
$toClean[$c] =~ s/\\n/ /g;
}
return @toClean;
}
# Parses out the paragraph in the 7th field of the nbe item
# Expected Input:
# 1: IP Address String
# 2: The paragraph (7th field of the nbe item)
# Returns an array of the items in the paragraph:
# 0: IP Address
# 1: Port Number
# 2: Synopsis
# 3: Description
# 4: Solution
# 5: Risk factor
# 6: CVSS Base factor
# 7: Plugin Output
# 8: CVE
# 9: BID
# 10: Other references
sub parse_paragraph_test{
my $ipAddressTest = shift;
my $port = shift;
my $paragraphTest = shift;
my (@listOfItemsTest, @splitLineTest, @splitLineTest2);
$listOfItemsTest[0] = $ipAddressTest;
$listOfItemsTest[1] = $port;
# Parse out the Synopsis verbiage, if any
if ($paragraphTest =~ m/Synopsis[\s]*:/i){
@splitLineTest = split(/Synopsis[\s]*:/, $paragraphTest);
if ($paragraphTest =~ m/Description[\s]*:/i){
@splitLineTest2 = split(/Description[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[2] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Solution[\s]*:/i){
@splitLineTest2 = split(/Solution[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[2] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Risk factor[\s]*:/i){
@splitLineTest2 = split(/Risk factor[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[2] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/CVSS Base Score[\s]*:/i){
@splitLineTest2 = split(/CVSS Base Score[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[2] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Plugin output[\s]*:/i){
@splitLineTest2 = split(/Plugin output[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[2] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/CVE[\s]*:/i){
@splitLineTest2 = split(/CVE[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[2] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/BID[\s]*:/i){
@splitLineTest2 = split(/BID[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[2] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Other references[\s]*:/i){
@splitLineTest2 = split(/Other references[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[2] = $splitLineTest2[0];
}
# This should never happen, but just in case
else{
$listOfItemsTest[2] = $splitLineTest[1];
}
}
# If there is no Synopsis or Description, then put the in the description field and null
# in the synopsys field.
elsif (!($paragraphTest =~ m/Description[\s]*:/i)){
if ($paragraphTest =~ m/Solution[\s]*:/i){
@splitLineTest = split(/Solution[\s]*:/, $paragraphTest);
$listOfItemsTest[3] = $splitLineTest[0];
}
elsif ($paragraphTest =~ m/Risk factor[\s]*:/i){
@splitLineTest = split(/Risk factor[\s]*:/, $paragraphTest);
$listOfItemsTest[3] = $splitLineTest[0];
}
elsif ($paragraphTest =~ m/CVSS Base Score[\s]*:/i){
@splitLineTest = split(/CVSS Base Scor[\s]*:/, $paragraphTest);
$listOfItemsTest[3] = $splitLineTest[0];
}
elsif ($paragraphTest =~ m/Plugin output[\s]*:/i){
@splitLineTest = split(/Plugin output[\s]*:/, $paragraphTest);
$listOfItemsTest[3] = $splitLineTest[0];
}
elsif ($paragraphTest =~ m/CVE[\s]*:/i){
@splitLineTest = split(/CVE[\s]*:/, $paragraphTest);
$listOfItemsTest[3] = $splitLineTest[0];
}
elsif ($paragraphTest =~ m/BID[\s]*:/i){
@splitLineTest = split(/BID[\s]*:/, $paragraphTest);
$listOfItemsTest[3] = $splitLineTest[0];
}
elsif ($paragraphTest =~ m/Other references[\s]*:/i){
@splitLineTest = split(/Other references[\s]*:/, $paragraphTest);
$listOfItemsTest[3] = $splitLineTest[0];
}
# In this case, there is just a description
else{
$listOfItemsTest[3] = $paragraphTest;
}
}
# Parse out the Description verbiage, if any
if ($paragraphTest =~ m/Description[\s]*:/i){
@splitLineTest = split(/Description[\s]*:/, $paragraphTest);
if ($paragraphTest =~ m/Solution[\s]*:/i){
@splitLineTest2 = split(/Solution[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[3] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Risk factor[\s]*:/i){
@splitLineTest2 = split(/Risk factor[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[3] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/CVSS Base Score[\s]*:/i){
@splitLineTest2 = split(/CVSS Base Score[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[3] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Plugin output[\s]*:/i){
@splitLineTest2 = split(/Plugin output[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[3] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/CVE[\s]*:/i){
@splitLineTest2 = split(/CVE[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[3] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/BID[\s]*:/i){
@splitLineTest2 = split(/BID[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[3] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Other references[\s]*:/i){
@splitLineTest2 = split(/Other references[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[3] = $splitLineTest2[0];
}
else{
$listOfItemsTest[3] = $splitLineTest[1];
}
}
# Parse out the Solution verbiage, if any
if ($paragraphTest =~ m/Solution[\s]*:/i){
@splitLineTest = split(/Solution[\s]*:/, $paragraphTest);
if ($paragraphTest =~ m/Risk factor[\s]*:/i){
@splitLineTest2 = split(/Risk factor[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[4] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/CVSS Base Score[\s]*:/i){
@splitLineTest2 = split(/CVSS Base Score[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[4] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Plugin output[\s]*:/i){
@splitLineTest2 = split(/Plugin output[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[4] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/CVE[\s]*/i){
@splitLineTest2 = split(/CVE[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[4] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/BID[\s]*:/i){
@splitLineTest2 = split(/BID[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[4] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Other references[\s]*:/i){
@splitLineTest2 = split(/Other references[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[4] = $splitLineTest2[0];
}
# This should never happen, but just in case
else{
$listOfItemsTest[4] = $splitLineTest[1];
}
}
# Parse out the Risk Factor verbiage, if any
if ($paragraphTest =~ m/Risk factor[\s]*:/i){
@splitLineTest = split(/Risk factor[\s]*:/, $paragraphTest);
if ($paragraphTest =~ m/CVSS Base Score[\s]*:/i){
@splitLineTest2 = split(/CVSS Base Score[\s]*:/, $splitLineTest[1]);
# When there is a CVSS Base Score, there will be a trailing: /
$splitLineTest2[0] = substr($splitLineTest2[0], 0, -3);
if ($splitLineTest2[0] =~ m/none/i){
$listOfItemsTest[5] = "None";
}
elsif ($splitLineTest2[0] =~ m/low/i){
$listOfItemsTest[5] = "Low";
}
elsif ($splitLineTest2[0] =~ m/medium/i){
$listOfItemsTest[5] = "Medium";
}
elsif ($splitLineTest2[0] =~ m/high/i){
$listOfItemsTest[5] = "High";
}
}
elsif ($paragraphTest =~ m/Plugin output[\s]*:/i){
@splitLineTest2 = split(/Plugin output[\s]*:/, $splitLineTest[1]);
if ($splitLineTest2[0] =~ m/none/i){
$listOfItemsTest[5] = "None";
}
elsif ($splitLineTest2[0] =~ m/low/i){
$listOfItemsTest[5] = "Low";
}
elsif ($splitLineTest2[0] =~ m/medium/i){
$listOfItemsTest[5] = "Medium";
}
elsif ($splitLineTest2[0] =~ m/high/i){
$listOfItemsTest[5] = "High";
}
}
elsif ($paragraphTest =~ m/CVE[\s]*:/i){
@splitLineTest2 = split(/CVE[\s]*:/, $splitLineTest[1]);
if ($splitLineTest2[0] =~ m/none/i){
$listOfItemsTest[5] = "None";
}
elsif ($splitLineTest2[0] =~ m/low/i){
$listOfItemsTest[5] = "Low";
}
elsif ($splitLineTest2[0] =~ m/medium/i){
$listOfItemsTest[5] = "Medium";
}
elsif ($splitLineTest2[0] =~ m/high/i){
$listOfItemsTest[5] = "High";
}
}
elsif ($paragraphTest =~ m/BID[\s]*:/i){
@splitLineTest2 = split(/BID[\s]*:/, $splitLineTest[1]);
if ($splitLineTest2[0] =~ m/none/i){
$listOfItemsTest[5] = "None";
}
elsif ($splitLineTest2[0] =~ m/low/i){
$listOfItemsTest[5] = "Low";
}
elsif ($splitLineTest2[0] =~ m/medium/i){
$listOfItemsTest[5] = "Medium";
}
elsif ($splitLineTest2[0] =~ m/high/i){
$listOfItemsTest[5] = "High";
}
}
elsif ($paragraphTest =~ m/Other references[\s]*:/i){
@splitLineTest2 = split(/Other references[\s]*:/, $splitLineTest[1]);
if ($splitLineTest2[0] =~ m/none/i){
$listOfItemsTest[5] = "None";
}
elsif ($splitLineTest2[0] =~ m/low/i){
$listOfItemsTest[5] = "Low";
}
elsif ($splitLineTest2[0] =~ m/medium/i){
$listOfItemsTest[5] = "Medium";
}
elsif ($splitLineTest2[0] =~ m/high/i){
$listOfItemsTest[5] = "High";
}
}
# if there arent any more delimiters
else{
if ($splitLineTest2[1] =~ m/none/i){
$listOfItemsTest[5] = "None";
}
elsif ($splitLineTest2[1] =~ m/low/i){
$listOfItemsTest[5] = "Low";
}
elsif ($splitLineTest2[1] =~ m/medium/i){
$listOfItemsTest[5] = "Medium";
}
elsif ($splitLineTest2[1] =~ m/high/i){
$listOfItemsTest[5] = "High";
}
}
}
# Parse out the CVSS Base Score verbiage, if any
if ($paragraphTest =~ m/CVSS Base Score[\s]*:/i){
@splitLineTest = split(/CVSS Base Score[\s]*:/, $paragraphTest);
if ($paragraphTest =~ m/Plugin output[\s]*:/i){
@splitLineTest2 = split(/Plugin output[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[6] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/CVE[\s]*:/i){
@splitLineTest2 = split(/CVE[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[6] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/BID[\s]*:/i){
@splitLineTest2 = split(/BID[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[6] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Other references[\s]*:/i){
@splitLineTest2 = split(/Other references[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[6] = $splitLineTest2[0];
}
# This should never happen, but just in case
else{
$listOfItemsTest[6] = $splitLineTest[1];
}
}
# Parse out the Plugin output verbiage, if any
if ($paragraphTest =~ m/Plugin output[\s]*:/i){
@splitLineTest = split(/Plugin output[\s]*:/, $paragraphTest);
if ($paragraphTest =~ m/CVE[\s]*:/i){
@splitLineTest2 = split(/CVE[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[7] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/BID[\s]*/i){
@splitLineTest2 = split(/BID[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[7] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Other references[\s]*:/i){
@splitLineTest2 = split(/Other references[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[7] = $splitLineTest2[0];
}
# This should never happen, but just in case
else{
$listOfItemsTest[7] = $splitLineTest[1];
}
}
# Parse out the CVE verbiage, if any
if ($paragraphTest =~ m/CVE[\s]*:/i){
@splitLineTest = split(/CVE[\s]*:/, $paragraphTest);
if ($paragraphTest =~ m/BID[\s]*:/i){
@splitLineTest2 = split(/BID[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[8] = $splitLineTest2[0];
}
elsif ($paragraphTest =~ m/Other references[\s]*:/i){
@splitLineTest2 = split(/Other references[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[8] = $splitLineTest2[0];
}
# This should never happen, but just in case
else{
$listOfItemsTest[8] = $splitLineTest[1];
}
}
# Parse out the BID verbiage, if any
if ($paragraphTest =~ m/BID[\s]*:/i){
@splitLineTest = split(/BID[\s]*:/, $paragraphTest);
if ($paragraphTest =~ m/Other references[\s]*:/i){
@splitLineTest2 = split(/Other references[\s]*:/, $splitLineTest[1]);
$listOfItemsTest[9] = $splitLineTest2[0];
}
# This should never happen, but just in case
else{
$listOfItemsTest[9] = $splitLineTest[1];
}
}
# Parse out the Other references verbiage, if any
if ($paragraphTest =~ m/Other references[\s]*:/i){
@splitLineTest = split(/Other references[\s]*:/, $paragraphTest);
$listOfItemsTest[10] = $splitLineTest[1];
}
# One-Offs
# If there are one-off cases, where the generic rules don't apply, place the code here
return @listOfItemsTest;
} # Ends sub parse_paragraph_test
# Sorts by IP Address then Risk
# Expected Input format is an array of strings
# The strings are delimited by the vertical bar: |
sub sortIPAndRisk{
my @listOfVulns1 = @_;
my (@oldSorted1, @fields1, @fields2, $padIP1, $padIP2, $res1, $res2);
@oldSorted1 = sort{@fields1 = parse_vertical_bar($a);
@fields2 = parse_vertical_bar($b);
$padIP1 = padIP($fields1[0]);
$padIP2 = padIP($fields2[0]);
$res1 = $padIP1 cmp $padIP2;
# if the IP addresses are not the same
if ($res1 != 0){
return $res1;
}
# if the IP addresses are the same, sort based on risk
else{
$res2 = 0;
return $res2;
}
} @listOfVulns1;
return @oldSorted1;
}
# Compares the the two risk factor strings passed and returns 1, 0, -1 based on the following:
# High < Medium < Low < None <
sub riskCompare{
my $risk1 = shift;
my $risk2 = shift;
my $one = 1;
my $zero = 0;
my $negative = 0 - 1;
my $retV;
# The two strings are equal
if (($risk1 cmp $risk2) == 0){
$retV = $risk1 cmp $risk2;
return $retV;
}
if ($risk1 eq 'High'){
return $negative;
}
elsif ($risk1 eq 'Medium'){
if ($risk2 eq 'High'){
return 1;
}
else{
return $negative;
}
}
elsif ($risk1 eq 'Low'){
if (($risk2 eq 'High') || ($risk2 eq 'Medium')){
return 1;
}
else{
return $negative;
}
}
elsif ($risk1 eq 'None'){
if (($risk2 eq 'High') || ($risk2 eq 'Medium') || ($risk2 eq 'Low')){
return 1;
}
else{
return $negative;
}
}
else{
if (($risk2 eq 'High') || ($risk2 eq 'Medium') || ($risk2 eq 'Low')){
return $negative;
}
else{
return $risk1 cmp $risk2;
}
}
# Should never happen
return 0;
}
# Sorts by IP Address then Port
# Expected Input format is an array
# @_[0] = IP Address Ex: 192.168.1.1
# @_[1] = Port Ex: 80/tcp
# @_[2] = Service Detected Ex: http
sub sortIPs{
my @listOfVulns = @_;
my ($port1, $port2, $padIP1, $padIP2, $res1, @fields1, @fields2, @listOfVulns, @oldSorted);
@oldSorted = sort{@fields1 = parse_csv($a);
@fields2 = parse_csv($b);
$padIP1 = padIP($fields1[0]);
$padIP2 = padIP($fields2[0]);
$res1 = $padIP1 cmp $padIP2;
# if the IP addresses are not the same
if ($res1 != 0){
return $res1;
}
# if the IP addresses are the same, sort based on port number
else{
$port1 = portNum($fields1[1]);
$port2 = portNum($fields2[1]);
return $port1 <=> $port2;
}
} @listOfVulns;
return @oldSorted;
}
# Takes in a string / and returns the port number only
# Ex: 80/tcp
sub portNum{
my $portString = shift;
my @splitText = split(/\//, $portString);
return $splitText[0];
}
# Pads an IP address string with 0's for sorting purposes
sub padIP{
my $ipIn = shift;
my ($line2, $len, $len2);
my @splitLine = split(/\./, $ipIn);
foreach my $splitS (@splitLine){
$len = length($splitS);
if ($len == 1){
$line2 = $line2 . "00" . $splitS;
}
elsif ($len2 == 2){
$line2 = $line2 . "0" . $splitS;
}
else{
$line2 = $line2 . $splitS;
}
$line2 = $line2 . ".";
}
chop($line2);
return $line2;
}
# Takes in a string and parses by the vertical bar character: |
sub parse_vertical_bar{
my $text = shift;
my @new = ();
push(@new, $+)
while $text =~m{
"([^\"\\]*(?:\\.[^\"\\]*)*)",?
| ([^|]+),?
|,
}gx;
push(@new,undef)
if substr($text, -1, 1) eq '|';
return @new;
}
# Takes in a string and parses by the comma character: ,
sub parse_csv{
my $text = shift;
my @new = ();
push(@new, $+)
while $text =~m{
"([^\"\\]*(?:\\.[^\"\\]*)*)",?
| ([^,]+),?
|,
}gx;
push(@new,undef)
if substr($text, -1, 1) eq ',';
return @new;
}
# Return an array of the strings in port/service element field
sub parse_port_element{
my $whole_text = shift;
chomp $whole_text;
# Removes the Windows newline character ^M or \r
$whole_text=~s/\r//g;
# splits into:
# $first_split[0] = Service Name
# $first_split[1] = /
my @first_split = split(/ /, $whole_text);
# Remove the parenthesis around the port and protocol
$first_split[1] = substr($first_split[1],1,-1);
# splits into:
# $second_split[0] =
# $second_split[1] =