CVE-2018-11652 (published 2018-06-01T15:29Z)

CSV Injection vulnerability in Nikto 2.1.6 and earlier allows remote attackers to inject arbitrary OS commands via the Server field in an HTTP response header, which is directly injected into a CSV report.

Severity: HIGH
CVSS v2 base score: 10.0 (AV:N/AC:L/Au:N/C:C/I:C/A:C)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-1236
cwe_name: Improper Neutralization of Formula Elements in a CSV File
description: The product saves user-provided information into a Comma-Separated Value (CSV) file, but it does not neutralize or incorrectly neutralizes special elements that could be interpreted as a command when the file is opened by a spreadsheet product.
extended_description: User-provided data is often saved to traditional databases.  This data can be exported to a CSV file, which allows users to read the data using spreadsheet software such as Excel, Numbers, or Calc.  This software interprets entries beginning with '=' as formulas, which are then executed by the spreadsheet software.  The software's formula language often allows methods to access hyperlinks or the local command line, and frequently allows enough characters to invoke an entire script. Attackers can populate data fields which, when saved to a CSV file, may attempt information exfiltration or other malicious activity when automatically executed by the spreadsheet software.
			

Fix information:
repo_url: https://github.com/sullo/nikto
commit_details: repo_url: https://github.com/sullo/nikto
author: sullo
author_date: 2018-05-31 23:30:03-04:00
msg: Fix CSV injection issue if server responds with a malicious Server string & CSV output is opened in Excel or other spreadsheet app. Potentially malicious cell start characters are now prefaced with a ' mark. Thanks to Adam (@bytesoverbombs) for letting me know!

Also fixed a crash in the outdated plugin if the $sepr field ends up being something that triggers a panic in split().
repository: repo_url: https://github.com/sullo/nikto
repo_name: sullo/nikto
description: Nikto web server scanner
repo_language: Perl
owner: sullo
file_changes: filename: nikto_outdated.plugin
change_type: ModificationType.MODIFY
diff: @@ -88,7 +88,7 @@ sub nikto_outdated {
             $sepr = substr($sepr, (length($sepr) - 1), 1);
 
             # break up ID string on $sepr
-            my @T = split(/$sepr/, $mark->{'banner'});
+            my @T = split(/\\$sepr/, $mark->{'banner'});
 
             # assume last is version...
             for ($i = 0 ; $i < $#T ; $i++) { $MATCHSTRING .= "$T[$i] "; }

programming_language: Perl
filename: nikto_report_csv.plugin
change_type: ModificationType.MODIFY
diff: @@ -53,10 +53,11 @@ sub csv_host_start {
     my ($handle, $mark) = @_;
     $mark->{'banner'} =~ s/"/\\"/g;
     my $hostname = $mark->{'vhost'} ? $mark->{'vhost'} : $mark->{'hostname'};
-    print $handle "\"$hostname\","
-      . "\"$mark->{'ip'}\","
-      . "\"$mark->{'port'}\"," . "\"\"," . "\"\"," . "\"\","
-      . "\"$mark->{'banner'}\"\n";
+    print $handle "\"" . csv_safecell($hostname) . "\","
+      . "\"" . csv_safecell($mark->{'ip'}) . "\","
+      . "\"" . csv_safecell($mark->{'port'}) . "\"," . "\"\"," . "\"\"," . "\"\","
+      #. "\"" . $mark->{'banner'} . "\"\n";
+      . "\"" . csv_safecell($mark->{'banner'}) . "\"\n";
     return;
 }
 
@@ -67,33 +68,41 @@ sub csv_item {
     foreach my $uri (split(' ', $item->{'uri'})) {
         my $line = '';
     	my $hostname = $item->{'mark'}->{'vhost'} ? $item->{'mark'}->{'vhost'} : $item->{'mark'}->{'hostname'};
-        $line .= "\"$hostname\",";
-        $line .= "\"$item->{'mark'}->{'ip'}\",";
-        $line .= "\"$item->{'mark'}->{'port'}\",";
+        $line .= "\"" . csv_safecell($hostname) . "\",";
+        $line .= "\"" . csv_safecell($item->{'mark'}->{'ip'}) . \",";
+        $line .= "\"" . csv_safecell($item->{'mark'}->{'port'}) . "\",";
 
         $line .= "\"";
         if ($item->{'osvdb'} ne '') { $line .= "OSVDB-" . $item->{'osvdb'}; }
         $line .= "\",";
 
         $line .= "\"";
-        if ($item->{'method'} ne '') { $line .= $item->{'method'}; }
+        if ($item->{'method'} ne '') { $line .= csv_safecell($item->{'method'}); }
         $line .= "\",";
 
         $line .= "\"";
         if (($uri ne '') && ($mark->{'root'} ne '') && ($uri !~ /^$mark->{'root'}/))
-			{ $line .= $mark->{'root'} . $uri; }
-		else { $line .= $uri; }
+			{ $line .= csv_safecell($mark->{'root'}) . $uri; }
+		else { $line .= csv_safecell($uri); }
         $line .= "\",";
 	
-		my $msg = $item->{'message'};
-		$uri=quotemeta($uri);
-		my $root = quotemeta($mark->{'root'});
-		$msg =~ s/^$uri:\s//;
-		$msg =~ s/^$root$uri:\s//;
+	my $msg = $item->{'message'};
+	$uri=quotemeta($uri);
+	my $root = quotemeta($mark->{'root'});
+	$msg =~ s/^$uri:\s//;
+	$msg =~ s/^$root$uri:\s//;
         $msg =~ s/"/\\"/g;
-        $line .= "\"$msg\"";
+        $line .= "\"" . csv_safecell($msg) ."\"";
         print $handle "$line\n";
     }
 }
 
+###############################################################################
+# prevent CSV injection attacks
+sub csv_safecell {
+    my $celldata = $_[0] || return;
+    if ($celldata =~ /^[=+@-]/) { $celldata = "'" . $celldata; }
+    return $celldata;
+}
+
 1;

programming_language: Perl