CVE-2020-10174: Improper Link Resolution Before File Access ('Link Following') (CWE-59)
value: init_tmp in TeeJee.FileSystem.vala in Timeshift before 20.03 unsafely reuses a preexisting temporary directory in the predictable location /tmp/timeshift. It follows symlinks in this location or uses directories owned by unprivileged users. Because Timeshift also executes scripts under this location, an attacker can attempt to win a race condition to replace scripts created by Timeshift with attacker-controlled scripts. Upon success, an attacker-controlled script is executed with full root privileges. This logic is practically always triggered when Timeshift runs regardless of the command-line arguments used.
CWE description: The product attempts to access a file based on the filename, but it does not properly prevent that filename from identifying a link or shortcut that resolves to an unintended resource.
CVSS v3 base score: 7
Published: 2020-03-05T16:15Z
Repository: https://github.com/teejee2008/timeshift
Language: Other
Files changed: src/Core/Main.vala, src/Utility/TeeJee.FileSystem.vala, src/Utility/TeeJee.Process.vala
Commit date: 2020-03-05 08:57:24 +0530
Commit message: Change TEMP_DIR permissions and path; Cleanup on exit;


Diff: diff --git a/src/Core/Main.vala b/src/Core/Main.vala
index 9ce6d62..8b5abc1 100644
--- a/src/Core/Main.vala
+++ b/src/Core/Main.vala
@@ -4242,10 +4242,12 @@ public class Main : GLib.Object{
 		unmount_target_device(false);
 
 		clean_logs();
 
 		app_lock.remove();
+		
+		dir_delete(TEMP_DIR);
 
 		exit(exit_code);
 
 		//Gtk.main_quit ();
 	}
diff --git a/src/Utility/TeeJee.FileSystem.vala b/src/Utility/TeeJee.FileSystem.vala
index 846ada1..890ebeb 100644
--- a/src/Utility/TeeJee.FileSystem.vala
+++ b/src/Utility/TeeJee.FileSystem.vala
@@ -342,21 +342,25 @@ namespace TeeJee.FileSystem{
 
 		/* Creates a directory along with parents */
 
 		try{
 			var dir = File.parse_name (dir_path);
+			
 			if (dir.query_exists () == false) {
+				
 				bool ok = dir.make_directory_with_parents (null);
+				
 				if (show_message){
 					if (ok){
 						log_msg(_("Created directory") + ": %s".printf(dir_path));
 					}
 					else{
 						log_error(_("Failed to create directory") + ": %s".printf(dir_path));
 					}
 				}
 			}
+			
 			return true;
 		}
 		catch (Error e) {
 			log_error (e.message);
 			log_error(_("Failed to create directory") + ": %s".printf(dir_path));
@@ -371,13 +375,16 @@ namespace TeeJee.FileSystem{
 		if (!dir_exists(dir_path)){
 			return true;
 		}
 		
 		string cmd = "rm -rf '%s'".printf(escape_single_quote(dir_path));
+		
 		log_debug(cmd);
+		
 		string std_out, std_err;
 		int status = exec_sync(cmd, out std_out, out std_err);
+		
 		if (show_message){
 			if (status == 0){
 				log_msg(_("Deleted directory") + ": %s".printf(dir_path));
 			}
 			else{
diff --git a/src/Utility/TeeJee.Process.vala b/src/Utility/TeeJee.Process.vala
index 70dd934..e31a643 100644
--- a/src/Utility/TeeJee.Process.vala
+++ b/src/Utility/TeeJee.Process.vala
@@ -34,18 +34,21 @@ namespace TeeJee.ProcessHelper{
 	// execute process ---------------------------------
 	
     public static void init_tmp(string subdir_name){
 		string std_out, std_err;
 
-		TEMP_DIR = Environment.get_tmp_dir() + "/" + subdir_name + "/" + random_string();
+		TEMP_DIR = Environment.get_tmp_dir() + "/" + random_string();
 		dir_create(TEMP_DIR);
-
+		chmod(TEMP_DIR, "0750");
+		
 		exec_script_sync("echo 'ok'",out std_out,out std_err, true);
-		if ((std_out == null)||(std_out.strip() != "ok")){
-			TEMP_DIR = Environment.get_home_dir() + "/.temp/" + subdir_name + "/" + random_string();
-			exec_sync("rm -rf '%s'".printf(TEMP_DIR), null, null);
+		
+		if ((std_out == null) || (std_out.strip() != "ok")){
+			
+			TEMP_DIR = Environment.get_home_dir() + "/.temp/" + random_string();
 			dir_create(TEMP_DIR);
+			chmod(TEMP_DIR, "0750");
 		}
 
 		//log_debug("TEMP_DIR=" + TEMP_DIR);
 	}
 

Vulnerable code: 		TEMP_DIR = Environment.get_tmp_dir() + "/" + subdir_name + "/" + random_string();

		if ((std_out == null)||(std_out.strip() != "ok")){
			TEMP_DIR = Environment.get_home_dir() + "/.temp/" + subdir_name + "/" + random_string();
			exec_sync("rm -rf '%s'".printf(TEMP_DIR), null, null);
Fixed code: 		
		dir_delete(TEMP_DIR);
			
				
				
			
		
		
		
		TEMP_DIR = Environment.get_tmp_dir() + "/" + random_string();
		chmod(TEMP_DIR, "0750");
		
		
		if ((std_out == null) || (std_out.strip() != "ok")){
			
			TEMP_DIR = Environment.get_home_dir() + "/.temp/" + random_string();
			chmod(TEMP_DIR, "0750");