From 3cca3a43f3d085e9bbe5a5840c8255bb1b5d052e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 31 Aug 2022 18:28:50 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=EF=B8=8F=20Close=20a=20sanity=20ch?= =?UTF-8?q?eck=20hole=20in=20language=20packs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A language pack containing a directory traversing symlink could be uploaded. Upon backup creation, the symlink would then lead to inclusion of whatever it was pointing to in the resulting backup. This could be used by an attacker with admin permissions to extract information from the server. --- src/octoprint/server/api/languages.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/octoprint/server/api/languages.py b/src/octoprint/server/api/languages.py index 528bbce8fd..87945a60b2 100644 --- a/src/octoprint/server/api/languages.py +++ b/src/octoprint/server/api/languages.py @@ -174,7 +174,8 @@ def deleteInstalledLanguagePack(locale, pack): def _unpack_uploaded_zipfile(path, target): with zipfile.ZipFile(path, "r") as zip: # sanity check - map(_validate_archive_name, zip.namelist()) + for info in zip.infolist(): + _validate_zip_info(info, target) # unpack everything zip.extractall(target) @@ -183,16 +184,27 @@ def _unpack_uploaded_zipfile(path, target): def _unpack_uploaded_tarball(path, target): with tarfile.open(path, "r") as tar: # sanity check - map(_validate_archive_name, tar.getmembers()) + for info in tar.getmembers(): + _validate_tar_info(info, target) # unpack everything tar.extractall(target) -def _validate_archive_name(name): - if name.startswith("/") or ".." in name: +def _validate_archive_name(name, target): + if not os.path.abspath(os.path.join(target, name)).startswith(target + os.path.sep): raise InvalidLanguagePack(f"Provided language pack contains invalid name {name}") +def _validate_zip_info(info, target): + _validate_archive_name(info.filename, target) + + +def _validate_tar_info(info, target): + _validate_archive_name(info.name, target) + if not (info.isfile() or info.isdir()): + raise InvalidLanguagePack("Provided language pack contains invalid file type") + + class InvalidLanguagePack(Exception): pass