From 0572f7a47bd27bfdced35439b8e8862f712aa52b Mon Sep 17 00:00:00 2001 From: TrellixVulnTeam Date: Sat, 10 Dec 2022 04:27:17 +0000 Subject: [PATCH] Adding tarfile member sanitization to extractall() --- levels/01_chroot_image/rd.py | 21 ++++++++++++++++++++- levels/02_mount_ns/rd.py | 21 ++++++++++++++++++++- levels/03_pivot_root/rd.py | 21 ++++++++++++++++++++- levels/04_overlay/rd.py | 21 ++++++++++++++++++++- levels/05_uts_namespace/rd.py | 21 ++++++++++++++++++++- levels/06_pid_namespace/rd.py | 21 ++++++++++++++++++++- levels/07_net_namespace/rd.py | 21 ++++++++++++++++++++- levels/08_cpu_cgroup/rd.py | 21 ++++++++++++++++++++- levels/09_memory_cgorup/rd.py | 21 ++++++++++++++++++++- levels/10_setuid/rd.py | 21 ++++++++++++++++++++- 10 files changed, 200 insertions(+), 10 deletions(-) diff --git a/levels/01_chroot_image/rd.py b/levels/01_chroot_image/rd.py index e917b2b..3edeec3 100755 --- a/levels/01_chroot_image/rd.py +++ b/levels/01_chroot_image/rd.py @@ -61,7 +61,26 @@ def create_container_root(image_name, image_dir, container_id, container_dir): # Fun fact: tar files may contain *nix devices! *facepalm* members = [m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)] - t.extractall(container_root, members=members) + 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(t, container_root, members=members) return container_root diff --git a/levels/02_mount_ns/rd.py b/levels/02_mount_ns/rd.py index be69bd9..318ee32 100755 --- a/levels/02_mount_ns/rd.py +++ b/levels/02_mount_ns/rd.py @@ -42,7 +42,26 @@ def create_container_root(image_name, image_dir, container_id, container_dir): # Fun fact: tar files may contain *nix devices! *facepalm* members = [m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)] - t.extractall(container_root, members=members) + 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(t, container_root, members=members) return container_root diff --git a/levels/03_pivot_root/rd.py b/levels/03_pivot_root/rd.py index 3dd4066..a2c4adf 100755 --- a/levels/03_pivot_root/rd.py +++ b/levels/03_pivot_root/rd.py @@ -47,7 +47,26 @@ def create_container_root(image_name, image_dir, container_id, container_dir): # Fun fact: tar files may contain *nix devices! *facepalm* members = [m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)] - t.extractall(container_root, members=members) + 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(t, container_root, members=members) return container_root diff --git a/levels/04_overlay/rd.py b/levels/04_overlay/rd.py index 972c30b..b0eb345 100755 --- a/levels/04_overlay/rd.py +++ b/levels/04_overlay/rd.py @@ -45,7 +45,26 @@ def create_container_root(image_name, image_dir, container_id, container_dir): # Fun fact: tar files may contain *nix devices! *facepalm* members = [m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)] - t.extractall(container_root, members=members) + 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(t, container_root, members=members) # TODO: create directories for copy-on-write (uppperdir), overlay workdir, # and a mount point diff --git a/levels/05_uts_namespace/rd.py b/levels/05_uts_namespace/rd.py index 247d9ff..bd3dba1 100755 --- a/levels/05_uts_namespace/rd.py +++ b/levels/05_uts_namespace/rd.py @@ -36,7 +36,26 @@ def create_container_root(image_name, image_dir, container_id, container_dir): # Fun fact: tar files may contain *nix devices! *facepalm* members = [m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)] - t.extractall(image_root, members=members) + 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(t, image_root, members=members) # Create directories for copy-on-write (uppperdir), overlay workdir, # and a mount point diff --git a/levels/06_pid_namespace/rd.py b/levels/06_pid_namespace/rd.py index 4e25208..9be3aa2 100755 --- a/levels/06_pid_namespace/rd.py +++ b/levels/06_pid_namespace/rd.py @@ -36,7 +36,26 @@ def create_container_root(image_name, image_dir, container_id, container_dir): # Fun fact: tar files may contain *nix devices! *facepalm* members = [m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)] - t.extractall(image_root, members=members) + 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(t, image_root, members=members) # Create directories for copy-on-write (uppperdir), overlay workdir, # and a mount point diff --git a/levels/07_net_namespace/rd.py b/levels/07_net_namespace/rd.py index f9b68e3..7eb693c 100755 --- a/levels/07_net_namespace/rd.py +++ b/levels/07_net_namespace/rd.py @@ -35,7 +35,26 @@ def create_container_root(image_name, image_dir, container_id, container_dir): # Fun fact: tar files may contain *nix devices! *facepalm* members = [m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)] - t.extractall(image_root, members=members) + 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(t, image_root, members=members) # Create directories for copy-on-write (uppperdir), overlay workdir, # and a mount point diff --git a/levels/08_cpu_cgroup/rd.py b/levels/08_cpu_cgroup/rd.py index 4c7b5bf..070e20e 100755 --- a/levels/08_cpu_cgroup/rd.py +++ b/levels/08_cpu_cgroup/rd.py @@ -35,7 +35,26 @@ def create_container_root(image_name, image_dir, container_id, container_dir): # Fun fact: tar files may contain *nix devices! *facepalm* members = [m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)] - t.extractall(image_root, members=members) + 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(t, image_root, members=members) # Create directories for copy-on-write (uppperdir), overlay workdir, # and a mount point diff --git a/levels/09_memory_cgorup/rd.py b/levels/09_memory_cgorup/rd.py index b9ec105..a5fb0d9 100755 --- a/levels/09_memory_cgorup/rd.py +++ b/levels/09_memory_cgorup/rd.py @@ -35,7 +35,26 @@ def create_container_root(image_name, image_dir, container_id, container_dir): # Fun fact: tar files may contain *nix devices! *facepalm* members = [m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)] - t.extractall(image_root, members=members) + 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(t, image_root, members=members) # Create directories for copy-on-write (uppperdir), overlay workdir, # and a mount point diff --git a/levels/10_setuid/rd.py b/levels/10_setuid/rd.py index c20570b..b86ca17 100755 --- a/levels/10_setuid/rd.py +++ b/levels/10_setuid/rd.py @@ -36,7 +36,26 @@ def create_container_root(image_name, image_dir, container_id, container_dir): # Fun fact: tar files may contain *nix devices! *facepalm* members = [m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)] - t.extractall(image_root, members=members) + 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(t, image_root, members=members) # Create directories for copy-on-write (uppperdir), overlay workdir, # and a mount point