
Reverse-Engineering the KREMLIN Chrome Extension: Permission Abuse, DNR Rules, and Native Messaging
Introduction
This post reverse-engineers how a banking trojan used a Chrome extension to hold a position inside the browser instead of on the disk: which permissions it needed, how declarativeNetRequest rules can quietly rewrite the pages a user sees, and how native messaging turns that browser foothold into endpoint code execution. The campaign behind it is tracked as KREMLIN, and the CyberSecurityNews write-up dated 2026-09-16 reports more than 1,500 systems infected through a malicious Chrome extension.
That is roughly all I can state as confirmed: a malware family, a delivery component, and a victim count. In the material available to me, the report does not name extension IDs, the distribution vector, C2 infrastructure, or which institutions were affected.
The gap is why most of this post works from Chrome platform mechanics instead of from a sample. A dropper gets an attacker a process on the host. An extension gets them a position inside the browser — persistent, DOM-aware, network-capable, and almost never present in endpoint inventory.
What the KREMLIN Report Confirms — and Where the Record Stops
Confirmed from the public report:
- KREMLIN is classified as banking malware.
- The campaign used a malicious Chrome extension.
- The reported victim count is over 1,500 systems.
- Publication date: 2026-09-16, CyberSecurityNews.
Not stated in the available material:
- extension IDs, names, or the Chrome Web Store listing (if one existed)
- distribution vector — sideloading, enterprise policy abuse, fake installer, or store listing
- C2 domains or the protocol used for tasking
- which banks or regions were targeted
- whether the extension itself exfiltrated, or only the native host did
A family name and a victim count carry a headline. They do not carry a hunting rule. So everything below that goes beyond the report is drawn from documented Chrome behavior — what a banker needs and what the APIs permit — and I mark it as mechanism or inference, not as KREMLIN forensics.
Why a Chrome Extension Is a Strong Payload for a Banking Trojan
The structural advantages over a standalone dropper are not accidental:
- Install location. Extensions live in the user profile, not
Program Filesor/usr/local/bin. No admin rights, no service, no driver, and far less attention from AV heuristics tuned for executables. - Persistence. Once installed, an extension survives reboots and Chrome updates. Uninstalling Chrome does not remove a browser extension.
- Early execution. Content scripts and
declarativeNetRequestrules apply before or around page script execution, so the attacker is often first to touch the page. - Data access. DOM, form fields, session cookies via same-site page context, and navigation events across every origin the user visits.
- Network shaping.
declarativeNetRequestcan redirect, block, and rewrite headers without a second process or a proxy.
The counterweight is real, though: extensions cannot run arbitrary native code or read arbitrary files from disk. They are sandboxed and their permissions are declarative. Which is exactly why a campaign like this needs the nativeMessaging component at all.
| Capability the banker wants | Component required |
|---|---|
| Read/modify login DOM | scripting, content scripts, host permissions |
| Redirect or block traffic | declarativeNetRequest (MV3) or webRequest (MV2) |
| Read session state | page context, cookies, tabs |
| Persist config | storage, alarms |
| Touch disk or spawn a process | nativeMessaging host |
Reading the Extension Manifest Like an Auditor
A banking overlay does not need exotic permissions. It needs broad ones, and broad permissions are boring.
The minimum viable set for a login-page overlay plus redirect looks like this: host permissions covering either <all_urls> or a set of per-bank match patterns; scripting and content script matches to inject; tabs so the extension knows when the user lands on a target origin; storage for config and tasking; alarms for polling instead of a long-lived background page; and, on Manifest V2 or with enterprise flags, webRequest and webRequestBlocking.
Now the uncomfortable part: a coupon finder, a password manager, a bookmark tagger, and a screen-reader helper ask for the same set. Reading the permission string tells you an extension can see every bank page. It cannot tell you whether it will. That is the shape of extension permission abuse in practice — the surface is legitimate API access pointed at hostile ends, not a broken permission check. Permission review is a filter, not a verdict, and any process treating a scary permission list as sufficient detection will be wrong in both directions.
Chrome Extension Permission-to-Capability Mapping
| Permission / key | What it unlocks | How a banker abuses it |
|---|---|---|
<all_urls> | Access to every origin | Match bank pages without naming them, and survive rebrands |
scripting | Programmatic injection | Inject overlay UI or form hooks at runtime, not just at load |
webRequest (MV2) | Blocking request inspection | Rewrite or block requests with full extension logic |
declarativeNetRequest | Declarative rule actions | Redirect main-frame navigations, strip headers, block reporting |
nativeMessaging | Talk to a host binary | Move stolen data and commands outside the sandbox |
storage | Persistent profile storage | Store tasking, targets, and exfil queues across restarts |
I would treat <all_urls> plus scripting plus an internet-facing update path as a finding on its own in any internal extension review, whatever the extension claims to do — unless there is a documented business case and the update path is pinned.
DeclarativeNetRequest Rules: Redirection Without Visible Extension Code
Manifest V3 removed blocking webRequest for regular extensions and replaced it with declarativeNetRequest (DNR). It is the change MV3 is best known for, and it is also the API a modern banker extension would prefer.
The mechanics: static rules ship in a rule_resources JSON file referenced from the manifest. Dynamic and session rules are added at runtime through chrome.declarativeNetRequest.updateDynamicRules, scoped to the extension's granted host access. The actions that matter:
redirect— send a bank URL to a lookalike origin or an attacker landing page.block— break the bank's own security or reporting endpoints so the user gets no warning.modifyHeaders— strip or weaken response headers such as framing protections on attacker-controlled pages.
Rules evaluate by priority, and dynamic rules persist in the profile across restarts. That is persistence with no service worker running and no code executing at page load — nothing to hook, nothing to breakpoint.
Where the evidence stops: the report never says KREMLIN used DNR rules, or DNR at all. What follows is the mechanism a current-generation extension would use and the artifacts an analyst would hunt for. The campaign-specific claim is inference; the API behavior is documented.
An Illustrative DNR Redirect Rule (Lab Only)
{
"id": 1001,
"priority": 1,
"action": {
"type": "redirect",
"redirect": { "url": "https://example.test/landing" }
},
"condition": {
"urlFilter": "||bank.example.invalid/login",
"resourceTypes": ["main_frame"]
}
}
The domains are placeholders on reserved TLDs; there is no real bank or attacker infrastructure here. In a live extension the rule would sit either in the packaged rule_resources file or be added at runtime. Analyst keys: action.type, redirect.url, condition.urlFilter (or regexFilter), and priority.
Inspecting Live DNR Rules on a Compromised Chrome Profile
On a profile you already suspect, open chrome://extensions with Developer mode on, find the extension, and open its service worker console. The documented API dumps live dynamic rules from there:
const rules = await chrome.declarativeNetRequest.getDynamicRules();
console.log(JSON.stringify(rules, null, 2));
Expected shape:
[
{
"id": 1001,
"priority": 1,
"action": { "type": "redirect", "redirect": { "url": "https://example.test/landing" } },
"condition": { "urlFilter": "||bank.example.invalid/login", "resourceTypes": ["main_frame"] }
}
]
Diff that against whatever rules ship in the extension package. A dynamic rule with no packaged counterpart is the finding. Where Chrome stores on-disk rule state: I have not verified the exact path across current Chrome versions, so treat any specific path you see quoted as needing confirmation on your target build rather than as fact.
Native Messaging: The Bridge Out of the Browser Sandbox
With the nativeMessaging permission, an extension calls chrome.runtime.connectNative() against a registered host application and exchanges length-prefixed JSON over stdio. Registration has two halves: a host manifest on disk and an OS-level registration entry pointing at it. On Windows that entry is a registry key under HKCU\Software\Google\Chrome\NativeMessagingHosts; on macOS and Linux it is a per-user directory or a documented config path.
Two manifest fields bind the host to the extension: allowed_origins, a list of extension IDs permitted to connect, and path, the executable. The host binary is an ordinary executable. Chrome does not sign it, does not validate it beyond its existence, and it runs with the user's full privileges outside the browser sandbox.
Here is my read: this is the step that turns a browser-scoped compromise into endpoint code execution for a banking trojan, and it is structurally weak in one specific way. allowed_origins is only as trustworthy as the extension ID it names. If the attacker's extension is on that list, the allowlist does nothing but document the attacker's own ID back to you. Which is also what makes it useful — the host manifest is a static artifact naming the malicious extension. It is one of the few points in this chain that qualifies as good evidence.
I am not asserting specific KREMLIN host names, paths, or binary names; the report does not provide them.
Native Messaging Host Manifest Fields Worth Alerting On
name— frequently mimics a legit vendor string.path— flag user-writable locations:AppData,%TEMP%,/tmp,~/.local/bin.type: stdio— expected, and cheap to check.allowed_origins— extract the extension IDs and check install time.description— social-engineering copy, often a real product name.
The detection asymmetry is that the registration entry is per-user. Machine-level inventory and golden-image comparisons routinely walk right past it.
Detection and Hunting: Three Telemetry Sources That Actually Fire
| Telemetry source | Signal | Why it matters |
|---|---|---|
| Extension install/sideload events | New extension install, chrome://extensions inventory drift, enterprise policy change | Earliest point in the chain, but noisy on unmanaged endpoints |
| Native-host registration | New NativeMessagingHosts key or host manifest; unsigned binary with chrome.exe parent | Highest fidelity, because legit hosts are rare and static |
| Network and page behavior | Extension-originated requests to low-reputation hosts, sudden redirects on banking domains, DNR rule churn | Catches live activity but requires baseline and blind spots |
Policy drift deserves its own line: ExtensionInstallAllowlist, ExtensionInstallForcelist, and ExtensionInstallBlocklist in Chrome Enterprise are the controls here, and an unexpected change to any of them is itself an alert.
If I got exactly one sensor, it would be native-host registration. It is the quietest source, the least monitored, and it marks the precise step that escalates a browser nuisance into endpoint compromise. A process-lineage rule for an unsigned executable whose parent is a Chrome process is cheap to write and has almost no benign baseline on a managed fleet.
Mitigations That Reduce Blast Radius, in Priority Order
- Monitor and restrict native messaging host registration. Without it, the extension stays in the browser and the damage ceiling drops sharply.
- Force an allowlist policy and disable sideloading/developer mode on managed endpoints. Be honest that this is inventory control, not detection — it shrinks the surface and gives you a baseline to diff.
- Stop treating "it is only an extension" as a triage downgrade. It holds credentials and DOM access on banking origins. That is not a minor asset.
- Deploy phishing-resistant, origin-bound authentication. Passkeys and FIDO2 raise the bar for credential replay — with a precise limit: they do not stop a page-level overlay from relaying a session or from manipulating what the user sees and approves. That residual risk is why endpoint-side controls still carry weight.
I also reject the framing that MV3 hardens this class of attack. MV3 changed which API the attacker uses and moved blocking logic into declarative rules. It did not change whether a browser is a viable position to hold. Arguably DNR rules are harder to observe at runtime than old blocking webRequest handlers, because there is no callback to breakpoint.
What I Did Not Test
I did not obtain a KREMLIN sample, did not run the extension, and did not confirm extension IDs, host-manifest names, C2 domains, or DNR rule contents. I did not reproduce the 1,500-system figure. From the public report I take only the malware classification, the extension component, the victim count, and the date. The API behavior — DNR actions, dynamic rule persistence, native messaging registration fields — is documented Chrome platform behavior. The claim that a campaign like this would lean on DNR redirection plus a native host is my inference, not a finding.
Further Reading
- chrome.declarativeNetRequest API reference — rule actions, priorities, dynamic rule lifecycle.
- chrome.runtime native messaging and Native messaging host manifests —
allowed_origins,path, registration paths. - Chrome Enterprise policy list —
ExtensionInstallAllowlist,ExtensionInstallForcelist,ExtensionInstallBlocklist. - MITRE ATT&CK T1176: Browser Extensions — taxonomy anchor for this technique.
- The victim count and malware classification come from the CyberSecurityNews write-up dated 2026-09-16; in the material available to me the only URL was a Google News redirect, so I am not linking the aggregator hop rather than pretending it resolves to a stable article page.


