VYPR
High severityNVD Advisory· Published Oct 25, 2024· Updated Oct 28, 2024

pyLoad vulnerable to remote code execution by download to /.pyload/scripts using /flashgot API

CVE-2024-47821

Description

pyLoad is a free and open-source Download Manager. The folder /.pyload/scripts has scripts which are run when certain actions are completed, for e.g. a download is finished. By downloading a executable file to a folder in /scripts and performing the respective action, remote code execution can be achieved in versions prior to 0.5.0b3.dev87. A file can be downloaded to such a folder by changing the download folder to a folder in /scripts path and using the /flashgot API to download the file. This vulnerability allows an attacker with access to change the settings on a pyload server to execute arbitrary code and completely compromise the system. Version 0.5.0b3.dev87 fixes this issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
pyload-ngPyPI
< 0.5.0b3.dev870.5.0b3.dev87

Affected products

1

Patches

1
48f59567393a

fix GHSA-w7hq-f2pj-c53g security advisory

https://github.com/pyload/pyloadGammaC0deOct 7, 2024via ghsa
3 files changed · +31 14
  • src/pyload/core/api/__init__.py+15 3 modified
    @@ -14,6 +14,8 @@
     import time
     from enum import IntFlag
     
    +from pyload import PKGDIR
    +
     from ..datatypes.data import *
     from ..datatypes.enums import *
     from ..datatypes.exceptions import *
    @@ -185,12 +187,22 @@ def set_config_value(self, category, option, value, section="core"):
             )
     
             if section == "core":
    -            self.pyload.config[category][option] = value
    +            if category == "general" and option == "storage_folder":
    +                # Forbid setting the download folder inside dangerous locations
    +                correct_case = lambda x: x.lower() if os.name == "nt" else x
    +                directories = [
    +                    correct_case(os.path.join(os.path.realpath(d), ""))
    +                    for d in [value, PKGDIR, self.pyload.userdir]
    +                ]
    +                if any(directories[0].startswith(d) for d in directories[1:]):
    +                    return
    +
    +            self.pyload.config.set(category, option, value)
     
    -            if option in (
    +            if category == "download" and option in (
                     "limit_speed",
                     "max_speed",
    -            ):  #: not so nice to update the limit
    +            ):  #: not such a nice method to update the limit
                     self.pyload.request_factory.update_bucket()
     
             elif section == "plugin":
    
  • src/pyload/core/__init__.py+15 4 modified
    @@ -132,12 +132,23 @@ def _init_config(self, userdir, tempdir, storagedir, debug):
             # otherwise save setting to config dir
             if storagedir is None:
                 storagedir = self.config.get("general", "storage_folder")
    -            # Make sure storage_folder is not empty
    -            if not storagedir:
    -                self.config.set("general", "storage_folder", "~/Downloads/pyLoad")
    -                storagedir = self.config.get("general", "storage_folder")
    +
             else:
                 self.config.set("general", "storage_folder", storagedir)
    +
    +        # Make sure storage_folder is not empty
    +        # and also not inside dangerous locations
    +        correct_case = lambda x: x.lower() if os.name == "nt" else x
    +        directories = [
    +            correct_case(os.path.join(os.path.realpath(d), "") )
    +            for d in [storagedir or PKGDIR, PKGDIR, userdir]
    +        ]
    +        is_bad_dir = any(directories[0].startswith(d) for d in directories[1:])
    +
    +        if not storagedir or is_bad_dir:
    +            self.config.set("general", "storage_folder", "~/Downloads/pyLoad")
    +            storagedir = self.config.get("general", "storage_folder")
    +
             os.makedirs(storagedir, exist_ok=True)
     
             if not self._dry_run:
    
  • src/pyload/webui/app/blueprints/json_blueprint.py+1 7 modified
    @@ -268,15 +268,9 @@ def save_config():
         for key, value in flask.request.form.items():
             try:
                 section, option = key.split("|")
    -        except Exception:
    +        except ValueError:
                 continue
     
    -        if section == 'general' and option=='storage_folder':
    -            abs_path_value = os.path.join(os.path.abspath(value).lower(), "")
    -            abs_PKGDIR = os.path.join(os.path.abspath(PKGDIR).lower(), "")
    -            if abs_path_value.startswith(abs_PKGDIR):
    -                continue
    -
             api.set_config_value(section, option, value, category)
     
         return jsonify(True)
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

4

News mentions

0

No linked articles in our index yet.