BugForge - Daily - Sokudo
Daily - Sokudo
Vulnerabilities Covered
Summary
Sokudo is a typing speed test application where users measure their typing speed and compare their stats against other players. While reviewing the application's traffic, a /api/graphql endpoint was discovered handling the application's data operations. The GraphQL API enforced no field-level authorization, allowing any authenticated user to run an arbitrary users query. By progressively adding fields to the query, the schema was found to expose sensitive properties including role and password. Querying these fields returned every user's credentials in plaintext, and the admin account's password contained the flag — demonstrating how missing object/field level authorization on a GraphQL API leads to full disclosure of sensitive data.
Reference
Account Registration
Sokudo is a typing speed test application where users can test their typing speed and view stats comparing their results against other users on the stats page. We started off by registering an account, which returned our user details along with a valid JWT token.

Discovering the GraphQL endpoint
Looking through our HTTP history in Caido, the first thing that stood out was a /api/graphql POST request. Seeing that the application was backed by GraphQL prompted me to focus my attention there, since a misconfigured GraphQL API is a common source of data exposure.

Within the traffic, notice the LogActivity mutation and its variables — in particular that a userId is supplied directly to the mutation. This hinted that the schema exposed a users type, so I tried querying it directly:
{"query":"{ users { id email } }"}
Enumerating users
The users query returned data for every user on the platform, with no authorization check restricting the result to our own account.

Extracting sensitive fields
Since the API placed no limits on which fields could be requested, I extended the query to include role and password:
{"query":"{ users { id email role password } }"}
The response disclosed every user’s password in plaintext. Reading the admin user’s password field revealed the flag.

Impact
- Full disclosure of every user’s account data through a single GraphQL query
- Exposure of plaintext passwords, enabling complete account takeover of any user including administrators
- Leakage of the
rolefield, revealing which accounts hold elevated privileges - Credential reuse risk — leaked plaintext passwords may unlock accounts on other services
- Trivial mass exploitation, as no special privileges or tooling are required beyond a registered account
Vulnerability Classification
- OWASP Top 10: A01:2021 - Broken Access Control (with A02:2021 - Cryptographic Failures for plaintext password storage)
- OWASP API Top 10: API1:2023 - Broken Object Level Authorization, API3:2023 - Broken Object Property Level Authorization
- Vulnerability Type: Broken Access Control / Excessive Data Exposure via GraphQL
- Attack Surface: GraphQL API endpoint (
/api/graphql),usersquery - CWE: CWE-285 - Improper Authorization, CWE-200 - Exposure of Sensitive Information, CWE-256 - Plaintext Storage of a Password
Root Cause
The GraphQL API exposes a users query that returns the entire users collection with no object or field level authorization. Any authenticated user can request arbitrary fields on any record, including sensitive properties such as role and password that should never be returned by the API. This is compounded by passwords being stored in plaintext, so the exposed password field directly reveals usable credentials. The resolver enforces no scoping to restrict results to the requesting user, and the schema offers no allow-list of safe fields, turning a single query into a complete dump of all user credentials.
Remediation
- Enforce object and field level authorization in GraphQL resolvers so users can only access data they are entitled to
- Never expose sensitive fields such as
passwordthrough the GraphQL schema; resolve them out entirely - Store passwords using a strong, salted one-way hashing algorithm (e.g. bcrypt/argon2) rather than plaintext
- Scope the
usersquery to the authenticated user, or gate broad listings behind an administrative authorization check - Disable introspection in production and apply query depth/complexity limits to reduce schema reconnaissance
- Adopt the principle of least privilege and data minimization for all API responses
- Add monitoring and rate limiting on the GraphQL endpoint to detect enumeration attempts