CVE-2016-2539: Cross-Site Request Forgery (CSRF) (CWE-352)
value: Cross-site request forgery (CSRF) vulnerability in install_modules.php in ATutor before 2.2.2 allows remote attackers to hijack the authentication of users for requests that upload arbitrary files and execute arbitrary PHP code via vectors involving a crafted zip file.
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 8.8
Published: 2017-02-07T15:59Z
Repository: https://github.com/atutor/ATutor
Language: PHP
Files changed: mods/_core/modules/install_modules.php, themes/default/admin/modules/install_modules.tmpl.php
Commit date: 2016-03-05 14:12:46 -0500
Commit message: 5632,5644 removed array_multisort to fix module install issues, and added csrftoken to fix postential CSRF attack

Diff: diff --git a/mods/_core/modules/install_modules.php b/mods/_core/modules/install_modules.php
index 3ad709f566..69fcd5a797 100644
--- a/mods/_core/modules/install_modules.php
+++ b/mods/_core/modules/install_modules.php
@@ -16,10 +16,12 @@ define('AT_INCLUDE_PATH', '../../../include/');
 require (AT_INCLUDE_PATH.'vitals.inc.php');
 admin_authenticate(AT_ADMIN_PRIV_MODULES);
 require(AT_INCLUDE_PATH.'../mods/_core/modules/classes/ModuleListParser.class.php');
 require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php');
 // delete all folders and files in $dir
