CVE-2023-0048 (published 2023-01-04T14:15Z)

Code Injection in GitHub repository lirantal/daloradius prior to master-branch.

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-94
cwe_name: Improper Control of Generation of Code ('Code Injection')
description: The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
extended_description: 
            When a product allows a user's input to contain code syntax, it might be possible for an attacker to craft the code in such a way that it will alter the intended control flow of the product. Such an alteration could lead to arbitrary code execution.
            Injection problems encompass a wide variety of issues -- all mitigated in very different ways. For this reason, the most effective way to discuss these weaknesses is to note the distinct features which classify them as injection weaknesses. The most important issue to note is that all injection problems share one thing in common -- i.e., they allow for the injection of control plane data into the user-controlled data plane. This means that the execution of the process may be altered by sending code in through legitimate data channels, using no other mechanism. While buffer overflows, and many other flaws, involve the use of some further issue to gain execution, injection problems need only for the data to be parsed. The most classic instantiations of this category of weakness are SQL injection and format string vulnerabilities.
         
         

Fix information:
repo_url: https://github.com/lirantal/daloradius
commit_details: repo_url: https://github.com/lirantal/daloradius
author: Filippo
author_date: 2023-01-04 13:58:17+01:00
msg: Validating/sanitizing user input (#327)

* Better validating log filepath

* improving configuration read/write function

* SMTP configuration parameter validation

* Input validation/sanitization fixes

* minor fix
repository: repo_url: https://github.com/lirantal/daloradius
repo_name: lirantal/daloradius
description: daloRADIUS is an advanced RADIUS web management application for managing hotspots and general-purpose ISP deployments. It features user management, graphical reporting, accounting, a billing engine, and integrates with OpenStreetMap for geolocation. The system is based on FreeRADIUS with which it shares access to the backend database.
repo_language: PHP
owner: lirantal
file_changes: filename: config-backup-managebackups.php
change_type: ModificationType.MODIFY
diff: @@ -36,7 +36,29 @@
     include("library/layout.php");
     include_once("include/management/functions.php");
 
-    $file = (array_key_exists('file', $_POST) && isset($_POST['file'])) ? $_POST['file'] : "";
+    // validate path
+    $backup_path_prefix = $configValues['CONFIG_PATH_DALO_VARIABLE_DATA'] . "/backup";
+    $backup_file_suffix = ".sql";
+
+    $file = "";
+    if (array_key_exists('file', $_POST) && !empty(trim($_POST['file']))) {
+        $candidate_backup_file = trim($_POST['file']);
+        
+        if (
+                // this ensures that candidate_backup_file does not contain any ".." sequence
+                strpos($candidate_backup_file, "..") === false &&
+                
+                // this ensures that candidate_backup_file does not contain any "/" char
+                strpos($candidate_backup_file, "/") === false &&
+                
+                // this ensures that candidate_backup_file ends with the backup_file_suffix
+                substr($candidate_backup_file, -strlen($backup_file_suffix)) === $backup_file_suffix
+           ) {
+            
+            $file = $candidate_backup_file;
+        }
+        
+    }
 
     $backupAction = (array_key_exists('action', $_POST) && isset($_POST['action']) &&
                      in_array($_POST['action'], array_keys($valid_backupActions))) ? $_POST['action'] : "";
@@ -63,15 +85,14 @@
                ? strtolower($_GET['orderType']) : "asc";
 
     // init backup paths
-    $filePath = $configValues['CONFIG_PATH_DALO_VARIABLE_DATA'] . "/backup";
-    $fileName = sprintf("%s/%s", $filePath, $file);
+    $fileName = sprintf("%s/%s", $backup_path_prefix, $file);
     $baseFile = basename($fileName);
     
     if ($_SERVER['REQUEST_METHOD'] === 'POST') {
         
         if (array_key_exists('csrf_token', $_POST) && isset($_POST['csrf_token']) && dalo_check_csrf_token($_POST['csrf_token'])) {
             
-            if (!empty($file) && !empty($backupAction) && is_dir($filePath) && is_readable($fileName)) {
+            if (!empty($file) && !empty($backupAction) && is_dir($backup_path_prefix) && is_readable($fileName)) {
         
                 $fileContents = file_get_contents($fileName);
                 $fileLen = strlen($fileContents);
@@ -163,6 +184,9 @@
                     $logAction .= "$failureMsg on page: ";
                 }
 
+            } else {
+                $failureMsg = sprintf("The requested action cannot be performed");
+                $logAction .= "$failureMsg on page: ";
             }
 
 
@@ -191,29 +215,29 @@
     // get backup info
     $backupInfo = array();
     
-    if (is_dir($filePath)) {
-        $files = scandir($filePath);
+    if (is_dir($backup_path_prefix)) {
+        $files = scandir($backup_path_prefix);
         if ($orderType == "desc") {
             rsort($files);
         }
     
         $skipList = array( ".", "..", ".svn", ".git" );
-        foreach ($files as $file) {
+        foreach ($files as $this_file) {
             
-            if (in_array($file, $skipList)) {
+            if (in_array($this_file, $skipList)) {
                 continue;
             }
 
-            list($junk, $date, $time) = explode("-", $file);
+            list($junk, $date, $time) = explode("-", $this_file);
             
             $fileDate = substr($date, 0, 4) . "-" . substr($date, 4, 2) . "-" . substr($date, 6, 2);
             $fileTime = substr($time, 0, 2) . ":" . substr($time, 2, 2) . ":" . substr($time, 4, 2);
 
-            $fileSize = filesize($filePath."/".$file);
+            $fileSize = filesize(sprintf("%s/%s", $backup_path_prefix, $this_file));
             
             $backupInfo[] = array(
                                     sprintf("%s, %s", $fileDate, $fileTime),
-                                    $file,
+                                    $this_file,
                                     toxbyte($fileSize),
                                  );
             

programming_language: PHP
filename: config-logging.php
change_type: ModificationType.MODIFY
diff: @@ -71,7 +71,7 @@
                         // this ensures that candidate_log_file starts with the log_path_prefix
                         substr($candidate_log_file, 0, strlen($log_path_prefix)) === $log_path_prefix &&
                         
-                        // this ensures that candidate_log_file does not contain ".."
+                        // this ensures that candidate_backup_file does not contain any ".." sequence
                         strpos($candidate_log_file, "..") === false &&
                         
                         // this ensures that candidate_log_file ends with the log_file_suffix

programming_language: PHP
filename: config-mail.php
change_type: ModificationType.MODIFY
diff: @@ -25,28 +25,80 @@
     $operator = $_SESSION['operator_user'];
 
     include('library/check_operator_perm.php');
-
     include_once('library/config_read.php');
+    
+    // init logging variables
     $log = "visited page: ";
-
+    $logAction = "";
+    $logDebugSQL = "";
+    
     include_once("lang/main.php");
+    include("library/validation.php");
+    include("library/layout.php");
 
-    if (isset($_REQUEST['submit'])) {
+    $param_label = array(
+                            'CONFIG_MAIL_SMTPADDR' => t('all','SMTPServerAddress'),
+                            'CONFIG_MAIL_SMTPPORT' => t('all','SMTPServerPort'),
+                            'CONFIG_MAIL_SMTPFROM' => t('all','SMTPServerFromEmail'),
+                        );
 
-        if (isset($_REQUEST['config_mail_smtpaddr']))
-            $configValues['CONFIG_MAIL_SMTPADDR'] = $_REQUEST['config_mail_smtpaddr'];
-        
-        if (isset($_REQUEST['config_mail_smtpport']))
-            $configValues['CONFIG_MAIL_SMTPPORT'] = $_REQUEST['config_mail_smtpport'];
-            
-        if (isset($_REQUEST['config_mail_smtp_fromemail']))
-            $configValues['CONFIG_MAIL_SMTPFROM'] = $_REQUEST['config_mail_smtp_fromemail'];
-        
-        include ("library/config_write.php");
-    }    
+    $invalid_input = array();
 
-    include("library/layout.php");
+    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+        if (array_key_exists('csrf_token', $_POST) && isset($_POST['csrf_token']) && dalo_check_csrf_token($_POST['csrf_token'])) {
+            
+            // validate email
+            if (
+                    array_key_exists('CONFIG_MAIL_SMTPFROM', $_POST) &&
+                    !empty(trim($_POST['CONFIG_MAIL_SMTPFROM'])) &&
+                    filter_var(trim($_POST['CONFIG_MAIL_SMTPFROM']), FILTER_VALIDATE_EMAIL)
+               ) {
+                $configValues['CONFIG_MAIL_SMTPFROM'] = trim($_POST['CONFIG_MAIL_SMTPFROM']);
+            } else {
+                $invalid_input['CONFIG_MAIL_SMTPFROM'] = $param_label['CONFIG_MAIL_SMTPFROM'];
+            }
+            
+            // validate port
+            if (
+                    array_key_exists('CONFIG_MAIL_SMTPPORT', $_POST) &&
+                    !empty(trim($_POST['CONFIG_MAIL_SMTPPORT'])) &&
+                    intval(trim($_POST['CONFIG_MAIL_SMTPPORT'])) >= 0 &&
+                    intval(trim($_POST['CONFIG_MAIL_SMTPPORT'])) <= 65535
+               ) {
+                $configValues['CONFIG_MAIL_SMTPPORT'] = intval(trim($_POST['CONFIG_MAIL_SMTPPORT']));
+            } else {
+                $invalid_input['CONFIG_MAIL_SMTPPORT'] = $param_label['CONFIG_MAIL_SMTPPORT'];
+            }
+            
+            // validate ip address/hostname
+            if (
+                    array_key_exists('CONFIG_MAIL_SMTPADDR', $_POST) &&
+                    !empty(trim($_POST['CONFIG_MAIL_SMTPADDR'])) &&
+                    (
+                        preg_match(HOSTNAME_REGEX, trim($_POST['CONFIG_MAIL_SMTPADDR'])) ||
+                        preg_match(IP_REGEX, trim($_POST['CONFIG_MAIL_SMTPADDR']))
+                    )
+               ) {
+                $configValues['CONFIG_MAIL_SMTPADDR'] = trim($_POST['CONFIG_MAIL_SMTPADDR']);
+            } else {
+                $invalid_input['CONFIG_MAIL_SMTPADDR'] = $param_label['CONFIG_MAIL_SMTPADDR'];
+            }
+            
+            if (count($invalid_input) > 0) {
+                $failureMsg = sprintf("Invalid input: [%s]", implode(", ", array_values($invalid_input)));
+                $logAction .= "$failureMsg on page: ";
+            } else {
+                include("library/config_write.php");
+            }
+            
+        } else {
+            // csrf
+            $failureMsg = "CSRF token error";
+            $logAction .= "$failureMsg on page: ";
+        }
+    }
 
+    
     // print HTML prologue
     $title = t('Intro','configmail.php');
     $help = t('helpPage','configmail');
@@ -60,50 +112,66 @@
     
     include_once('include/management/actionMessages.php');
 
-?>
-
-<form name="mailsettings" method="POST">
-    <fieldset>
-        <h302><?= t('title','Settings'); ?></h302>
-        
-        <br/>
-
-        <ul>
-
-            <li class="fieldset">
-                <label for="config_mail_smtpaddr" class="form"><?= t('all','SMTPServerAddress') ?></label>
-                <input type="text" value="<?= $configValues['CONFIG_MAIL_SMTPADDR'] ?>" name="config_mail_smtpaddr">
-            </li>
+    $fieldset0_descriptor = array(
+                                    "title" => t('title','Settings')
+                                 );
 
-            <li class="fieldset">
-                <label for="config_mail_smtpport" class="form"><?= t('all','SMTPServerPort') ?></label>
-                <input type="number" min="0" max="65535" value="<?= $configValues['CONFIG_MAIL_SMTPPORT'] ?>" name="config_mail_smtpport">
-            </li>
-
-            <li class="fieldset">
-                <label for="config_mail_smtp_fromemail" class="form"><?= t('all','SMTPServerFromEmail') ?></label>
-                <input type="text" value="<?= $configValues['CONFIG_MAIL_SMTPFROM'] ?>" name="config_mail_smtp_fromemail">
-            </li>
-
-
-            <li class="fieldset">
-                <br/><hr><br/>
-                <input type="submit" name="submit" value="<?= t('buttons','apply') ?>" class="button">
-            </li>
-        </ul>
-    </fieldset>
-</form>
-
-        </div><!-- #contentnorightbar -->
-        
-        <div id="footer">
-<?php
+    $input_descriptors0 = array();
+    
+    
+    $input_descriptors0[] = array(
+                                        "type" => "text",
+                                        "caption" => t('all','SMTPServerAddress'),
+                                        "name" => 'CONFIG_MAIL_SMTPADDR',
+                                        "value" => (!array_key_exists('CONFIG_MAIL_SMTPADDR', $invalid_input)
+                                                    ? $configValues['CONFIG_MAIL_SMTPADDR'] : "")
+                                     );
+    
+    $input_descriptors0[] = array(
+                                        "type" => "number",
+                                        "caption" => t('all','SMTPServerPort'),
+                                        "name" => 'CONFIG_MAIL_SMTPPORT',
+                                        "value" => (!array_key_exists('CONFIG_MAIL_SMTPPORT', $invalid_input)
+                                                    ? $configValues['CONFIG_MAIL_SMTPPORT'] : ""),
+                                        "min" => 0,
+                                        "max" => 65535
+                                 );
+    
+    $input_descriptors0[] = array(
+                                        "type" => "email",
+                                        "caption" => t('all','SMTPServerFromEmail'),
+                                        "name" => 'CONFIG_MAIL_SMTPFROM',
+                                        "value" => (!array_key_exists('CONFIG_MAIL_SMTPFROM', $invalid_input)
+                                                    ? $configValues['CONFIG_MAIL_SMTPFROM'] : ""),
+                                     );
+    
+    $input_descriptors0[] = array(
+                                    "name" => "csrf_token",
+                                    "type" => "hidden",
+                                    "value" => dalo_csrf_token(),
+                                 );
+
+    $input_descriptors0[] = array(
+                                    'type' => 'submit',
+                                    'name' => 'submit',
+                                    'value' => t('buttons','apply')
+                                 );
+
+    open_form();
+    
+    // open 0-th fieldset
+    open_fieldset($fieldset0_descriptor);
+    
+    foreach ($input_descriptors0 as $input_descriptor) {
+        print_form_component($input_descriptor);
+    }
+    
+    close_fieldset();
+    
+    close_form();
+    
     include('include/config/logging.php');
-    include('page-footer.php');
-?>
-        </div><!-- #footer -->
-    </div>
-</div>
+    
+    print_footer_and_html_epilogue();
 
-</body>
-</html>
+?>

programming_language: PHP
filename: 2.css
change_type: ModificationType.MODIFY
diff: @@ -362,7 +362,7 @@ fieldset textarea {
 
 fieldset input[type=text], fieldset input[type=number],
 fieldset input[type=date], fieldset input[type=password],
-fieldset select {
+fieldset input[type=email], fieldset select {
     padding: 5px;
     margin: 4px;
     border: 1px solid var(--rich-black);
@@ -383,7 +383,7 @@ fieldset select {
 
 fieldset input[type=text]:disabled, fieldset input[type=numer]:disabled,
 fieldset input[type=date]:disabled, fieldset input[type=password]:disabled,
-fieldset select:disabled {
+fieldset input[type=email]:disabled, fieldset select:disabled {
     background-color: #eeeeee;
     border: 1px solid gray;
 }

programming_language: CSS
filename: config_read.php
change_type: ModificationType.MODIFY
diff: @@ -14,40 +14,32 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  *********************************************************************************************************
- * Description:
- * 		reads configuration file from daloradius.conf and appends it to the $configValues associated array
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Description:    reads configuration file from daloradius.conf.php and
+ *                 loads its contents to the $configValues associated array
+ *
+ * Authors:        Liran Tal <liran@enginx.com>
+ *                 Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
- 
-$_configFile = dirname(__FILE__).'/daloradius.conf.php';
-include($_configFile);
 
-/*
-****************************************************************************************************
-* deprecated for handling the configuration variables as a PHP page for the sake of security
-****************************************************************************************************
-$_configCommentChar = "#";
+// prevent this file to be directly accessed
+if (strpos($_SERVER['PHP_SELF'], '/library/config_read.php') !== false) {
+    header("Location: ../index.php");
+    exit;
+}
+
+$_configFile = dirname(__FILE__) . '/daloradius.conf.php';
+include($_configFile);
 
-$_configFp = fopen($_configFile, "r");
-if ($_configFp) {
-	while (!feof($_configFp)) {
-		$_configLine = trim(fgets($_configFp));
-		if ($_configLine && !ereg("^$_configCommentChar", $_configLine)) {
-			$_configPieces = explode("=", $_configLine);
-			$_configOption = trim($_configPieces[0]);
-			$_configValue = trim($_configPieces[1]);
-			$configValues[$_configOption] = $_configValue;
-		}
-	}
-	fclose($_configFp);
-} else {
-	$failureMsg = "Could not open the file for reading:<b> $_configFile </b>
-	<br/>Check file permissions. The file should be readable by the webserver's user/group";
+// strip slashes (if any)
+foreach ($configValues as $_configOption => $_configElem) {
+    if (is_array($_configElem)) {
+        continue;
+    }
+    
+    $configValues[$_configOption] = stripslashes($_configElem);
 }
-****************************************************************************************************
-*/
 
 ?>

programming_language: PHP
filename: config_write.php
change_type: ModificationType.MODIFY
diff: @@ -14,59 +14,93 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  *********************************************************************************************************
- * Description:
- * 		writes configuration information from the $configValues array to daloradius.conf
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Description:    writes configuration information from the $configValues array
+ *                 to the daloradius.conf.php configuration file
+ * 
+ * Authors:        Liran Tal <liran@enginx.com>
+ *                 Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
 
-$configFile = dirname(__FILE__).'/daloradius.conf.php';
+// prevent this file to be directly accessed
+if (strpos($_SERVER['PHP_SELF'], '/library/config_write.php') !== false) {
+    header("Location: ../index.php");
+    exit;
+}
+
+// useful variables
+$configFile = dirname(__FILE__) . '/daloradius.conf.php';
 $date = date("D M j G:i:s T Y");
 
-$fp = fopen($configFile, "w");
-if ($fp) {
-	fwrite($fp, 
-		"<?php\n".
-		"/*\n".
-		" *********************************************************************************************************\n".
-		" * daloRADIUS - RADIUS Web Platform\n".
-		" * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.\n".
-		" *\n".
-		" * This program is free software; you can redistribute it and/or\n".
-		" * modify it under the terms of the GNU General Public License\n".
-		" * as published by the Free Software Foundation; either version 2\n".
-		" * of the License, or (at your option) any later version.\n".
-		" *\n".
-		" * You should have received a copy of the GNU General Public License\n".
-		" * along with this program; if not, write to the Free Software\n".
-		" * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n".
-		" *\n".
-		" *********************************************************************************************************\n".
-		" * Description:\n".
-		" *              daloRADIUS Configuration File\n".
-		" *\n".
-		" * Modification Date:\n".
-		" *              $date\n".
-		" *********************************************************************************************************\n".
-		" */\n".
-		"\n\n");
-	foreach ($configValues as $_configOption => $_configElem) {
-		if (is_array($configValues[$_configOption])) {
-			$var = "\$configValues['" . $_configOption . "'] = \t\t";
-			$var .= var_export($configValues[$_configOption], true);
-			$var .= ";\n";
-			fwrite($fp, $var);
-		} else
-	        fwrite($fp, "\$configValues['" . $_configOption . "'] = '" . $configValues[$_configOption] . "';\n");
-	}
-	fwrite($fp, "\n\n?>");
-	fclose($fp);
-	$successMsg = "Updated database settings for configuration file";
-} else {
-	$failureMsg = "Could not open the file for writing: <b>$configFile</b>
-	<br/>Check file permissions. The file should be writable by the webserver's user/group";
+//
+// generating file contents
+//
+
+// 1. open
+$fileContents = <<<EOL
+<?php
+/*
+ *********************************************************************************************************
+ * daloRADIUS - RADIUS Web Platform
+ * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *********************************************************************************************************
+ * 
+ * Description:          daloRADIUS Configuration File
+ *
+ * Modification Date:    {$date}
+ *
+ *********************************************************************************************************
+ */
+
+// prevent this file to be directly accessed
+if (strpos(\$_SERVER['PHP_SELF'], '/library/daloradius.conf.php') !== false) {
+    header("Location: ../index.php");
+    exit;
+}
+
+
+EOL;
+
+// 2. body
+foreach ($configValues as $_configOption => $_configElem) {
+    $fileContents .= sprintf("\$configValues['%s'] =", $_configOption);
+    
+    if (is_array($configValues[$_configOption])) {
+        $fileContents .= str_repeat(" ", 8) . sprintf("%s;\n", var_export($configValues[$_configOption], true));
+    } else {
+        $fileContents .= sprintf(" '%s';\n", addslashes($configValues[$_configOption]));
+    }
 }
 
+// 3. close
+$fileContents .= <<<EOL
+
+?>
+
+EOL;
+
+//
+// putting contents into file
+//
+$writtenBytes = intval(file_put_contents($configFile, $fileContents));
+
+if ($writtenBytes > 0) {
+    $successMsg = "Configuration file has been successfully updated";
+} else {
+    $failureMsg = sprintf("Could not open the file for writing: <strong>%s</strong>", $configFile)
+                . "<br>Check file permissions. The file should be writable by the webserver's user/group";
+} 
+
 ?>

programming_language: PHP
filename: validation.php
change_type: ModificationType.MODIFY
diff: @@ -29,6 +29,7 @@ if (strpos($_SERVER['PHP_SELF'], '/library/validation.php') !== false) {
 // commonly used regexes collection
 define("DATE_REGEX", '/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/');
 define("ORDER_TYPE_REGEX", '/^(de|a)sc$/');
+define("HOSTNAME_REGEX", '/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/');
 define("IP_REGEX", '/^(((2(5[0-5]|[0-4][0-9]))|1[0-9]{2}|[1-9]?[0-9]).){3}((2(5[0-5]|[0-4][0-9]))|1[0-9]{2}|[1-9]?[0-9])$/');
 define("NETMASK_LENGTH_REGEX", '/^3[0-2]|[1-2][0-9]|[1-9]$/');
 define("MACADDR_REGEX", '/^(?:[0-9A-Fa-f]{2}([-:]))(?:[0-9A-Fa-f]{2}\1){4}[0-9A-Fa-f]{2}$/');

programming_language: PHP