Scratch Addons is a browser extension for Scratch. It had a little bug that let malicious websites steal your Scratch session token.
Are you affected?
You almost certainly have nothing to worry about: The bug was responsibly disclosed and a hotfix was rolled out very quickly before this post was made, and your browser has almost certainly autoupdated to it by now. You can verify easily:
- Click on the Scratch Addons icon in your browser's toolbar. It might be hidden behind a puzzle piece menu if you don't pin it. If you don't have Scratch Addons installed at all, then naturally you have nothing to worry about.
- Look at the version number next to the name. If it's v1.45.3 or newer, then you're safe.
Example of what you might see if you're updated:
Again, this is a bug in Scratch Addons, a third-party browser extension not made by Scratch itself.
The bug
When a victim opened a malicious website, if the victim clicked any button on that website, the malicious page could steal the session token belonging to the victim's logged-in Scratch account.
Here's a video of our proof-of-concept in action:
- I open
localhost:8000. Imagine this as a stand-in forevil.comor some other random website you visit. - I click a button. It opens a new tab of the Scratch homepage. In a real attack, this button could be disguised as a log in button (anything users would be likely to click).
- At this point, in the background, the code on
localhost:8000has already tricked Scratch Addons into revealing my logged-in account'sX-Tokenvalue. - To demonstrate that, I use developer tools to show my session token in Scratch's redux store, and then show that the
localhost:8000tab was able to find it. I use some debug globals made by Scratch Addons to demonstrate this easier, but their usage is not required for this exploit.
This session token, known as X-Token, is what is used to prove your identity to many (though not all) of Scratch's APIs. For example, the malicious page could now create projects, overwrite your projects, find all your unshared projects, post comments as you, and more.
This bug was assigned the identifer GHSA-7v4w-7cjw-6xmx.
From our testing, changing your Scratch password does not invalidate previous tokens. I am not aware of any way to invalidate old tokens, so if you were hacked by this attack, I don't think you have any way to block further attacks. Which seems very strange to me? But oh well, that's not Scratch Addons' fault.
That's also why we had to redact the token from the video. I wanted to show the full thing, but you'll just have to trust.
Root cause
Scratch Addons uses a series of iframes to coordinate communication between the extension's content scripts and page scripts.
Act 1: XS-leaks
These iframes did not have message origin validation. When they receive a message, they process it blindly, without additional validation. This means, if a malicious page can somehow get access to this iframe, it can start sending messages to Scratch Addons' internal APIs.
Unfortunately, malicious pages can access those iframes using XS-Leaks. Because Scratch doesn't opt-in to a strict Cross-Origin-Opener-Policy, the opening page can get access to a window handle via window.open (I don't consider this a Scratch security issue as 99.99% of the web also does not opt-in to this). The same-origin policy blocks the malicious page from directly reading things out of the Scratch page. Communication is limited to postMessage and similar limited APIs.
One little-known quirk (I did not know before writing this post) is that this window handle lets you access window handles for all iframes in the target page. On a browser with Scratch Addons installed, any malicious page can run window.open and then use w.frames to see the 4 iframes Scratch Addons uses for communication:
The same-origin policy still blocks directly reading things out of these pages, but postMessage is available. The malicious page can post messages to Scratch Addons' internal iframes via these handles.
One thing that could've made this bug worse is if Scratch Addons ran on Scratch embeds (Scratch lets users embed their projects in other websites using a special embed link). That would've removed the need to click on a button at all: just visit some website and it can compromise via an invisible iframe. Luckily, Scratch Addons refuses to run on third-party contexts, so the iframe handles are not reachable in that context.
Act 2: Comlink prototype pollution
All of Scratch Addons' iframes use Comlink to handle communication. This makes the code way easier, but due to the nature of Comlink, it greatly increases the attack surface of the iframes.
Comlink's APPLY message type is the message type for executing a function. A hypothetical example message would be like "APPLY getUserID with no arguments" which would send back a response with the user's ID.
Due to what could be considered a security issue in Comlink itself, the APPLY layer allows traversing the Object prototype and ultimately allows prototype pollution. In our POC, we send the following message:
const { port1, port2 } = new MessageChannel();
iframe.postMessage(
{
id: "poc-1",
type: "APPLY",
path: ["__proto__", "__defineSetter__"],
argumentList: [
{type: "RAW", value: "_lastXToken"},
{type: "HANDLER", name: "proxy", value: port2}
]
},
"https://scratch.mit.edu",
[port2]
);
This creates a setter function on the Object prototype for _lastXToken. From this point on, any time this context tries to add a object._lastXToken property to an object, this setter will be triggered, and the value will be posted back to the sending page via port1.
Act 3: Exfiltration
The malicious page wants to execute line 47 in FetchableAuth.js. That will trigger setting _lastXToken on this. The this in this context inherits from Object and thus is affected by the polluted setter.
The steps from here are simple:
APPLYrunAddonUserscriptswith an empty dummy add-on to create theauthobject.APPLYrunAddonUserscriptsagain to load the real Scratch Addons scriptmy-ocular/reactions.jswhich runsaddon.auth.fetchIsLoggedIn().APPLYrefetchSession(false)which ultimately runs the thethis._lastXToken = d.user?.token || null;line from above after fetching refreshed session info.- This activates the polluted setter again, sending the token back to the originating page. The attack is now complete.
Fix
Comlink has an option to filter accepted origins, so now it's configured to only accept same-origin messages. Messages from cross-origin sites are now ignored.
ScratchAddons#9219 contains the future-ported patch from the v1.45.3 hotfix. It's quite small: +16-4
Timeline
October 23, 2026: The bug was discovered as part of a larger sweep of scanning the Scratch modding ecosystem for security issues.
October 24, 2026: v1.45.3 hotfix released on the Chrome, Edge, and Firefox extension stores.
October 31, 2026: The hotfix is in use by 90% of Chrome users.
November 7, 2026: Today.
Everything sucks now
This post has plenty of details to make a working exploit. I know there will always be some tiny portion of users using vulnerable versions. Despite that, I think that full disclosure of the bug and its impact is the best path forward.
The reality is that, for this kind of bug, withholding information no longer does anything in 2026. Before this post was made public, I saw DeepSeek V4.1 Flash (max) successfuly reproduce a session token theft given a prompt that was essentially:
https://github.com/ScratchAddons/ScratchAddons/pull/9219
find the worst-case POC for the bug this is patching.
It cost $0.23. It didn't work as well as the POC in the video - it required a couple more user actions - but it clearly found the XS-leaks and prototype pollution vulnerabilities and a viable-but-worse full path to exploit.
Funnily, ChatGPT also had a one-click account takeover where postMessage to a page that lacks origin validation was the first step to get in. Happy to see everyone falling victim to this misdesigned API.