From b2eebb878780a94794ae4a1289aa3fde1da681a0 Mon Sep 17 00:00:00 2001 From: TrellixVulnTeam Date: Sat, 17 Dec 2022 08:30:18 +0000 Subject: [PATCH] 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