CVE-2018-10897: Improper Link Resolution Before File Access ('Link Following') (CWE-59)
value: A directory traversal issue was found in reposync, a part of yum-utils, where reposync fails to sanitize paths in remote repository configuration files. If an attacker controls a repository, they may be able to copy files outside of the destination directory on the targeted system via path traversal. If reposync is running with heightened privileges on a targeted system, this flaw could potentially result in system compromise via the overwriting of critical system files. Version 1.1.31 and older are believed to be affected.
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: 8.1
Published: 2018-08-01T17:29Z
Repository: https://github.com/rpm-software-management/yum-utils
Language: Python
Files changed: reposync.py
Commit date: 2018-07-19 15:21:25 +0200
Commit message: reposync: refactor: sanitize remote_path usage

For better readability (remote_path == relativepath).

Diff: diff --git a/reposync.py b/reposync.py
index 541ab9d..6b65116 100755
--- a/reposync.py
+++ b/reposync.py
@@ -219,12 +219,11 @@ def main():
         if opts.delete and os.path.exists(local_repo_path):
             current_pkgs = localpkgs(local_repo_path)
 
             download_set = {}
             for pkg in download_list:
-                remote = pkg.returnSimple('relativepath')
-                rpmname = os.path.basename(remote)
+                rpmname = os.path.basename(pkg.remote_path)
                 download_set[rpmname] = 1
 
             for pkg in current_pkgs:
                 if pkg in download_set:
                     continue
@@ -267,12 +266,11 @@ def main():
                         my.logger.error("Unable to fetch metadata: %s" % e)
 
         remote_size = 0
         if not opts.urls:
             for pkg in download_list:
-                remote = pkg.returnSimple('relativepath')
-                local = local_repo_path + '/' + remote
+                local = os.path.join(local_repo_path, pkg.remote_path)
                 sz = int(pkg.returnSimple('packagesize'))
                 if os.path.exists(local) and os.path.getsize(local) == sz:
                     continue
                 remote_size += sz
 
@@ -280,24 +278,22 @@ def main():
             urlgrabber.progress.text_meter_total_size(remote_size)
 
         download_list.sort(key=lambda pkg: pkg.name)
         if opts.urls:
             for pkg in download_list:
-                remote = pkg.returnSimple('relativepath')
-                local = os.path.join(local_repo_path, remote)
+                local = os.path.join(local_repo_path, pkg.remote_path)
                 if not (os.path.exists(local) and my.verifyPkg(local, pkg, False)):
-                    print urljoin(pkg.repo.urls[0], pkg.relativepath)
+                    print urljoin(pkg.repo.urls[0], pkg.remote_path)
             continue
 
         # create dest dir
         if not os.path.exists(local_repo_path):
             os.makedirs(local_repo_path)
 
         # set localpaths
         for pkg in download_list:
-            rpmfn = pkg.remote_path
-            pkg.localpath = os.path.join(local_repo_path, rpmfn)
+            pkg.localpath = os.path.join(local_repo_path, pkg.remote_path)
             pkg.repo.copy_local = True
             pkg.repo.cache = 0
             localdir = os.path.dirname(pkg.localpath)
             if not os.path.exists(localdir):
                 os.makedirs(localdir)

Vulnerable code:                 remote = pkg.returnSimple('relativepath')
                rpmname = os.path.basename(remote)
                remote = pkg.returnSimple('relativepath')
                local = local_repo_path + '/' + remote
                remote = pkg.returnSimple('relativepath')
                local = os.path.join(local_repo_path, remote)
                    print urljoin(pkg.repo.urls[0], pkg.relativepath)
            rpmfn = pkg.remote_path
            pkg.localpath = os.path.join(local_repo_path, rpmfn)
Fixed code:                 rpmname = os.path.basename(pkg.remote_path)
                local = os.path.join(local_repo_path, pkg.remote_path)
                local = os.path.join(local_repo_path, pkg.remote_path)
                    print urljoin(pkg.repo.urls[0], pkg.remote_path)
            pkg.localpath = os.path.join(local_repo_path, pkg.remote_path)