← 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 extension enabled a CVSS 9.3 drive-by RCE, which ANY website could trigger, even if the user wasn't using the extension. It's content script forwards any web page's postMessage to its native host with no origin check, then 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. 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 with over 3 million weekly active users1, with clients including banks, government agencies, 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 it's users machines. Its own security page sells the product on "Unmatched Security" from "an ISO 27001 Certified Development Center".

The patch has been released, but it's auto updater does not seem to work. 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,
);

There's 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.

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
    // ...
}

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 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, although it doesn't check for a .dll file extension.

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, without any origin checks

Getting Your DLL onto the Disk

Path traversal lets the host load a file, but how do we exploit this?

It takes one download. The attacker serves the stage-1 DLL named reminder.pdf, typed as application/pdf - this bypasses Chrome's defences which flag downloads of .dll files. Our dll with the .pdf file extension has no problems against this however, so when the auto download puts the library into %USERPROFILE%\Downloads, and the first programmatic download per navigation lands without a click - a download is shown to the user, but the victim never approves anything.

Because we start in C:\Windows\System32, we need to traverse back to the downloads folder with a string like this: ..\..\Users\<USER>\Downloads\reminder.pdf. This does introduce one issue, which requires us to enumerate or guess the username of the user, but thankfully with the extension we can do this without many problems:

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

That oracle only reads paths the host's user can read, 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 just come back as "not found". 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 it's not possible with the traversal back from System32.

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, assuming the user approves this and is an admin.

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 very niche tool as 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. We do get the impression they primarily serve the Indian departments of these major western companies, however they are still in the supply chain

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, 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.

We would have liked to have seen some origin enforcement or a solid licence check system which would significantly reduce the attack surface moving forward, however this did not happen.

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.

The only fix we see actually working here is manually updating the installer, and while you're at it, you can restrict the sites that can communicate with the extension in the settings in Chrome.

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.

Conclusion

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.