Is Videochat Extension - IP Locator for Omegle safe?

Medium risk

Videochat IP Locator is medium risk. Uninstalling triggers Chrome to visit a URL encoding a profile: time installed, browser language, Patreon status, and a timezone flag for outside Russia/CIS. XOR-encoded with a hardcoded key, trivially readable by an interceptor.…

qrlukev4.1.7Chrome Web Store
49Risk
Who publishes it

qrluke - no other listings under this identity, 1 shared hostname

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

Publisher
qrluke

Shared hosts - 1 hostname

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.

api.jsonsilo.com
Also called by 3 other listings, including WhatsHide

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

Publishers can request a review.

Findings

Install profile sent to developer server when you uninstall

Uninstalling triggers Chrome to visit a URL encoding a profile: time installed, browser language, Patreon status, and a timezone flag for outside Russia/CIS.

XOR-encoded with a hardcoded key, trivially readable by an interceptor.

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

You uninstall the extension from your browser.

The extension did this

Chrome fetches a pre-registered URL encoding an install profile, including a flag for whether your clock is set to a Russian or CIS timezone.

The profile is XOR-encoded with a static hardcoded key visible in the extension source, making it readable by anyone who intercepts the request.

Fields encoded in the uninstall URL data parameter
  • First seen timestamp
    1748000000000

    When the extension was first installed on your browser.

  • Legacy user flag
    false

    Whether your install predates the current version series.

  • First install flag
    false

    Whether this was your very first install of the extension.

  • Legacy stats time
    0

    Accumulated usage time recorded under an older storage format.

  • Outside CIS timezone
    true

    True if your timezone is not one of 27 Russian/CIS zones in the source, letting the developer infer your approximate region.

  • Patreon status
    unknown

    Whether you were expected to have an active Patreon subscription.

  • Browser language
    en

    Your browser's reported UI language.

The code that does this

Profile assembly and XOR encoding (background.js:1619-1642)

Readable version

Profile assembly and uninstall URL registration