+
+
 function clear_dir($dir)
 {
 	if ($dh = opendir($dir)) 
 	{
 		while (($file = readdir($dh)) !== false)
@@ -152,12 +154,19 @@ else if (isset($_POST['install']) || isset($_POST["download"]) || isset($_POST["
 					$msg->addError('ALREADY_INSTALLED');
 			}
 
 			if (!$msg->containsErrors())
 			{
-				header('Location: module_install_step_1.php?mod='.urlencode($module_folder).SEP.'new=1');
-				exit;
+			        if($_POST['csrftoken'] != $_SESSION['token']){
+                        $msg->addError('ACCESS_DENIED');
+                    } else {
+                    
+                        header('Location: module_install_step_1.php?mod='.urlencode($module_folder).SEP.'new=1');                        
+                        exit;
+                    }
+				//header('Location: module_install_step_1.php?mod='.urlencode($module_folder).SEP.'new=1');
+				//exit;
 			}
 		}
 		
 		if ($_POST['download'])
 		{
@@ -179,12 +188,17 @@ else if (isset($_POST['install']) || isset($_POST["download"]) || isset($_POST["
 
 if (isset($_POST['mod'])) {
 	$dir_name = str_replace(array('.','..'), '', $_POST['mod']);
 
 	if (isset($_POST['install_manually'])) {
-		header('Location: '.AT_BASE_HREF.'mods/_core/modules/module_install_step_2.php?mod='.urlencode($dir_name).SEP.'new=1'.SEP.'mod_in=1');
-		exit;
+	// Check for potential  CSRF
+        if($_POST['csrftoken'] != $_SESSION['token']){
+                $msg->addError('ACCESS_DENIED');
+        } else {
+            header('Location: '.AT_BASE_HREF.'mods/_core/modules/module_install_step_2.php?mod='.urlencode($dir_name).SEP.'new=1'.SEP.'mod_in=1');
+            exit;
+        }
 	}
 
 } else if (isset($_POST['install_manually'])) {
 	$msg->addError('NO_ITEM_SELECTED');
 }
@@ -253,20 +267,22 @@ $rows_installed_mods = queryDB($sql, array(TABLE_PREFIX));
         $sort_versions = array_unique($version, SORT_REGULAR);
         rsort($sort_versions);
 
     // Add $module_list_array as the last parameter, to sort by the common key
     // Sorts by original $module_list_array by reference, then returns true|false
-    $sort_by_version = array_multisort($version, SORT_DESC, $module_list_array);
+    //$sort_by_version = array_multisort($version, SORT_DESC, $module_list_array);
 
 // Create menu for filter ATutor versions
-function select_atversion(){ 
+function select_atversion($v=0){ 
     global $sort_versions;
     $menu = '<form action="'.$_SERVER['PHP_SELF'].'" method="post">'; 
     $menu.= '<select name="atversions">';
     $menu.= '<option value="0">'._AT("all").'</option>';
     foreach($sort_versions as $version){
-        if($version == VERSION){
+        if($version == $v){
+            $menu .= '<option value="'.$version.'" selected="selected">'.$version.'</option>';
+        }else if($version == VERSION){
             $menu .= '<option value="'.$version.'" selected="selected">'.$version.'</option>';
         }else{
             $menu .= '<option value="'.$version.'" >'.$version.'</option>';
         }
     }
diff --git a/themes/default/admin/modules/install_modules.tmpl.php b/themes/default/admin/modules/install_modules.tmpl.php
index f181d7039a..562e0f4f24 100644
--- a/themes/default/admin/modules/install_modules.tmpl.php
+++ b/themes/default/admin/modules/install_modules.tmpl.php
@@ -6,10 +6,11 @@ if ($this->enable_upload) {
 <div class="input-form">
 		<div class="row"><?php echo _AT("upload_module"); ?></div>
 
 		<div class="row">
 			<input type="hidden" name="MAX_FILE_SIZE" value="52428800" />
+			<input type="hidden" name="csrftoken"  value="<?php echo $_SESSION['token'];?>" />
 			<input type="file" name="modulefile"  size="50" />
 		</div>
 		
 		<div class="row buttons">
 			<input type="submit" name="install_upload" value="<?php echo _AT('install'); ?>" onclick="javascript: return validate_filename(); " class="submit" />
@@ -24,10 +25,11 @@ if ($this->enable_upload) {
 
 if (count($this->keys) > 0)
 {
 ?>
 <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" name="installform">
+<input type="hidden" name="csrftoken"  value="<?php echo $_SESSION['token'];?>" />
 <table class="data" summary="">
 <thead>
 <tr>
 	<th scope="col">&nbsp;</th>
 	<th scope="col"><?php echo _AT('module_name'); ?></th>
@@ -80,14 +82,27 @@ if ($this->enable_remote_installation === true) {
     <legend><?php echo _AT('filter'); ?></legend>
     <div class="input-form">
     <div class="row">
     <?php echo _AT('old_module_notes'); ?>
     </div>
-<?php echo select_atversion(); ?>
+<?php 
+
+if(isset($_POST['atversions'])){
+    $v = htmlspecialchars($_POST['atversions']);
+    $_SESSION['atversion'] = $_POST['atversions'];
+} elseif(isset($_SESSION['atversion'] )){
+    $v = substr($_SESSION['atversion'], 0, 3);
+} else {
+    $v = substr(VERSION, 0, 3);
+}
+echo select_atversion($v); 
+
+?>
 </div>
 </fieldset>
 <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" name="form">
+<input type="hidden" name="csrftoken"  value="<?php echo $_SESSION['token'];?>" />
 <table class="data" summary="">
 <thead>
 	<tr>
 		<th scope="col">&nbsp;</th>
 		<th scope="col"><?php echo _AT('module_name');?></th>

Vulnerable code: 				header('Location: module_install_step_1.php?mod='.urlencode($module_folder).SEP.'new=1');
				exit;
		header('Location: '.AT_BASE_HREF.'mods/_core/modules/module_install_step_2.php?mod='.urlencode($dir_name).SEP.'new=1'.SEP.'mod_in=1');
		exit;
    $sort_by_version = array_multisort($version, SORT_DESC, $module_list_array);
function select_atversion(){ 
        if($version == VERSION){
<?php echo select_atversion(); ?>
Fixed code: 

			        if($_POST['csrftoken'] != $_SESSION['token']){
                        $msg->addError('ACCESS_DENIED');
                    } else {
                    
                        header('Location: module_install_step_1.php?mod='.urlencode($module_folder).SEP.'new=1');                        
                        exit;
                    }
				//header('Location: module_install_step_1.php?mod='.urlencode($module_folder).SEP.'new=1');
				//exit;
	// Check for potential  CSRF
        if($_POST['csrftoken'] != $_SESSION['token']){
                $msg->addError('ACCESS_DENIED');
        } else {
            header('Location: '.AT_BASE_HREF.'mods/_core/modules/module_install_step_2.php?mod='.urlencode($dir_name).SEP.'new=1'.SEP.'mod_in=1');
            exit;
        }
    //$sort_by_version = array_multisort($version, SORT_DESC, $module_list_array);
function select_atversion($v=0){ 
        if($version == $v){
            $menu .= '<option value="'.$version.'" selected="selected">'.$version.'</option>';
        }else if($version == VERSION){
			<input type="hidden" name="csrftoken"  value="<?php echo $_SESSION['token'];?>" />
<input type="hidden" name="csrftoken"  value="<?php echo $_SESSION['token'];?>" />
<?php 

if(isset($_POST['atversions'])){
    $v = htmlspecialchars($_POST['atversions']);
    $_SESSION['atversion'] = $_POST['atversions'];
} elseif(isset($_SESSION['atversion'] )){
    $v = substr($_SESSION['atversion'], 0, 3);
} else {
    $v = substr(VERSION, 0, 3);
}
echo select_atversion($v); 

?>
<input type="hidden" name="csrftoken"  value="<?php echo $_SESSION['token'];?>" />