SECURITY: SDK archive upload vulnerable to path traversal (tar/zip slip) — attacker can write arbitrary files on the server #89

Closed
opened 2026-05-25 19:27:16 -04:00 by zgaetano · 0 comments
Owner

Description

services/mam-api/src/routes/sdk.js extracts uploaded archives without checking for path traversal entries ("zip slip" / "tar slip"). A malicious archive whose entries contain ../ sequences can write files outside SDK_ROOT.

Affected code (sdk.js):

if (lower.endsWith('.zip')) {
  cmd = 'unzip'; args = ['-q', '-o', archivePath, '-d', dir];
} else if (...) {
  cmd = 'tar'; args = ['-xf', archivePath, '-C', dir];
}

await new Promise((resolve, reject) => {
  const child = spawn(cmd, args, ...);
  ...
});

Exploitation

An attacker who has authenticated access (the route has requireAuth) could upload a crafted .tar.gz such as:

# Create a malicious archive that writes to /etc/cron.d/
tar -czf evil.tar.gz --transform 's|^|../../etc/cron.d/|' backdoor

When the API extracts this to /sdk/blackmagic/, the relative ../../etc/cron.d/backdoor entry escapes SDK_ROOT and writes to the system cron directory (or anywhere the process has write access).

Impact

  • With AUTH_ENABLED=true: Exploitable by any authenticated admin (since SDK management is typically admin-only).
  • With AUTH_ENABLED=false (dev mode): Exploitable by anyone who can reach the API.

The running process (node) would need write access to the target path, but on many deployments the container runs as root or has elevated permissions.

Suggested fixes

Option 1 — Validate entries before extraction (most robust):
For zip: use a library like adm-zip to inspect entry names before extracting. For tar: use --strip-components and a content listing pre-pass.

Option 2 — Use --no-overwrite-dir and a jail:
For tar, add --no-absolute-names and validate that no entry path contains ..:

tar -tf archive.tar.gz | grep -P '\.\.' && exit 1
tar -xf archive.tar.gz -C dir --no-absolute-names

Option 3 — Run extraction in a separate sandboxed process/container with a read-only filesystem root, only the target directory writable.

## Description `services/mam-api/src/routes/sdk.js` extracts uploaded archives without checking for path traversal entries ("zip slip" / "tar slip"). A malicious archive whose entries contain `../` sequences can write files outside `SDK_ROOT`. **Affected code (`sdk.js`):** ```js if (lower.endsWith('.zip')) { cmd = 'unzip'; args = ['-q', '-o', archivePath, '-d', dir]; } else if (...) { cmd = 'tar'; args = ['-xf', archivePath, '-C', dir]; } await new Promise((resolve, reject) => { const child = spawn(cmd, args, ...); ... }); ``` ### Exploitation An attacker who has authenticated access (the route has `requireAuth`) could upload a crafted `.tar.gz` such as: ```bash # Create a malicious archive that writes to /etc/cron.d/ tar -czf evil.tar.gz --transform 's|^|../../etc/cron.d/|' backdoor ``` When the API extracts this to `/sdk/blackmagic/`, the relative `../../etc/cron.d/backdoor` entry escapes `SDK_ROOT` and writes to the system cron directory (or anywhere the process has write access). ### Impact - **With `AUTH_ENABLED=true`:** Exploitable by any authenticated admin (since SDK management is typically admin-only). - **With `AUTH_ENABLED=false` (dev mode):** Exploitable by anyone who can reach the API. The running process (`node`) would need write access to the target path, but on many deployments the container runs as root or has elevated permissions. ### Suggested fixes **Option 1 — Validate entries before extraction (most robust):** For zip: use a library like `adm-zip` to inspect entry names before extracting. For tar: use `--strip-components` and a content listing pre-pass. **Option 2 — Use `--no-overwrite-dir` and a jail:** For tar, add `--no-absolute-names` and validate that no entry path contains `..`: ```bash tar -tf archive.tar.gz | grep -P '\.\.' && exit 1 tar -xf archive.tar.gz -C dir --no-absolute-names ``` **Option 3 — Run extraction in a separate sandboxed process/container** with a read-only filesystem root, only the target directory writable.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: WildDragonLLC/dragonflight#89
No description provided.