Dev.to Security 🔐 Cybersecurity 👁 0 📖 5 min read

Browser extension monitoring: spot a sold or hijacked add-on

Originally published on Extenify. Browser extension monitoring matters because the extension you vetted on install day is not necessarily the code running in your browser today. The big extension attacks of the past yea

Originally published on Extenify.

Browser extension monitoring matters because the extension you vetted on install day is not necessarily the code running in your browser today. The big extension attacks of the past year did not come from shady new listings. They came from trusted extensions that were sold to a new owner or had their developer account phished, then pushed an update that every user installed silently.

Below: what those incidents had in common, the signals that showed up before or during them, and a tiny Node.js check you can run daily.

Three incidents, one pattern

  • QuickLens (about 7,000 users). Listed for sale in October 2025, transferred on February 1, 2026 to an owner with a contact address on a .top domain, then updated on February 17 with code that stripped security headers like X-Frame-Options and fetched JavaScript from a server to run on every page. All the original features kept working.
  • ShadyPanda (4.3 million Chrome and Edge users). Koi Security described extensions that behaved normally for years before an update turned them into spyware or a remote code execution backdoor.
  • Cyberhaven (December 2024). An employee was phished with a fake "Chrome Web Store Developer Support" email and a malicious OAuth app. The attacker published version 24.10.4, which was live for roughly a day before the team caught it and pulled it within an hour.

In every case, the extension was already installed and trusted, and something visible on the listing changed before or during the attack.

"No new permissions" is not a safety signal

This is the part most developers get wrong. Chrome disables an extension when an update adds a permission that triggers a warning, and keeps it disabled until the user accepts. An attacker who wants the update to reach everyone therefore avoids new permissions and reuses the access the extension already has. If it already runs on all sites, it needs nothing more.

So the useful signals are elsewhere:

Signal Sold extension Hijacked account
Developer name, email, website Often changes, sometimes weeks early Usually the same
Privacy policy URL Often moves to a new domain Usually the same
Release timing First update after a quiet stretch A release the team never shipped, often at odd hours
Code New script files, remote loading Small additions to a clean build
Reviews New 1-star reports of ads or redirects Same, if it stays live

A dormant extension that suddenly ships right after its contact email moves to an unfamiliar domain is the QuickLens pattern, and it would have been visible about two weeks before the bad update.

A minimal daily check in Node.js

Chrome downloads extensions from a public update endpoint, and you can request the same package for any public extension ID. Two facts make a cheap monitor possible: a removed or taken-down extension answers 204 No Content instead of a file, and any code change produces a different package. Here is a stripped-down sketch (Node 18+) that hashes whatever Chrome serves:

import { createHash } from 'node:crypto';

const url = (id) =>
  'https://clients2.google.com/service/update2/crx?response=redirect' +
  '&prodversion=155.0&acceptformat=crx3&x=' + encodeURIComponent(`id=${id}&uc`);

export async function snapshot(id) {
  const res = await fetch(url(id));
  if (res.status === 204) return { id, served: false };
  if (!res.ok) throw new Error(`${id}: HTTP ${res.status}`);
  const buf = Buffer.from(await res.arrayBuffer());
  return { id, served: true, sha256: createHash('sha256').update(buf).digest('hex') };
}

Store the result, compare it with yesterday's, and alert on a flip to served: false or a hash change. The full guide goes further: it unpacks the CRX3 file (a short header plus a normal ZIP), reads manifest.json, and reports exactly which script files are new or changed, plus a Firefox check against the official addons.mozilla.org API that returns authors, support email and declared permissions.

A few lessons from running checks like this:

  1. Confirm on two runs. During a staged rollout the endpoint can serve old and new versions alternately, so a flip-flop is not an incident.
  2. Remote payloads stay invisible. QuickLens loaded its real code at runtime. A fingerprint tells you the package changed, not what it downloads later, which is why publisher fields matter just as much.
  3. Be polite. One request per extension per day is plenty.
  4. On Firefox, track the author ID, not the display name, since a name can be edited to look unchanged.

Sorting alerts so people read them

  • Act today: the store stops serving the package; a new version lands within weeks of a developer or contact change; new broad host access such as <all_urls>; any version of your own extension you did not publish.
  • Review this week: a publisher change with no new version yet; a first release after six or more months of silence; a cluster of 1-star reviews mentioning ads, redirects or search changes.
  • Log only: routine bumps with the same publisher, same access and the usual build output.

If you publish extensions, compare the live version on every store with your release log daily. A mismatch you cannot explain is the fastest sign that someone else is publishing in your name. If you manage a team's browsers, Chrome's ExtensionSettings policy lets you block specific permissions and keep extensions away from sensitive hosts with runtime_blocked_hosts.

Key takeaways

  • The common attack is a trusted extension that changes hands or gets hijacked, not a new malicious one.
  • Watch publisher fields, declared access and the actual code Chrome serves.
  • Unchanged permissions prove nothing; attackers avoid triggering the re-consent prompt.
  • A daily check catches takedowns (HTTP 204) and code changes; confirm on two runs before escalating.
  • Monitor your own listing too: a version you did not ship means your account is compromised.

The original post has the complete CRX-parsing script, the Firefox AMO check and an FAQ: how to monitor browser extensions for ownership changes and malicious updates. If you would rather not maintain scripts for dozens of listings, Extenify tracks Chrome, Edge and Firefox extension listings and alerts on publisher and version changes.

Have you ever had an extension you relied on change owners or ship an update you did not expect, and how did you find out?

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.