CVE-2022-0238 (published 2022-01-16T11:15Z)

phoronix-test-suite is vulnerable to Cross-Site Request Forgery (CSRF)

Severity: MEDIUM
CVSS v2 base score: 4.3 (AV:N/AC:M/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 4.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N)

Weakness information:
cwe_id: CWE-352
cwe_name: Cross-Site Request Forgery (CSRF)
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.
extended_description: When a web server is designed to receive a request from a client without any mechanism for verifying that it was intentionally sent, then it might be possible for an attacker to trick a client into making an unintentional request to the web server which will be treated as an authentic request. This can be done via a URL, image load, XMLHttpRequest, etc. and can result in exposure of data or unintended code execution.
         

Fix information:
repo_url: https://github.com/phoronix-test-suite/phoronix-test-suite
commit_details: repo_url: https://github.com/phoronix-test-suite/phoronix-test-suite
author: Michael Larabel
author_date: 2022-01-15 03:56:13-06:00
msg: phoromatic: Additional input sanitization / validation
repository: repo_url: https://github.com/phoronix-test-suite/phoronix-test-suite
repo_name: phoronix-test-suite/phoronix-test-suite
description: The Phoronix Test Suite open-source, cross-platform automated testing/benchmarking software.
repo_language: PHP
owner: phoronix-test-suite
file_changes: filename: phoromatic_benchmark.php
change_type: ModificationType.MODIFY
diff: @@ -79,7 +79,7 @@ class phoromatic_benchmark implements pts_webui_interface
 
 			if(!empty($row))
 			{
-				if(isset($_GET['remove']))
+				if(isset($_GET['remove']) && verify_submission_token())
 				{
 					$stmt = phoromatic_server::$db->prepare('DELETE FROM phoromatic_benchmark_tickets WHERE AccountID = :account_id AND TicketID = :ticket_id');
 					$stmt->bindValue(':account_id', $_SESSION['AccountID']);
@@ -87,7 +87,7 @@ class phoromatic_benchmark implements pts_webui_interface
 					$result = $stmt->execute();
 					header('Location: /?benchmark');
 				}
-				else if(isset($_GET['repeat']))
+				else if(isset($_GET['repeat']) && verify_submission_token())
 				{
 					$stmt = phoromatic_server::$db->prepare('UPDATE phoromatic_benchmark_tickets SET TicketIssueTime = :new_ticket_time, State = 1 WHERE AccountID = :account_id AND TicketID = :ticket_id');
 					$stmt->bindValue(':account_id', $_SESSION['AccountID']);
@@ -95,7 +95,7 @@ class phoromatic_benchmark implements pts_webui_interface
 					$stmt->bindValue(':new_ticket_time', time());
 					$result = $stmt->execute();
 				}
-				else if(isset($_GET['disable']))
+				else if(isset($_GET['disable']) && verify_submission_token())
 				{
 					$stmt = phoromatic_server::$db->prepare('UPDATE phoromatic_benchmark_tickets SET State = 0 WHERE AccountID = :account_id AND TicketID = :ticket_id');
 					$stmt->bindValue(':account_id', $_SESSION['AccountID']);
@@ -107,7 +107,7 @@ class phoromatic_benchmark implements pts_webui_interface
 				$main .= '<h1>' . $row['Title'] . '</h1>';
 				$main .= '<h3>' . $row['Description'] . '</h3>';
 				$main .= '<p>This benchmark ticket was created on <strong>' . date('j F Y \a\t H:i', strtotime($row['LastModifiedOn'])) . '</strong> by <strong>' . $row['LastModifiedBy'] . '. The ticket was last issued for testing at ' . date('j F Y \a\t H:i', $row['TicketIssueTime']) . '</strong>.';
-				$main .= '<p> <a href="/?benchmark/' . $PATH[0] . '/&repeat">Repeat Ticket</a> &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&remove">Remove Ticket</a>' . (!isset($_GET['disable']) && $row['State'] > 0 ? ' &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&disable">End Ticket</a>' : null) . '</p>';
+				$main .= '<p> <a href="/?benchmark/' . $PATH[0] . '/&repeat' . append_token_to_url('') . '">Repeat Ticket</a> &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&remove' . append_token_to_url('') . '">Remove Ticket</a>' . (!isset($_GET['disable']) && $row['State'] > 0 ? ' &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&disable' . append_token_to_url('') . '">End Ticket</a>' : null) . '</p>';
 
 				if(!empty($row['RunTargetSystems']))
 				{

programming_language: PHP
filename: phoromatic_r_add_test_details.php
change_type: ModificationType.MODIFY
diff: @@ -37,6 +37,7 @@ class phoromatic_r_add_test_details implements pts_webui_interface
 	}
 	public static function render_page_process($PATH)
 	{
+		phoromatic_quit_if_invalid_input_found(array('tp'));
 		$test_profile = new pts_test_profile($_GET['tp']);
 		$name = $test_profile->get_title();
 		$description = $test_profile->get_description();

programming_language: PHP
filename: phoromatic_r_basic_suite_details.php
change_type: ModificationType.MODIFY
diff: @@ -36,6 +36,7 @@ class phoromatic_r_basic_suite_details implements pts_webui_interface
 	}
 	public static function render_page_process($PATH)
 	{
+		phoromatic_quit_if_invalid_input_found(array('ts'));
 		$ts = $_GET['ts'];
 		$ts_file = phoromatic_server::find_suite_file($_SESSION['AccountID'], $ts);
 		$test_suite = new pts_test_suite($ts_file);

programming_language: PHP
filename: phoromatic_schedules.php
change_type: ModificationType.MODIFY
diff: @@ -57,7 +57,7 @@ class phoromatic_schedules implements pts_webui_interface
 				if(!PHOROMATIC_USER_IS_VIEWER)
 				{
 
-					if(isset($_POST['add_to_schedule_select_test']))
+					if(isset($_POST['add_to_schedule_select_test']) && verify_submission_token())
 					{
 						phoromatic_quit_if_invalid_input_found(array('add_to_schedule_select_test'));
 						$name = $_POST['add_to_schedule_select_test'];
@@ -89,7 +89,7 @@ class phoromatic_schedules implements pts_webui_interface
 							phoromatic_add_activity_stream_event('tests_for_schedule', $PATH[0], 'added');
 						}
 					}
-					else if(isset($_POST['suite_add']))
+					else if(isset($_POST['suite_add']) && verify_submission_token())
 					{
 						$test_suite = phoromatic_server::find_suite_file($_SESSION['AccountID'], $_POST['suite_add']);
 						if(is_file($test_suite))
@@ -154,7 +154,7 @@ class phoromatic_schedules implements pts_webui_interface
 						$row['State'] = $new_state;
 						phoromatic_add_activity_stream_event('schedule', $PATH[0], $PATH[1]);
 					}
-					else if(isset($_POST['do_manual_test_run']))
+					else if(isset($_POST['do_manual_test_run']) && verify_submission_token())
 					{
 						$stmt = phoromatic_server::$db->prepare('INSERT INTO phoromatic_schedules_triggers (AccountID, ScheduleID, Trigger, TriggeredOn) VALUES (:account_id, :schedule_id, :trigger, :triggered_on)');
 						$stmt->bindValue(':account_id',	$_SESSION['AccountID']);
@@ -164,7 +164,7 @@ class phoromatic_schedules implements pts_webui_interface
 						$stmt->execute();
 						$main .= '<h2 style="color: red;">Manual Test Run Triggered</h2>';
 					}
-					else if(isset($_POST['skip_current_ticket']))
+					else if(isset($_POST['skip_current_ticket']) && verify_submission_token())
 					{
 						$stmt = phoromatic_server::$db->prepare('INSERT INTO phoromatic_schedules_trigger_skips (AccountID, ScheduleID, Trigger) VALUES (:account_id, :schedule_id, :trigger)');
 						$stmt->bindValue(':account_id',	$_SESSION['AccountID']);
@@ -251,10 +251,10 @@ class phoromatic_schedules implements pts_webui_interface
 					$main .= '<p>This test schedule can be manually triggered to run at any time by calling <strong>' . $trigger_url . '</strong> where <em>XXX</em> is the trigger value to be used (if relevant, such as a time-stamp, Git/SVN commit number or hash, etc). There\'s also the option of sub-targeting system(s) part of this schedule. One option is appending <em>&sub_target_this_ip</em> if this URL is being called from one of the client test systems to only sub-target the triggered testing on that client, among other options.</p>';
 					$main .= '<p>If you wish to run this test schedule now, click the following button and the schedule will be run on all intended systems at their next earliest possible convenience.</p>';
 					$main .= '<p><form action="?schedules/' . $PATH[0] . '" name="manual_run" method="post">';
-					$main .= '<input type="hidden" name="do_manual_test_run" value="1" /><input type="submit" value="Run Test Schedule Now" onclick="return confirm(\'Run this test schedule now?\');" />';
+					$main .= write_token_in_form() . '<input type="hidden" name="do_manual_test_run" value="1" /><input type="submit" value="Run Test Schedule Now" onclick="return confirm(\'Run this test schedule now?\');" />';
 					$main .= '</form></p>';
 					$main .= '<p><form action="?schedules/' . $PATH[0] . '" name="skip_run" method="post">';
-					$main .= '<input type="hidden" name="skip_current_ticket" value="1" /><input type="submit" value="Skip Current Test Ticket" onclick="return confirm(\'Skip any currently active test ticket on all systems?\');" />';
+					$main .= write_token_in_form() . '<input type="hidden" name="skip_current_ticket" value="1" /><input type="submit" value="Skip Current Test Ticket" onclick="return confirm(\'Skip any currently active test ticket on all systems?\');" />';
 					$main .= '</form></p>';
 				}
 
@@ -329,7 +329,7 @@ class phoromatic_schedules implements pts_webui_interface
 				{
 					$main .= '<hr /><h2>Add A Test</h2>';
 					$main .= '<form action="?schedules/' . $PATH[0] . '" name="add_test" id="add_test" method="post">';
-					$main .= '<select name="add_to_schedule_select_test" id="add_to_schedule_select_test" onchange="phoromatic_schedule_test_details(\'\');">';
+					$main .= write_token_in_form() . '<select name="add_to_schedule_select_test" id="add_to_schedule_select_test" onchange="phoromatic_schedule_test_details(\'\');">';
 					$dc = pts_client::download_cache_path();
 					$dc_exists = is_file($dc . 'pts-download-cache.json');
 					if($dc_exists)
@@ -376,7 +376,7 @@ class phoromatic_schedules implements pts_webui_interface
 
 					$main .= '<hr /><h2>Add A Suite:</h2>';
 					$main .= '<form action="?schedules/' . $PATH[0] . '" name="add_suite" id="add_suite" method="post">';
-					$main .= '<p><select name="suite_to_run" id="suite_to_run_identifier" onchange="phoromatic_show_basic_suite_details(\'\');">';
+					$main .= write_token_in_form() . '<p><select name="suite_to_run" id="suite_to_run_identifier" onchange="phoromatic_show_basic_suite_details(\'\');">';
 					foreach(array_merge($local_suites, $official_suites) as $title => $id)
 					{
 						$main .= '<option value="' . $id . '">' . $title . '</option>';

programming_language: PHP
filename: phoromatic_system_claim.php
change_type: ModificationType.MODIFY
diff: @@ -42,7 +42,7 @@ class phoromatic_system_claim implements pts_webui_interface
 		if(!PHOROMATIC_USER_IS_VIEWER)
 		{
 
-			if(function_exists('ssh2_connect') && isset($_POST['ip']) && isset($_POST['port']) && isset($_POST['password']) && isset($_POST['username']))
+			if(function_exists('ssh2_connect') && isset($_POST['ip']) && isset($_POST['port']) && isset($_POST['password']) && isset($_POST['username']) && verify_submission_token())
 			{
 				$connection = ssh2_connect($_POST['ip'], $_POST['port']);
 
@@ -72,7 +72,7 @@ class phoromatic_system_claim implements pts_webui_interface
 					ssh2_exec($connection, 'rm' . $tmp_remote_file);
 				}
 			}
-			if((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) && isset($_POST['ping']))
+			if((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) && isset($_POST['ping']) && verify_submission_token())
 			{
 				$ip_ping = ip2long($_POST['ip_claim']) !== false ? $_POST['ip_claim'] : null;
 				if($ip_ping)
@@ -83,7 +83,7 @@ class phoromatic_system_claim implements pts_webui_interface
 					echo '</pre>';
 				}
 			}
-			else if((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) || (isset($_POST['mac_claim']) && !empty($_POST['mac_claim'])))
+			else if(((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) || (isset($_POST['mac_claim']) && !empty($_POST['mac_claim']))) && verify_submission_token())
 			{
 				$stmt = phoromatic_server::$db->prepare('INSERT INTO phoromatic_system_association_claims (AccountID, IPAddress, NetworkMAC, CreationTime) VALUES (:account_id, :ip_address, :mac_address, :creation_time)');
 				$stmt->bindValue(':account_id', $_SESSION['AccountID']);
@@ -92,7 +92,7 @@ class phoromatic_system_claim implements pts_webui_interface
 				$stmt->bindValue(':creation_time', phoromatic_server::current_time());
 				$result = $stmt->execute();
 			}
-			if(isset($_POST['remove_claim']) && !empty($_POST['remove_claim']))
+			if(isset($_POST['remove_claim']) && !empty($_POST['remove_claim']) && verify_submission_token())
 			{
 				list($ipc, $macc) = explode(',', $_POST['remove_claim']);
 				$stmt = phoromatic_server::$db->prepare('DELETE FROM phoromatic_system_association_claims WHERE AccountID = :account_id AND NetworkMAC = :mac_address AND IPAddress = :ip_address');
@@ -109,7 +109,7 @@ class phoromatic_system_claim implements pts_webui_interface
 			if(function_exists('ssh2_connect'))
 			{
 				$main .= '<h3>Phoromatic Client SSH Information:</h3>';
-				$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="ssh_connect" method="post">
+				$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="ssh_connect" method="post">' . write_token_in_form() . '
 				<p><strong>IP Address:</strong> <input type="text" name="ip" /></p>
 				<p><strong>SSH Port:</strong> <input type="text" name="port" value="22" /></p>
 				<p><strong>Username:</strong> <input type="text" name="username" /></p>
@@ -124,7 +124,7 @@ class phoromatic_system_claim implements pts_webui_interface
 			$main .= '<hr />';
 			$main .= '<h2>Add Phoromatic Server Info Via IP/MAC</h2>
 			<p>If deploying a Phoromatic Server within an organization, you can attempt for automatic configuration of Phoromatic clients if you know the system\'s IP or MAC addresses. When specifying either of these fields, if a Phoromatic client attempts to connect to this Phoromatic system without being associated to an account, it will be claimed by this account as long as no other Phoromatic accounts are attempting to claim the IP/MAC. This method can be particularly useful if running the Phoromatic client as a systemd/Upstart service where it will continually poll every 90 seconds auto-detected Phoromatic Servers on the LAN via zero-conf networking. For this feature to work, the zero-conf networking (Avahi) support must be enabled and working.</p>';
-			$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="auto_associate" method="post">
+			$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="auto_associate" method="post">' . write_token_in_form() . '
 			<p><strong>IP Address Claim:</strong> <input type="text" name="ip_claim" /></p>
 			<p><strong>MAC Address Claim:</strong> <input type="text" name="mac_claim" /></p>
 			<p><input name="ping" value="Ping Test" type="submit" /> &nbsp; <input name="submit" value="Submit Claim" type="submit" /></p>
@@ -147,7 +147,7 @@ class phoromatic_system_claim implements pts_webui_interface
 			if(!empty($claims))
 			{
 				$main .= '<hr /><h2>Remove Claim</h2><p>Removing a claimed IP / MAC address.</p>';
-				$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="remove_claim" method="post"><select name="remove_claim" id="remove_claim">';
+				$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="remove_claim" method="post"><select name="remove_claim" id="remove_claim">' . write_token_in_form();
 
 				foreach($claims as $claim)
 				{

programming_language: PHP
filename: phoromatic_systems.php
change_type: ModificationType.MODIFY
diff: @@ -52,7 +52,7 @@ class phoromatic_systems implements pts_webui_interface
 			$stmt->bindValue(':block_power_offs', $_POST['block_power_offs']);
 			$stmt->execute();
 		}
-		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['maintenance_mode']))
+		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['maintenance_mode']) && verify_submission_token())
 		{
 			$stmt = phoromatic_server::$db->prepare('UPDATE phoromatic_systems SET MaintenanceMode = :maintenance_mode WHERE AccountID = :account_id AND SystemID = :system_id');
 			$stmt->bindValue(':account_id', $_SESSION['AccountID']);
@@ -67,7 +67,7 @@ class phoromatic_systems implements pts_webui_interface
 			$stmt->bindValue(':system_id', $PATH[0]);
 			$stmt->execute();
 		}
-		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_reboot']))
+		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_reboot']) && verify_submission_token())
 		{
 			$stmt = phoromatic_server::$db->prepare('UPDATE phoromatic_systems SET TickThreadEvent = :event WHERE AccountID = :account_id AND SystemID = :system_id');
 			$stmt->bindValue(':account_id', $_SESSION['AccountID']);
@@ -75,7 +75,7 @@ class phoromatic_systems implements pts_webui_interface
 			$stmt->bindValue(':event', time() . ':reboot');
 			$stmt->execute();
 		}
-		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_halt']))
+		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_halt']) && verify_submission_token())
 		{
 			$stmt = phoromatic_server::$db->prepare('UPDATE phoromatic_systems SET TickThreadEvent = :event WHERE AccountID = :account_id AND SystemID = :system_id');
 			$stmt->bindValue(':account_id', $_SESSION['AccountID']);
@@ -92,6 +92,7 @@ class phoromatic_systems implements pts_webui_interface
 		}
 		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['system_var_names'])&& isset($_POST['system_var_values']))
 		{
+			phoromatic_quit_if_invalid_input_found(array('system_var_names', 'system_var_values'));
 			$vars = array();
 			foreach($_POST['system_var_names'] as $i => $name)
 			{
@@ -199,13 +200,13 @@ class phoromatic_systems implements pts_webui_interface
 						$mm_onclick = 'return confirm(\'Enter maintenance mode now?\');';
 					}
 
-					$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post"><input type="hidden" name="maintenance_mode" value="' . $mm_val . '" /><input type="submit" value="' . $mm_str . '" onclick="' . $mm_onclick . '" style="float: left; margin: 0 20px 5px 0;" /></form> Putting the system into maintenance mode will power up the system (if supported and applicable) and cause the Phoronix Test Suite Phoromatic client to idle and block all testing until the mode has been disabled. If a test is already running on the system, the maintenance mode will not be entered until after the testing has completed. The maintenance mode can be used if wishing to update the system software or carry out other tasks without interfering with the Phoromatic client process. Once disabled, the Phoronix Test Suite will continue to function as normal.</p>';
+					$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post">' . write_token_in_form() . '<input type="hidden" name="maintenance_mode" value="' . $mm_val . '" /><input type="submit" value="' . $mm_str . '" onclick="' . $mm_onclick . '" style="float: left; margin: 0 20px 5px 0;" /></form> Putting the system into maintenance mode will power up the system (if supported and applicable) and cause the Phoronix Test Suite Phoromatic client to idle and block all testing until the mode has been disabled. If a test is already running on the system, the maintenance mode will not be entered until after the testing has completed. The maintenance mode can be used if wishing to update the system software or carry out other tasks without interfering with the Phoromatic client process. Once disabled, the Phoronix Test Suite will continue to function as normal.</p>';
 
 					if($row['CoreVersion'] >= 5730)
 					{
-						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post"><input type="hidden" name="tick_thread_reboot" value="1" /><input type="submit" value="Reboot System" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and connected to the Phoromatic Server, this will send a message to the system to issue a reboot -- in case the system is hung on a test or you wish to otherwise manually reboot the server.</p>';
+						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post">' . write_token_in_form() . '<input type="hidden" name="tick_thread_reboot" value="1" /><input type="submit" value="Reboot System" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and connected to the Phoromatic Server, this will send a message to the system to issue a reboot -- in case the system is hung on a test or you wish to otherwise manually reboot the server.</p>';
 
-						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post"><input type="hidden" name="tick_thread_halt" value="1" /><input type="submit" value="Halt Testing" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and running a test/benchmark via the Phoromatic Server, this will tell the system to halt the testing prematurely as soon as the currently-active test has finished. The results successfully ran will then be uploaded to the Phoromatic Server.</p>';
+						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post">' . write_token_in_form() . '<input type="hidden" name="tick_thread_halt" value="1" /><input type="submit" value="Halt Testing" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and running a test/benchmark via the Phoromatic Server, this will tell the system to halt the testing prematurely as soon as the currently-active test has finished. The results successfully ran will then be uploaded to the Phoromatic Server.</p>';
 					}
 				}
 

programming_language: PHP
filename: phoromatic_functions.php
change_type: ModificationType.MODIFY
diff: @@ -527,9 +527,9 @@ function write_token_in_form()
 {
 	return '<input type="hidden" name="token_submit" value="' . $_SESSION['Token'] . '" />';
 }
-function append_token_to_url()
+function append_token_to_url($prefix = '/')
 {
-	return '/&token_submit=' . $_SESSION['Token'];
+	return $prefix . '&token_submit=' . $_SESSION['Token'];
 }
 function verify_submission_token()
 {

programming_language: PHP