CVE-2018-10916: Improper Input Validation (CWE-20)
value: It has been discovered that lftp up to and including version 4.8.3 does not properly sanitize remote file names, leading to a loss of integrity on the local system when reverse mirroring is used. A remote attacker may trick a user to use reverse mirroring on an attacker controlled FTP server, resulting in the removal of all files in the current working directory of the victim's system.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
CVSS v3 base score: 6.5
Published: 2018-08-01T14:29Z
Repository: https://github.com/lavv17/lftp
Language: C++
Files changed: src/MirrorJob.cc
Commit date: 2018-07-31 10:57:35 +0300
Commit message: mirror: prepend ./ to rm and chmod arguments to avoid URL recognition (fix #452)

Diff: diff --git a/src/MirrorJob.cc b/src/MirrorJob.cc
index cf106c40..0be45431 100644
--- a/src/MirrorJob.cc
+++ b/src/MirrorJob.cc
@@ -1162,39 +1162,33 @@ int   MirrorJob::Do()
 	       else
 		  Report(_("Old file `%s' is not removed"),target_name_rel);
 	    }
 	    continue;
 	 }
+	 bool use_rmdir = (file->TypeIs(file->DIRECTORY)
+			   && recursion_mode==RECURSION_NEVER);
 	 if(script)
 	 {
-	    ArgV args("rm");
-	    if(file->TypeIs(file->DIRECTORY))
-	    {
-	       if(recursion_mode==RECURSION_NEVER)
-		  args.setarg(0,"rmdir");
-	       else
-		  args.Append("-r");
-	    }
+	    ArgV args(use_rmdir?"rmdir":"rm");
+	    if(file->TypeIs(file->DIRECTORY) && !use_rmdir)
+	       args.Append("-r");
 	    args.Append(target_session->GetFileURL(file->name));
 	    xstring_ca cmd(args.CombineQuoted());
 	    fprintf(script,"%s\n",cmd.get());
 	 }
 	 if(!script_only)
 	 {
-	    ArgV *args=new ArgV("rm");
-	    args->Append(file->name);
+	    ArgV *args=new ArgV(use_rmdir?"rmdir":"rm");
+	    args->Append(dir_file(".",file->name));
 	    args->seek(1);
 	    rmJob *j=new rmJob(target_session->Clone(),args);
 	    args->CombineTo(j->cmdline);
 	    JobStarted(j);
 	    if(file->TypeIs(file->DIRECTORY))
 	    {
 	       if(recursion_mode==RECURSION_NEVER)
-	       {
-		  args->setarg(0,"rmdir");
 		  j->Rmdir();
-	       }
 	       else
 		  j->Recurse();
 	    }
 	 }
 	 const char *target_name_rel=dir_file(target_relative_dir,file->name);
@@ -1256,11 +1250,11 @@ int   MirrorJob::Do()
 	    fprintf(script,"%s\n",cmd.get());
 	 }
 	 if(!script_only)
 	 {
 	    ArgV *a=new ArgV("chmod");
-	    a->Append(file->name);
+	    a->Append(dir_file(".",file->name));
 	    a->seek(1);
 	    ChmodJob *cj=new ChmodJob(target_session->Clone(),
 				 file->mode&~mode_mask,a);
 	    a->CombineTo(cj->cmdline);
 	    if(!verbose_report)
@@ -1378,11 +1372,11 @@ int   MirrorJob::Do()
 	    fprintf(script,"%s\n",cmd.get());
 	 }
 	 if(!script_only)
 	 {
 	    ArgV *args=new ArgV("rm");
-	    args->Append(file->name);
+	    args->Append(dir_file(".",file->name));
 	    args->seek(1);
 	    rmJob *j=new rmJob(source_session->Clone(),args);
 	    args->CombineTo(j->cmdline);
 	    JobStarted(j);
 	 }

Vulnerable code: 	    ArgV args("rm");
	    if(file->TypeIs(file->DIRECTORY))
	    {
	       if(recursion_mode==RECURSION_NEVER)
		  args.setarg(0,"rmdir");
	       else
		  args.Append("-r");
	    }
	    ArgV *args=new ArgV("rm");
	    args->Append(file->name);
	       {
		  args->setarg(0,"rmdir");
	       }
	    a->Append(file->name);
	    args->Append(file->name);
Fixed code: 	 bool use_rmdir = (file->TypeIs(file->DIRECTORY)
			   && recursion_mode==RECURSION_NEVER);
	    ArgV args(use_rmdir?"rmdir":"rm");
	    if(file->TypeIs(file->DIRECTORY) && !use_rmdir)
	       args.Append("-r");
	    ArgV *args=new ArgV(use_rmdir?"rmdir":"rm");
	    args->Append(dir_file(".",file->name));
	    a->Append(dir_file(".",file->name));
	    args->Append(dir_file(".",file->name));