background.js
async function buildAndRegisterUninstallUrl() {  const { legacy } = await chrome.storage.sync.get({ legacy: { stats: { time: 0 } } });  const legacyTime = legacy?.stats?.time || 0;  const profile = {    fs:    await getStorage('firstSeen', 0),         // first seen timestamp    ilu:   await getStorage('isLegacyUser', false),  // is legacy user    fi:    await getStorage('firstInstall', false),  // first install    ots:   legacyTime,                               // old stats time    pa:    !CIS_TIMEZONES.includes(                  // NOT in Russian/CIS timezone             Intl.DateTimeFormat().resolvedOptions().timeZone),    pstba: await getStorage('patreonSupposedToBeActive', 'unknown'),    lg:    chrome.i18n.getMessage('language'),       // browser language  };  function xorEncode(str, key) {    let out = '';    for (let i = 0; i < str.length; i++)      out += String.fromCharCode(str.charCodeAt(i) ^ key.charCodeAt(i % key.length));    return out;  }  const encoded = btoa(    xorEncode(JSON.stringify(profile), 'a-super-secret-key-that-is-not-so-secret')  );  const url = `https://api.vext.works/farewell?v=${chrome.runtime.getManifest().version}&data=${encodeURIComponent(encoded)}`;  chrome.runtime.setUninstallURL(url);}
Why you can't catch this in DevTools

The profile is XOR-encoded with a static key visible in the source code, then base64-encoded. The result is placed in the data= query parameter of the uninstall URL. Anyone with the key (published here) can decode any captured uninstall request.

Decoded value
{  "fs": 1780680743225,  "ilu": false,  "fi": false,  "ots": 0,  "pa": true,  "pstba": "unknown",  "lg": "en"}
Reproduce it yourself

Decodes any uninstall URL data= parameter captured from this extension, producing the original profile JSON.

Requires
  • Node.js 14+
decode-uninstall-payload.js · js
#!/usr/bin/env node// Decode the data= parameter from api.vext.works/farewell uninstall requests.// Source: background.js:1637-1641 (XOR key hardcoded at line 1639)const KEY = 'a-super-secret-key-that-is-not-so-secret';function xorDecode(encoded, key) {  let result = '';  for (let i = 0; i < encoded.length; i++)    result += String.fromCharCode(encoded.charCodeAt(i) ^ key.charCodeAt(i % key.length));  return result;}// Pass the raw value of the data= query parameter (URL-decoded) as the first argument.const b64 = decodeURIComponent(process.argv[2] || '');if (!b64) {  console.error('Usage: node decode-uninstall-payload.js <base64_data_param>');  console.error('Example: node decode-uninstall-payload.js Gg8VBlJfQxpDVVNCVUQdW1VJHVhKCBhYS0lLDwMHSF9NSxpHWRQEGBJIX1cfEQEPSVVPUBUVD1ERC1gRREMEXh0RTExVVlgdBEMcEg1QSVYNSlFPUgAcDw4%3D');  process.exit(1);}const decoded = Buffer.from(b64, 'base64').toString('binary');const plaintext = xorDecode(decoded, KEY);console.log('Decoded profile:');try {  const parsed = JSON.parse(plaintext);  console.log(JSON.stringify(parsed, null, 2));  console.log();  console.log('Field meanings:');  console.log('  fs   =', parsed.fs,   '  (firstSeen timestamp, ms since epoch)');  console.log('  ilu  =', parsed.ilu,  '  (isLegacyUser)');  console.log('  fi   =', parsed.fi,   '  (firstInstall)');  console.log('  ots  =', parsed.ots,  '  (legacy stats time)');  console.log('  pa   =', parsed.pa,   '  (true = NOT in Russian/CIS timezone)');  console.log('  pstba=', parsed.pstba,'  (patreonSupposedToBeActive)');  console.log('  lg   =', parsed.lg,   '  (browser language)');} catch(e) {  console.log('Raw plaintext:', plaintext);}
How to run it
  1. 1Capture the GET request to https://api.vext.works/farewell when uninstalling the extension.
  2. 2Copy the value of the data= query parameter.
  3. 3Run: node decode-uninstall-payload.js '<data_value>'

Behavior config fetched from third-party JSON hosts at startup

Each browser start, the extension fetches its config from up to four URLs, two public JSON hosts (jsonsilo.com, npoint.io) anyone can register on.

The payload is encrypted with a hardcoded passphrase and RSA-signed.

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

You install the extension or restart your browser.

The extension did this

The extension fetches its config from a priority-ordered list of four URLs, two of which are on third-party public JSON hosting services.

The decrypted config controls which sites the extension activates on and can override the redirect URL shown when a blocked site is visited.

Config delivery endpoints (tried in order)
    • vext.works

    Primary config endpoint, developer-controlled domain.

    • api.jsonsilo.com

    First fallback, public third-party JSON hosting. Specific bucket UUID 3f0366b1-ddea-467f-83d8-cc8ba36c4cab hardcoded in source.

    • api.npoint.io

    Second fallback, public third-party JSON hosting. Specific document ID df41cdd27f701e150e23 hardcoded in source.

    • api.vext.works

    Third fallback, developer-controlled subdomain.

The code that does this

Priority fetch loop and endpoint list (background.js:749, 764-795)

Readable version

Endpoint list

background.js
const CONFIG_URLS = [  "https://vext.works/status.json",  "https://api.jsonsilo.com/public/3f0366b1-ddea-467f-83d8-cc8ba36c4cab",  "https://api.npoint.io/df41cdd27f701e150e23",  "https://api.vext.works/status.json"];

Fetch loop

background.js
async function fetchRemoteConfig(timeoutMs = 5000) {  for (let i = 0; i < CONFIG_URLS.length; i++) {    const url = CONFIG_URLS[i];    try {      const response = await fetchWithTimeout(url, timeoutMs);      if (!response.ok) continue;      let data = await response.json();      if (typeof data === 'string') {        try { data = JSON.parse(data); }        catch { continue; }      }      if (!isValidConfigShape(data)) continue;      const decrypted = await decryptAndVerify(data);      if (!decrypted) continue;      if (!isValidConfigContent(decrypted)) continue;      await chrome.storage.local.set({ encryptedDataCache: data });      return decrypted;    } catch { continue; }  }  return null;}
What the config controls

Once decrypted and validated, the config's `blacklist` array is merged with the extension's built-in site list to determine which videochat sites the extension activates on. A `forgivelist` array can remove sites from that combined list. The `try_this_instead` or `try_this_instead_dict` field supplies a redirect URL that the extension displays when a user navigates to a blacklisted site. DA observed the extension making this fetch 6 times during a 120-second session observed during dynamic analysis, all returning HTTP 200.

What it can do

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

  • Act on the current tab, but only after you click the extension

    activeTab

  • Add items to the right-click menu

    contextMenus

  • Show a panel beside the page

    sidePanel

  • Store data in your browser

    storage

  • See the address and title of every tab you have open

    tabs

  • Speak text aloud

    tts

  • Store an unlimited amount of data in your browser

    unlimitedStorage

Updated 30 September 2026alchldmijhnnapijdmchpkdeikibjgoi