diff --git a/devlib/module/android.py b/devlib/module/android.py index c0e1bd5..70564fd 100644 --- a/devlib/module/android.py +++ b/devlib/module/android.py @@ -22,7 +22,7 @@ import tempfile from devlib.module import FlashModule from devlib.exception import HostError from devlib.utils.android import fastboot_flash_partition, fastboot_command -from devlib.utils.misc import merge_dicts +from devlib.utils.misc import merge_dicts, safe_extract class FastbootFlashModule(FlashModule): @@ -86,7 +86,7 @@ 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) + 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/devlib/module/vexpress.py b/devlib/module/vexpress.py index 05e4146..c597747 100644 --- a/devlib/module/vexpress.py +++ b/devlib/module/vexpress.py @@ -21,6 +21,7 @@ from subprocess import CalledProcessError from devlib.module import HardRestModule, BootModule, FlashModule from devlib.exception import TargetError, TargetStableError, HostError +from devlib.utils.misc import safe_extract from devlib.utils.serial_port import open_serial_connection, pulse_dtr, write_characters from devlib.utils.uefi import UefiMenu, UefiConfig from devlib.utils.uboot import UbootMenu @@ -354,7 +355,7 @@ 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) + safe_extract(tar, self.vemsd_mount) def _overlay_images(self, images): for dest, src in images.items(): diff --git a/devlib/target.py b/devlib/target.py index 780008a..05fdaac 100644 --- a/devlib/target.py +++ b/devlib/target.py @@ -61,7 +61,7 @@ from devlib.utils.misc import memoized, isiterable, convert_new_lines, groupby_v from devlib.utils.misc import commonprefix, merge_lists from devlib.utils.misc import ABI_MAP, get_cpu_name, ranges_to_list from devlib.utils.misc import batch_contextmanager, tls_property, _BoundTLSProperty, nullcontext -from devlib.utils.misc import strip_bash_colors +from devlib.utils.misc import strip_bash_colors, safe_extract from devlib.utils.types import integer, boolean, bitmask, identifier, caseless_string, bytes_regex import devlib.utils.asyn as asyn @@ -827,7 +827,7 @@ class Target(object): await self.pull.asyn(tar_file_name, tmpfile) # Decompress with tarfile.open(tmpfile, 'r') as f: - f.extractall(outdir) + safe_extract(f, outdir) os.remove(tmpfile) # execution diff --git a/devlib/utils/misc.py b/devlib/utils/misc.py index c73a9c6..3bff6a8 100644 --- a/devlib/utils/misc.py +++ b/devlib/utils/misc.py @@ -991,3 +991,26 @@ def groupby_value(dct): tuple(map(itemgetter(0), _items)): v for v, _items in groupby(items, key=key) } + + +def safe_extract(tar, path=".", members=None, *, numeric_owner=False): + """ + A wrapper around TarFile.extract all to mitigate CVE-2007-4995 + (see https://www.trellix.com/en-us/about/newsroom/stories/research/tarfile-exploiting-the-world.html) + """ + + 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) + +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