CVE-2017-1000501: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: Awstats version 7.6 and earlier is vulnerable to a path traversal flaw in the handling of the "config" and "migrate" parameters resulting in unauthenticated remote code execution.
CWE description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVSS v3 base score: 9.8
Published: 2018-01-03T15:29Z
Repository: https://github.com/eldy/awstats
Language: Perl
Files changed: wwwroot/cgi-bin/awstats.pl
Commit date: 2017-12-27 13:39:57 +0100
Commit message: Fix another vulnerability reported by cPanel Security Team (can execute
arbitraty code)
Diff: diff --git a/wwwroot/cgi-bin/awstats.pl b/wwwroot/cgi-bin/awstats.pl
index fca4900f..4f14c6ee 100755
--- a/wwwroot/cgi-bin/awstats.pl
+++ b/wwwroot/cgi-bin/awstats.pl
@@ -17143,11 +17143,10 @@ if ( $ENV{'GATEWAY_INTERFACE'} ) {    # Run from a browser as CGI
 	# No update but report by default when run from a browser
 	$UpdateStats = ( $QueryString =~ /update=1/i ? 1 : 0 );
 
 	if ( $QueryString =~ /config=([^&]+)/i ) { 
 		$SiteConfig = &Sanitize("$1");
-		$SiteConfig =~ s/\.\.//g; 		# Avoid directory transversal
 	}
 	if ( $QueryString =~ /diricons=([^&]+)/i ) { $DirIcons = "$1"; }
 	if ( $QueryString =~ /pluginmode=([^&]+)/i ) {
 		$PluginMode = &Sanitize( "$1", 1 );
 	}
@@ -17189,26 +17188,30 @@ if ( $ENV{'GATEWAY_INTERFACE'} ) {    # Run from a browser as CGI
 	} # Filter on referer list can be defined with output=refererpages:filter to reduce number of lines read and showed
 
 	# If migrate
 	if ( $QueryString =~ /(^|-|&|&amp;)migrate=([^&]+)/i ) {
 		$MigrateStats = &Sanitize("$2");
+
 		$MigrateStats =~ /^(.*)$PROG(\d{0,2})(\d\d)(\d\d\d\d)(.*)\.txt$/;
-		$SiteConfig = $5 ? $5 : 'xxx';
+		$SiteConfig = &Sanitize($5 ? $5 : 'xxx');
 		$SiteConfig =~ s/^\.//;    # SiteConfig is used to find config file
 	}
+
+	$SiteConfig =~ s/\.\.//g; 		# Avoid directory transversal
 }
 else {                             # Run from command line
 	$DebugMessages = 1;
 
 	# Prepare QueryString
 	for ( 0 .. @ARGV - 1 ) {
 
 		# If migrate
 		if ( $ARGV[$_] =~ /(^|-|&|&amp;)migrate=([^&]+)/i ) {
-			$MigrateStats = "$2";
+			$MigrateStats = &Sanitize("$2");
+
 			$MigrateStats =~ /^(.*)$PROG(\d{0,2})(\d\d)(\d\d\d\d)(.*)\.txt$/;
-			$SiteConfig = $5 ? $5 : 'xxx';
+			$SiteConfig = &Sanitize($5 ? $5 : 'xxx');
 			$SiteConfig =~ s/^\.//;    # SiteConfig is used to find config file
 			next;
 		}
 
 		# TODO Check if ARGV is in @AllowedArg
@@ -17233,11 +17236,10 @@ else {                             # Run from command line
 	# Update with no report by default when run from command line
 	$UpdateStats = 1;
 
 	if ( $QueryString =~ /config=([^&]+)/i ) { 
 		$SiteConfig = &Sanitize("$1"); 
-		$SiteConfig =~ s/\.\.//g; 
 	}
 	if ( $QueryString =~ /diricons=([^&]+)/i ) { $DirIcons = "$1"; }
 	if ( $QueryString =~ /pluginmode=([^&]+)/i ) {
 		$PluginMode = &Sanitize( "$1", 1 );
 	}
@@ -17299,10 +17301,12 @@ else {                             # Run from command line
 	}
 	if ( $QueryString =~ /showdirectorigin/i ) {
 		$ShowDirectOrigin = 1;
 		$QueryString =~ s/showdirectorigin[^&]*//i;
 	}
+	
+	$SiteConfig =~ s/\.\.//g; 
 }
 if ( $QueryString =~ /(^|&|&amp;)staticlinks/i ) {
 	$StaticLinks = "$PROG.$SiteConfig";
 }
 if ( $QueryString =~ /(^|&|&amp;)staticlinks=([^&]+)/i ) {

Vulnerable code: 		$SiteConfig =~ s/\.\.//g; 		# Avoid directory transversal
		$SiteConfig = $5 ? $5 : 'xxx';
			$MigrateStats = "$2";
			$SiteConfig = $5 ? $5 : 'xxx';
		$SiteConfig =~ s/\.\.//g; 
Fixed code: 
		$SiteConfig = &Sanitize($5 ? $5 : 'xxx');

	$SiteConfig =~ s/\.\.//g; 		# Avoid directory transversal
			$MigrateStats = &Sanitize("$2");

			$SiteConfig = &Sanitize($5 ? $5 : 'xxx');
	
	$SiteConfig =~ s/\.\.//g;