← Writeups

One link to unlink: a CSRF in OpenAI's Discord verification

A single crafted link could quietly pull any member out of OpenAI's Discord server. No password, no phishing page, no warning to the victim, just an old-fashioned cross-site request forgery on an action that should have been guarded.

CSRF is one of the oldest tricks in the book, and most people file it under "boring." It gets interesting again the moment it lands somewhere that matters. Here it was on OpenAI's own Discord verification service, and the action it forged was quietly removing people from the OpenAI Discord. One link, and a member you targeted was unlinked, with nothing on their screen to tell them it had happened.

A Bugcrowd submission titled CSRF Vulnerability in the unlink endpoint, target *.openai.com, VRT Cross-Site Request Forgery, priority P3, status Resolved and fixed
The submission on OpenAI's Bugcrowd program: a CSRF on the /unlink endpoint, P3, marked Resolved and fixed.

How you get into the OpenAI Discord

OpenAI runs an official Discord server, and to get in you have to prove you actually have an OpenAI account. A small service at discord.verify.openai.com handles that handshake: you verify, and it links your OpenAI account to your Discord account so the server knows you belong.

The OpenAI Discord server showing the openai-verification channel, with a Verify with OpenAI button
The verification channel on OpenAI's Discord. You verify here to gain access.
A Discord message confirming the OpenAI and Discord accounts have been successfully linked
Once you verify, the service confirms your OpenAI and Discord accounts are linked.

If linking exists, so does its opposite: an unlink action to disconnect the two again. That is the one I went looking at, because "undo an important thing" is exactly the kind of action attackers love to find unguarded.

What CSRF actually is, in plain terms

Your browser is helpful to a fault. Once you are logged into a site, it quietly attaches your session to every request that goes to that site, no matter who started the request. Click a link, load an image, submit a form on some other page entirely, and if any of it points at a site you are logged into, your browser tags along with your logged-in identity.

Cross-site request forgery abuses that helpfulness. If a site lets a sensitive action be triggered by a request an attacker can forge, and it has no way to tell "the user really meant this" from "some other page told the browser to do this," then the attacker can make your browser perform that action as you. The standard defence is an anti-CSRF token: a secret value tied to your session that must accompany the real request, and that an attacker on another site cannot know or guess.

The endpoint had no such guard

The unlink action was triggered by an ordinary GET request to /unlink. No unpredictable token, nothing that proved the request came from a real click inside OpenAI's own page.

The raw HTTP request: GET /unlink to Host discord.verify.openai.com, with no anti-CSRF token and no cookie shown
The unlink request: a plain GET /unlink with no anti-CSRF token in sight.

That is the whole weakness. If the only thing standing between "linked" and "unlinked" is a request anyone can reproduce, then anyone can reproduce it, on your behalf, from a page you did not write.

Why a GET makes it worse

A state-changing action on a GET is the easiest possible CSRF, because a GET is what your browser does just by loading a URL. No form even required, an image tag, a redirect, or a link the victim clicks is enough to fire it.

Building the proof of concept

Burp Suite will generate a CSRF proof of concept for you: a tiny HTML page that, when opened, submits the request to the target on its own. Point it at /unlink and it produces a self-contained page.

Burp Suite's CSRF PoC generator producing an HTML page with a form that auto-submits to the unlink endpoint
Burp's CSRF proof-of-concept generator, building an HTML page that submits to /unlink by itself.
The generated CSRF proof-of-concept page open in a browser, ready to submit
The proof-of-concept page in a browser. In a real attack this would fire automatically, with no button to press.

In practice the attacker never shows a button. They wrap the same request in a link or an auto-submitting page and get it in front of the target: a Discord DM, an email, a comment, anywhere a curious member might click.

And the victim is out

Loading the page carried the action through, and the service happily reported the job done: the account had been de-linked.

An OpenAI success page stating that any Discord tied to the OpenAI account has now been de-linked
The result: OpenAI confirms the Discord account has been de-linked. The victim sees no warning and gives no consent.

Why it matters

The fix

Key takeaways

  • CSRF is only "boring" until it sits on an action that matters. "Undo an account link" is exactly such an action.
  • State-changing work on a GET with no token is the easiest CSRF there is, because just loading a URL is enough to trigger it.
  • The defence is a per-session secret the attacker cannot know: an anti-CSRF token, backed by SameSite cookies and POST-only state changes.
  • Always look at the reverse of a sensitive action. If linking is guarded but unlinking is not, unlinking is where the bug lives.
CSRF OpenAI Discord Authenticated Action
Share on X

More writeups