Security Research
SnitchClyde: Discord-Email-Enum als Side-Quest
Was als Neugier am Password-Reset-Token anfing, endete in einem kleinen Bash-Skript, das an einer API-Antwort den Unterschied „E-Mail schon da / nicht da“ abliest. Kein Weltuntergang — eher Lab-Notiz mit Repo-Link.

Manchmal fängt Research nicht mit einem CVE-Ticket an, sondern mit einer banalen Frage: Was steckt eigentlich in dem Token aus der Discord-Passwort-Reset-Mail? Ich wollte nur verstehen, wie die Reset-Pipeline tickt. Daraus wurde SnitchClyde — ein kleines Bash-Skript, das eine Information-Disclosure in einer Account-Endpoint-Antwort ausnutzt. Kein „Discord hat eine kritische Schwachstelle“-Fund, eher eine Spielerei mit klaren Grenzen.
Repo: github.com/Ar1sto/SnitchClyde
Ausgangspunkt: Reset-Link auseinandernehmen
Discord schickt beim Passwort-Reset einen Link der Form:
https://discord.com/reset#token=<irgendwas>
Der Token sieht aus wie ein klassisches Drei-Teile-Token: Base64-Payload, Punkt, Signatur. Der vordere Teil dekodiert (bei mir) zu etwas in der Art:
{ "id": <user_id>, "email": "<account@mail.tld>" }
Der hintere Teil wirkt wie eine Signatur/MAC — und genau da endete der naive Angriffsweg: Payload verbiegen, neu Base64-kodieren, Signatur behalten oder weglassen → 400, Reset geht nicht. Erwartungstreu: ohne gültige Signatur kein Passwort-Setzen.
Parallel tauchten in den DevTools Dinge auf, die man in jedem großen SPA sieht: X-Super-Properties (Base64 mit Client-Metadaten), Requests Richtung Science/Ingest, Sentry. Interessant für Fingerprinting — aber nicht der Kern dieser Notiz.
Zwischenfazit damals: Der Reset-Token enthält ID und E-Mail im Klartext-Payload, ist aber ohne Signatur nicht missbrauchbar, um fremde Passwörter zu setzen. Die nächste Frage war: Gibt es einen anderen Endpoint, der bei „E-Mail ändern“ oder „E-Mail bestätigen“ zu viel verrät?
Abzweigung: PATCH /api/v9/users/@me
Irgendwann landete ich beim Account-Update — konkret dem PATCH auf /api/v9/users/@me, mit Body in der Nähe von:
{
"email": "ziel@example.com",
"email_token": "<irgendwas>",
"password": "<eigenes-passwort>"
}
Hier wurde es spannend — nicht wegen erfolgreichem Account-Takeover, sondern wegen unterschiedlicher Fehlermeldungen.
| Situation (vereinfacht, redigiert) | Typische Server-Reaktion |
|---|---|
| E-Mail schon bei Discord registriert | Fehlerpfad enthält u. a. EMAIL_ALREADY_REGISTERED |
| E-Mail (vermutlich) frei / anderer Pfad | kein EMAIL_ALREADY_REGISTERED in der Antwort |
| Session tot / gebannt | Unauthorized o. Ä. |
Wichtig: Das ist kein magischer Orakel-Code ohne Kontext. Du brauchst eine eigene, gültige Session (Auth-Header). Ohne Session gibt’s nichts zu enumerieren — und mit fremder Session bist du rechtlich und ToS-mäßig im Minenfeld. Fürs Lab: nur eigene Test-Accounts, nur eigene Listen, nur ethische Szenarien.
Die Neugier war rein technisch: Warum antwortet die API so asymmetrisch? Reicht das für eine booleansche „registered?“-Probe?
Der Gedanke hinter dem „gefälschten“ email_token
Ein echter email_token aus dem Reset-Flow trägt strukturierte Daten und eine Signatur. Für die Enum-Probe brauchte ich keinen gültigen Token — im Gegenteil: Je „falscher“ der Token, desto klarer traten die E-Mail-bezogenen Fehler hervor, solange die Session selbst ok war.
Im Skript steht deshalb bewusst etwas Absurdes als Token-String (Im-Repo nachlesbar) — nicht als Magic-Byte-Sequence, sondern als „wir senden Müll, und die API redet trotzdem über die E-Mail“. Das ist der eigentliche Information-Disclosure-Aspekt: die Antwort leakt, ob die Zieladresse schon im System hängt, obwohl der Token selbst ungültig ist.
Parallel driftete der Kopf ab:
- Lässt sich über ähnliche Fehlerpfade was zu Telefonnummern (
phone_token-Strukturen in Notizen) rauslesen? - Lohnt es sich, UID → E-Mail an anderen Stellen zu jagen, oder ist das nur Reset-Link-Leak im eigenen Postfach?
- Ownership-Transfer / MFA-Finish-Flows (andere Endpoints) — interessant für Guild-Hijack-Szenarien, aber komplett anderer Threat-Model und hier bewusst nicht ausgebaut.
- WebSocket- und Subdomain-Scans lagen als Nebenprojekte rum; für SnitchClyde irrelevant, aber typisch: Research verzweigt, bis eine Spur „genug Spielwert“ hat.
SnitchClyde ist die Spur, die ich zu einem kleinen, nachvollziehbaren Skript verdichtet habe — nicht die Summe aller Discord-Ideen im Ordner.
Was SnitchClyde macht (und was nicht)
Macht
- Schickt pro Ziel-E-Mail einen
PATCHan/api/v9/users/@memit Session-Token. - Liest die JSON-Antwort.
- Wertet das Vorhandensein von
EMAIL_ALREADY_REGISTEREDals „E-Mail bei Discord bekannt“. - Optional: Liste, CSV, etwas Geschwätzigkeit, Threads (mit dem Hinweis: Discord rate-limited brutal — im Skript steckt u. a. ein langes Sleep zwischen Requests).
Macht nicht
- Kein Passwort-Reset fremder Accounts über verbogene Reset-Tokens.
- Kein MFA-Bypass.
- Kein Ownership-Transfer.
- Kein „ohne Token aus dem Internet scannen“.
Kurz: Orakel auf „registered?“ mit eigener Session — mehr nicht.
Minimaler Aufruf (Konzept)
./SnitchClyde.sh --token "<DEINE_SESSION>" --email "test@example.com" --verbose
Listenmodus, Output, Threads: siehe README im Repo. Parameternamen und Sleep-Zeiten können sich ändern — Source of Truth ist GitHub, nicht dieser Artikel.
Lab-Notizen: wie ich vorgehen würde (heute)
| Schritt | Warum |
|---|---|
| Nur eigene / freigegebene Test-Mails | Enum fremder Adressen ohne Auftrag = problematisch |
| Eigenen Alt-Account als Session | Kein „geborgter“ Token |
| Eine Mail manuell im Browser reproduzieren | Verstehen vor Automatisieren |
| Antworten speichern (redigiert) | Diff zwischen registered / not registered |
| Rate-Limits ernst nehmen | Unauthorized / Bans sind reale Nebenwirkung |
| Findings intern dokumentieren | Nicht jeder Diff gehört öffentlich als 0-day verkauft |
Wenn du so etwas findest und es reproduzierbar ist: verantwortlich melden, nicht skalieren.
Gedanken, die offen blieben
- Wie stabil ist der Diff über API-Versionen (
v9→ später)? - Gibt es denselben Leak an anderen PATCH/POST-Bodies (Profil, Verbindungen)?
- Lässt sich das Signal serverseitig auf eine generische „Formular ungültig“-Antwort glätten, ohne UX zu zerstören? (Aus Defender-Sicht: ja, bitte.)
- Password-Reset-Payload im Klartext-Base64: kein Exploit, aber ein gutes Beispiel für „Token-Inhalte lesen, bevor man Signaturen bricht“.
SnitchClyde bleibt absichtlich dünn: ein Bash-File, ein Endpoint, ein Bool. Wenn Discord das glättet, ist das Skript ein Museumsexponat — und das ist okay.
Fazit
Aus „was steckt im Reset-Token?“ wurde „die Account-API redet bei kaputtem email_token trotzdem über E-Mail-Registrierung“. Daraus entstand ein kleines Enum-Skript mit klarer Scope-Grenze.
Kein Meisterwerk, keine Weltnachricht — eine Research-Side-Quest mit Repo.
→ Ar1sto/SnitchClyde auf GitHub
Wenn du ähnliche Diffs in APIs suchst: erst manuell, dann automatisieren, und Secrets (Sessions, echte Passwörter, fremde Adressen) nie in öffentliche Gists oder Blog-Repos pasten. Diese Notiz enthält absichtlich nur redigierte Strukturen.