Security Alert: Confirmed Malware
Cursor-A custom cursor
ID: hdgdghnfcappcodemanhafioghjhlbpb
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- yurih4174View Profile
- Privacy
- Privacy Policy
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- Yes
Choose your own cursor. Customize your cursor and freshen your experience.
Cursor-A replaces the default cursor with something cute, funny, and trendy. We offer: ๐Pre-made themes and presets ๐Turn on/off this extension easily ๐Create your collection of mouse cursors from any images Customize your experience of using this app โก๏ธ
The extension registers an onMessageExternal listener that accepts commands from any external website with no sender origin validation. The 'get_config' command dumps the entire chrome.storage.local contents to the caller; 'set_config' and 'set_config_sync' allow any external website to write arbitrary data into the extension's local and sync storage; 'install_collection' and 'install_pack' let external sites mutate cursor collection data. Any website can silently read all stored extension data and overwrite extension configuration without authentication.
chrome.runtime.onMessageExternal.addListener(function(e, t, o) { if ("getInstalled" == e.action) return o({ collections: this.collection, ver: chrome.runtime.getManifest().version, action: "get_installed_collection" }); if ("install_collection" == e.action) { ...chrome.storage.local.set({ collection: this.collection })... } if ("install_pack" == e.action) { ...chrome.storage.local.set({ collection: this.collection })... } return "get_config" == e.action && chrome.storage.local.get(null, function(e) { return o(e) }.bind(this)), "set_config" == e.action ? (chrome.storage.local.set(e.data), o({ status: !0 })) : "set_config_sync" == e.action ? (chrome.storage.sync.set(e.data), o({ status: !0 })) : "get_config_sync" == e.action ? o(this.config_sync) : void 0}.bind(this))The extension generates and persists a unique client identifier (UUID v4 stored as 'cid' in chrome.storage.local) and sends a Google Analytics pageview beacon to https://www.google-analytics.com/collect on every background page load using tracking ID UA-211094822-1. This constitutes undisclosed user tracking: a permanent per-device UUID is silently created and exfiltrated to a third-party analytics endpoint without user consent or disclosure. The extension's stated purpose is cursor customization, yet it assigns each user a permanent tracking identifier.
async function s() { const e = await new Promise((e => { chrome.storage.local.get(["cid"], (t => { e(t) })) })); let { cid: t } = e; return t || (t = (0, r.v4)(), chrome.storage.local.set({ cid: t })), t}async function i(e) { const t = undefined, o = { v: "1", tid: e, cid: await s(), t: "pageview", dp: "/background", dt: "background", dh: `chrome-extension://${chrome.runtime.id}` }, r = `https://www.google-analytics.com/collect?${new URLSearchParams(o).toString()}`; await fetch(r, { method: "POST", body: "" })}o.default = iOn installation the extension opens a tab to https://ladnet.co/<extensionId>/thanks.html and registers https://ladnet.co/<extensionId>/uninstall.html as the uninstall URL, sending the live extension runtime ID to the undisclosed third-party domain ladnet.co. This allows ladnet.co to track the specific installed extension instance and monitor install and uninstall lifecycle events across 2M+ users without disclosure in the extension's privacy policy.
(0, r.default)("UA-211094822-1"),chrome.runtime.onInstalled.addListener((e => { "install" === e.reason && chrome.tabs.create({ url: `https://ladnet.co/${chrome.runtime.id}/thanks.html` }) })), chrome.runtime.setUninstallURL(`https://ladnet.co/${chrome.runtime.id}/uninstall.html`);On installation the extension immediately queries all open browser tabs and programmatically injects customCursor.js into every tab with an http/https URL using chrome.tabs.executeScript. This means the content script is pushed into all currently open pages (banking sites, email, internal tools) immediately upon install without waiting for page reload. While the extension declares <all_urls>, proactively injecting into all pre-existing tabs at install time is aggressive and exposes browsing context earlier than expected.
onInstall() { chrome.tabs.query({}, (function(e) { for (let t = 0; t < e.length; t++) if (e[t].url && -1 != e[t].url.indexOf("http")) try { chrome.tabs.executeScript(e[t].id, { file: "js/customCursor.js" }, (function(o) { let r; chrome.runtime.lastError && console.log("tab: %s", e[t].url) })) } catch (e) {} })), chrome.storage.local.set({ collection: i, size: 3, myOwnListCol: {} })}The reload() method calls chrome.tabs.query({}) with no filter, enumerating every open tab across all windows, then sends a message to each tab ID. This grants the popup visibility of all open tabs and the ability to coordinate actions across every content-script-injected page simultaneously, which is broader than needed for cursor customization.
reload() { chrome.tabs.query({}, (function(e) { let t = { action: "update" }; for (let n = 0; n < e.length; ++n) chrome.tabs.sendMessage(e[n].id, t) }))}clear() { let e = { action: "clear" }; chrome.storage.local.set({ selected: null, selected_type: "none" }), chrome.runtime.sendMessage(e)}CSS template strings are assembled by substituting cursor image path values retrieved from chrome.storage.local (f.cursor.path, f.pointer.path) and assigned to a style element via innerHTML on every page the content script runs across <all_urls>. If storage is tampered with (e.g., via the unvalidated set_config external message or a compromised sync account), an attacker could inject arbitrary CSS into every browsed page.
d = function() { if (f) { let e = 3;... "system" == f.type && (... t = t.replace(/#cursor#/g, f.cursor.path), t = t.replace(/#pointer#/g, f.pointer.path), ... l.innerHTML = t); "custom" == f.type && ( void 0 === f.pointer.path ? (t = s, t = t.replace(/#cursor#/g, f.cursor.path), ... l.innerHTML = t) : ...) } else l.innerHTML = "";The content script uses the deprecated chrome.extension.onMessage API and listens for action messages without verifying the sender's identity or origin. Any extension or page with access to the messaging channel can trigger a 'clear' to wipe cursor CSS from all pages or 'update' to force a storage read and CSS re-injection, with no sender authentication.
h = function() { chrome.storage.onChanged.addListener((function(e, t) { "local" == t && p() })), p(), chrome.extension.onMessage.addListener((function(e, t, n) { "update" == e.action && p(), "clear" == e.action && (l.innerHTML = "") }))}()The createTemplate method constructs an HTML string via template literals that directly interpolates user-controlled values from chrome.storage (collection IDs and image paths) and passes the result to jQuery's append(), which parses and executes it as HTML. If storage is compromised via the unvalidated set_config external message handler, this creates a stored XSS surface within the extension's settings page context.
static createTemplate(e, t, n, r, o) { const i = undefined; return `<div class="cur-wrapper" data-id="${e}"> <img class="close-card" src="../icons/delete.png"> <div class="card" id="template" data-id="${e}"> <div class="curContainer"> <div class="upload ${r?"filled":""}" data-id="${e}" data-type="cursor"> <img class="upload-img" src="${t}"> </div> </div> <div class="curContainer"> <div class="upload ${o?"filled":""}" data-id="${e}" data-type="pointer"> <img class="upload-img" src="${n}"> </div> </div> </div>`}// Used as: $("#mycollection").append(e)By severity
Versions scanned
Showing 3 of 3 scanned versions with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 0.7.3 | 7 |
| 0.7.2 | 8 |
| 0.7.1 | 3 |
Files with findings
3 distinct paths โ top paths by unique finding count:
- js/background.js11
- js/popup.js5
- js/settings.js2
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.