From b2eebb878780a94794ae4a1289aa3fde1da681a0 Mon Sep 17 00:00:00 2001 From: TrellixVulnTeam Date: Sat, 17 Dec 2022 08:30:18 +0000 Subject: [PATCH 1/2] Adding tarfile member sanitization to extractall() --- external/devlib/devlib/module/android.py | 21 ++++++++++++++++++- external/devlib/devlib/module/vexpress.py | 21 ++++++++++++++++++- external/devlib/devlib/target.py | 21 ++++++++++++++++++- .../wa/framework/output.py | 21 ++++++++++++++++++- .../wa/instruments/misc.py | 21 ++++++++++++++++++- .../wa/workloads/rt_app/__init__.py | 21 ++++++++++++++++++- 6 files changed, 120 insertions(+), 6 deletions(-) diff --git a/external/devlib/devlib/module/android.py b/external/devlib/devlib/module/android.py index c0e1bd5a7..749557388 100644 --- a/external/devlib/devlib/module/android.py +++ b/external/devlib/devlib/module/android.py @@ -86,7 +86,26 @@ class FastbootFlashModule(FlashModule): self._validate_image_bundle(image_bundle) extract_dir = tempfile.mkdtemp() with tarfile.open(image_bundle) as tar: - tar.extractall(path=extract_dir) + def is_within_directory(directory, target): + + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + + return prefix == abs_directory + + def safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(tar, path=extract_dir) files = [tf.name for tf in tar.getmembers()] if self.partitions_file_name not in files: extract_dir = os.path.join(extract_dir, files[0]) diff --git a/external/devlib/devlib/module/vexpress.py b/external/devlib/devlib/module/vexpress.py index 05e41467e..6feaf822a 100644 --- a/external/devlib/devlib/module/vexpress.py +++ b/external/devlib/devlib/module/vexpress.py @@ -354,7 +354,26 @@ class VersatileExpressFlashModule(FlashModule): validate_image_bundle(bundle) self.logger.debug('Extracting {} into {}...'.format(bundle, self.vemsd_mount)) with tarfile.open(bundle) as tar: - tar.extractall(self.vemsd_mount) + def is_within_directory(directory, target): + + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + + return prefix == abs_directory + + def safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(tar, self.vemsd_mount) def _overlay_images(self, images): for dest, src in images.items(): diff --git a/external/devlib/devlib/target.py b/external/devlib/devlib/target.py index 780008aa9..8aa517b93 100644 --- a/external/devlib/devlib/target.py +++ b/external/devlib/devlib/target.py @@ -827,7 +827,26 @@ class Target(object): await self.pull.asyn(tar_file_name, tmpfile) # Decompress with tarfile.open(tmpfile, 'r') as f: - f.extractall(outdir) + def is_within_directory(directory, target): + + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + + return prefix == abs_directory + + def safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(f, outdir) os.remove(tmpfile) # execution diff --git a/external/workload-automation/wa/framework/output.py b/external/workload-automation/wa/framework/output.py index b1a84c1b2..47ab71ff3 100644 --- a/external/workload-automation/wa/framework/output.py +++ b/external/workload-automation/wa/framework/output.py @@ -854,7 +854,26 @@ class DatabaseOutput(Output): def _read_dir_artifact(self, artifact): artifact_path = tempfile.mkdtemp(prefix='wa_') with tarfile.open(fileobj=self.conn.lobject(int(artifact.path), mode='b'), mode='r|gz') as tar_file: - tar_file.extractall(artifact_path) + def is_within_directory(directory, target): + + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + + return prefix == abs_directory + + def safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(tar_file, artifact_path) self.conn.commit() return artifact_path diff --git a/external/workload-automation/wa/instruments/misc.py b/external/workload-automation/wa/instruments/misc.py index a1573af70..b22bb873e 100644 --- a/external/workload-automation/wa/instruments/misc.py +++ b/external/workload-automation/wa/instruments/misc.py @@ -162,7 +162,26 @@ class SysfsExtractor(Instrument): self.target.execute('chmod 0777 {}'.format(on_device_tarball), as_root=True) self.target.pull(on_device_tarball, on_host_tarball) with tarfile.open(on_host_tarball, 'r:gz') as tf: - tf.extractall(context.output_directory) + def is_within_directory(directory, target): + + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + + return prefix == abs_directory + + def safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(tf, context.output_directory) self.target.remove(on_device_tarball) os.remove(on_host_tarball) diff --git a/external/workload-automation/wa/workloads/rt_app/__init__.py b/external/workload-automation/wa/workloads/rt_app/__init__.py index c4eccb584..ff14a85b8 100644 --- a/external/workload-automation/wa/workloads/rt_app/__init__.py +++ b/external/workload-automation/wa/workloads/rt_app/__init__.py @@ -286,6 +286,25 @@ class RtApp(Workload): host_path = os.path.join(context.output_directory, TARBALL_FILENAME) self.target.pull(target_path, host_path) with tarfile.open(host_path, 'r:gz') as tf: - tf.extractall(context.output_directory) + def is_within_directory(directory, target): + + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + + return prefix == abs_directory + + def safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(tf, context.output_directory) os.remove(host_path) self.target.execute('rm -rf {}/*'.format(self.target_working_directory)) -- GitLab From c8e83e7e14e707ff9be84256720061351c8ff9d6 Mon Sep 17 00:00:00 2001 From: TrellixVulnTeam Date: Sun, 8 Jan 2023 02:55:17 +0000 Subject: [PATCH 2/2] Adding tarfile member sanitization to extractall() --- lisa/_kmod.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/lisa/_kmod.py b/lisa/_kmod.py index 83aae6557..78cbbe715 100644 --- a/lisa/_kmod.py +++ b/lisa/_kmod.py @@ -217,7 +217,26 @@ def _make_chroot(make_vars, bind_paths=None, alpine_version='3.16.0', overlay_ba shutil.copyfileobj(url, f) with tarfile.open(tar_path, 'r') as f: - f.extractall(path=path) + def is_within_directory(directory, target): + + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + + return prefix == abs_directory + + def safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner) + + + safe_extract(f, path=path) else: packages = [] @@ -632,7 +651,26 @@ class TarOverlay(_PathOverlayBase): def write_to(self, dst): with tarfile.open(self.path) as tar: - tar.extractall(dst) + def is_within_directory(directory, target): + + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + + return prefix == abs_directory + + def safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner) + + + safe_extract(tar, dst) class KernelTree(Loggable, SerializeViaConstructor): @@ -1476,7 +1514,26 @@ class KernelTree(Loggable, SerializeViaConstructor): member.path for member in tar.getmembers() ) - tar.extractall(extract_folder) + def is_within_directory(directory, target): + + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + + return prefix == abs_directory + + def safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner) + + + safe_extract(tar, extract_folder) except BaseException: with contextlib.suppress(FileNotFoundError): shutil.rmtree(extract_folder, ignore_errors=True) -- GitLab