#!/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] =