Responsible disclosure submitted. No mutations were executed. No systems were harmed. Finding classified as Informative. 50 CHF bonus awarded.

Introduction.

Not every bug bounty story ends with a payout and a hall of fame mention.

Some end with a rejection, a lesson, and — if you're lucky — a small bonus that tells you the triage team saw something worth acknowledging even if they couldn't justify a full reward.

This is one of those stories.

I found what I genuinely believed was a Critical severity finding on a major operational platform. I documented it carefully, scored it at CVSS 9.1, mapped it to four OWASP API Top 10 categories, wrote a full responsible disclosure report, and submitted it through a Swiss Bug Bounty platform.

The triage team came back and marked it Rejected (Informative).

They were right. And here is exactly what happened, what I missed, and what every bug bounty hunter needs to understand about the difference between a misconfiguration and an exploitable vulnerability.

How I Found It — URL Fuzzing.

It started with a wordlist and a tool most bug bounty hunters keep in their back pocket.

I was fuzzing subdomains and paths on target.com using a standard GraphQL-focused wordlist. The paths I was testing included:

/graphql
/graphiql
/graphiql?path=/graphql
/api/graphql
/v1/graphql
/playground

When I hit subdomain.target.com with /graphiql?path=/graphql, the server returned a clean 200 OK and loaded a fully functional GraphiQL IDE — no login, no token, no challenge.

None
PoC Image.

GraphiQL is a browser-based developer tool for writing and testing GraphQL queries. It is never supposed to be publicly accessible. Seeing it load on an internet-facing subdomain with zero authentication was the first signal that something was misconfigured.

Building the Evidence — Step by Step.

"Confirming Live Access".

My first query was simple — confirm the endpoint is live and check what role I had:

{
  role
  lastUpdate
  globalMessage
}

Response:

{
  "data": {
    "role": "ANONYMOUS",
    "lastUpdate": "2026-05-19T00:00:01Z",
    "globalMessage": ""
  }
}
None
PoC Image.

Three immediate observations:

  • role: ANONYMOUS — no authentication, yet the API responds.
  • lastUpdate showed the exact day's date— that was a live production system.
  • A global message system exists, currently empty.

"Introspection Was On".

I ran the standard introspection query to dump the schema:

{
  __schema {
    types {
      name
      kind
    }
  }
}

The full type system came back — including something that made me sit up straight:

A Mutation type. Write operations. Accessible anonymously.

None
PoC Image.

"The Mutations".

I enumerated the mutation type:

{
  __schema {
    mutationType {
      fields {
        name
        description
        args { 
        name 
        type { name kind ofType { name kind } } 
        }
      }
    }
  }
}

Three mutations came back:

deleteGlobalMessage :Deletes the global message shown to all users.

updateGlobalMessage:Updates the global message for all users.

updateTrack :Updates state and duration of a track.

None
PoC Image.

On paper this looked devastating. An unauthenticated user could theoretically push fake messages to all visitors or close attractions remotely. I scored it CVSS 9.1, mapped it to OWASP API2, API5, and API8, and filed the report.

"Live Operational Data".

I also pulled real-time data from the tracks query — four operational attractions, all open, zero wait times, live timestamps. The system was clearly active and serving real visitors.

None
PoC Image.

The Rejection — And Why the Triage Team Was Right.

The triage team came back with this response:

None
PoC Image.

They attached a screenshot showing the actual mutation response:

None
PoC Image.

The mutations were visible in the schema but protected server-side. The GraphQL resolvers had authorization checks — the ANONYMOUS role simply could not execute them.

This is the key distinction I missed:

Schema visibility ≠ exploitability.

Just because a mutation appears in the GraphQL schema does not mean it is executable. The schema tells you what operations exist. The resolvers decide who can run them. In this case, the server correctly rejected anonymous mutation attempts.

What I Got Wrong.

1. I Assumed Schema = Access.

The presence of updateGlobalMessage in the schema led me to assume it was accessible. I never actually tried to execute it. If I had, I would have seen the UNAUTHORIZED error immediately and adjusted my severity assessment before filing.

Lesson: Always attempt to execute mutations in a safe, non-destructive way before assessing impact. A schema entry is a hint, not a guarantee.

2. I Over-Scored the CVSS.

My CVSS of 9.1 assumed Integrity: High and Availability: High based on the theoretical ability to modify tracks and push messages. Without confirmed execution, those scores were not justified.

Lesson: CVSS scores should reflect demonstrated impact, not theoretical impact based on schema visibility alone.

3. I Confused Misconfiguration with Exploitability.

GraphiQL being exposed and introspection being enabled are genuine misconfigurations. But misconfigurations without exploitable impact often land as Informative on bug bounty platforms — especially ones that explicitly require CIA impact.

Lesson: Know the platform's acceptance criteria before filing. This particular Swiss platform explicitly requires demonstrable CIA security impact. Misconfiguration alone rarely meets that bar.

What Was Still Valid.

To be fair to myself, two issues remain genuinely valid even after the triage response:

1. Introspection enabled in production.

Introspection gives attackers a complete API blueprint — all types, mutations, input structures, and enum values — without any credentials. This is explicitly flagged in OWASP API8:2023 and recommended against by Apollo, the GraphQL Foundation, and every major security framework. It is a real misconfiguration even if it did not lead to direct exploitation here.

2. GraphiQL publicly accessible.

A developer IDE should never be internet-facing. Its presence reveals the technology stack, endpoint structure, and internal API design to any anonymous visitor. This is a real finding even if it is not bounty-worthy on its own.

The triage team acknowledged both points by awarding a 50 CHF courtesy bonus — which was a fair and professional response.

None
PoC Image.

The 50 CHF Lesson.

Getting 50 CHF for a rejected finding might feel like a consolation prize. But think about what it actually means:

  • The triage team saw genuine effort and good-faith research.
  • The misconfigurations were real even if not exploitable.
  • The responsible disclosure report was well-structured and professional.
  • The finding gave them complementary insights about their own exposure.

That is not nothing. In bug bounty, how you report matters as much as what you report. A well-written Informative is better for your reputation than a poorly written Critical.

Conclusion.

This finding did not pay out the way I expected. The CVSS 9.1 became an Informative. The Critical badge became a courtesy bonus.

But it taught me more about bug bounty triage than most accepted findings would have. The difference between a misconfiguration and an exploitable vulnerability is exactly the kind of nuance that separates good researchers from great ones.

Schema visibility is not access. Theoretical impact is not demonstrated impact. And a rejection with a lesson is worth more than an easy acceptance that teaches you nothing.

The endpoint is still out there. The introspection is still on. The GraphiQL is still exposed.

Next time I'll test the mutation before I file the report.

💬 Before You Go.

Note, this article was written after responsible disclosure was submitted and triaged by the affected organization. The target name has been intentionally omitted. All findings are based on passive reconnaissance and read-only queries. No systems were modified, no data was exfiltrated, and no harm was caused.

If you are a security researcher, always practise responsible disclosure. If you are a developer, disable GraphiQL and introspection in production.

If this writeup helped you learn something new, drop a few claps and follow me for more bug bounty stories, security research, and the occasional rejection that teaches more than a payout ever could. See you in the next writeup, peace… 🙏