← Back to blog8 out of 10 Banks HATE This One Weird 3SKey RCE
/James Arnott
Share

8 out of 10 Banks HATE This One Weird 3SKey RCE

TL;DR. SConnect - 1M+ users, an extension middleware+native host for authentication with eIDs, 3SKeys and other hardware signing tokens had a drive-by RCE which enabled any site or iframe a user saw to silently download and execute a dll due to a poor hand-rolled implementation of RSA-2048 token validation, enabling a use of uninitialized memory validation bypass which enabled "plugins" (DLLs) to be loaded. v2.16.0.0 of the extension and native host is vulnerable. CVE-2026-18397. CVSS 9.4.

Intro

We're no stranger to extension + native host vulnerabilities, and this is another example of why they're so dangerous. This however, unlike the connective signing extension vulnerabilities, is not a story of incompetence, it's rather highlighting how the any site <-> extension <-> native host architecture is incredibly risky and often implemented in a way which does not securely handle input.

The title of this blog post is a continuation from our "8 out of 10 Banks in Belgium HATE this one weird eID RCE" which we will be referencing in this blog post. The blast radius here could potentially be wider, however public information about SConnect usage is limited - we don't actually know how many banks were using this but there will be many.

SConnect was initially made by Gemalto, which was acquired by Thales, the French conglomerate.

Who was using this?

When our pipeline highlighted SConnect's risky architecture, we didn't know who used it, we just thought it was worth looking into due to it's 1M+ users. It turned out to be the one of the main methods to authenticate with the SWIFT banking system, amongst other "high security" implementations. SWIFT is moving away from SConnect towards WebConnect, however users are very likely to still have SConnect installed.

SWIFT's critical update notice for SConnect

They published this notice

It's also explicitly used for:

And there's loads of other interfaces that use 3SKeys for signing or authentication. We don't own any 3SKeys or Qatari eIDs so this had similar struggles to the Connective Signing Extension research. Due to the friction here, we only proved the RCE and our bypass we found likely enabled many other fun activities with the SConnect extension.

As a side note, there's many other digital signing extensions with similar vulnerabilities, we've got too many candidates to report (as we don't send automated disclosures) so if you're interested in researching these and have some time on your hands, feel free to reach out. We've also found many digital signing tools which enable any site a user visits to forge signatures and can't report them due to the overwhelming number of vulnerabilities we have on hand.

How SConnect worked

SConnect worked by letting partnered sites communicate with the connected security hardware. These partnered sites had an RSA-2048 signed token, which crazily enough, was locked down to the host, unlike before.

The flow went as follows:

  • Websites post a message to the extension content script, which gets forwarded to the background service worker, with the origin.
  • The service worker forwards this message over to the native host with the origin of the request.
  • The native host sends over a GET request to the origin, requesting the token signed by Thales to verify if the site is approved to use SConnect - this persists for the session. Failures are silent and repeatable.
  • The site can then request to "install" and "addon" which is a DLL library to help communicate with the hardware security token/card
  • The site can then issue APDU commands to communicate with the hardware

Here's a simplified communication flow for how this works: Simplified SConnect communication flow

Bypassing the token validation

After we mapped out how SConnect works, it all looked sound at first glance, they checked the origins against a key we couldn't forge so we decided to look for CVEs in the libraries processing data from external untrusted sources. We looked into which version of OpenSSL or other cryptographic library they were using to validate the RSA-2048 tokens, however there was no such library, they handrolled it. Given how easy it is to get cryptography implementations wrong, we decided to look closer into this system, which is how we found the bypass.

There were 2 key key mechanisms of SConnect which enabled this bypass:

  1. The verifier reads a buffer that isn't always filled with the expected input. SConnect is meant to compute sig^e mod n and write this to the buffer which is then checked by the verifier, however if you give it a larger number, in our case 257 bytes of 0xFF then it never writes to this buffer.

  2. This buffer is never cleared upon initialisation, it's a simple malloc(256), so when the sig^e mod n computation fails and it fails to write to the buffer, only the reserved but not cleared memory is in that buffer, so if an attacker can write to this buffer, this verification step clears.

So from here we just need to spray the heap with these "magic strings" to force the buffer the verifier reads into the state we need. The native host architecture makes this easy as the extension in this case opens a single long-lived connectNative port to the host for the page and reuses it for every message, so the host process stays alive and its heap state carries over from one message to the next, which is what lets a spray accumulate and eventually land in the verifier's buffer. This whole exchange is silent and the user is never notified so each failed attempt just comes back as a quiet error, so we can keep spraying and retrying until one lands.

Here's a snippet to illustrate how this worked:

bool verify_pkcs1(const uint8_t *data, size_t data_len,
                  const uint8_t *sig, const char *modulus_hex)
{
    uint8_t expected[20];
    sha1(data, data_len, expected);            // hash of the message we're verifying

    uint8_t *block = malloc(k);                // (A) 256-byte buffer - not zeroed
    int rc = modexp(block, sig, modulus_hex);  // compute sig^e mod n, writing the result into block
    // (B) rc is ignored. modexp returns 0x401 and writes nothing when sig >= modulus.

    // Every check below reads `block`, whether or not modexp actually filled it:
    if (block[0] != 0x00 || block[1] != 0x01)         // 1. leading  00 01
        return false;
    for (int i = 2; i < k - 0x24; i++)                // 2. 0xFF padding   (block[2..219])
        if (block[i] != 0xFF)
            return false;
    if (block[k - 0x24] != 0x00)                       // 3. 00 separator   (block[220])
        return false;
    // (C) block[221..235] - the ASN.1 DigestInfo - is never checked
    if (memcmp(&block[k - 0x14], expected, 20) != 0)   // 4. trailing 20 bytes == SHA1(data)
        return false;

    return true;                                       // "signature valid"
}
  • (A) the recovered-block buffer is malloc'd and never zeroed (the un-wiped whiteboard).
  • (B) modexp (FUN_00403830) returns 0x401 and writes nothing when sig >= modulus; the caller ignores the return and reads the buffer anyway (nobody checks the write worked).
  • (C) the DigestInfo (hash-algorithm identifier) is never checked, only 00 01, the FF padding, the 00 separator, and the trailing 20-byte SHA-1 are.

So from here, all we needed to do is to work out an optimal method to heap-spray SConnect.exe to set that buffer to the correct value. We started off getting our agents to use Frida to inspect the memory at runtime to return the correct string which gave us the bypass every time, however this exploit wouldn't be much use if it required the client to have Frida installed and reporting back the best string to bypass the authentication with.

We were eventually able to iterate using the agents to get a ~18% success rate on the heap spray without Frida.

Loading the DLL

With the token check forged, we're past the gate that's meant to filter out untrusted sites, so now our origin is "authorized" and any command we send is treated as coming from a legitimate partner site.

This would theoretically enable attackers to use SConnect to issue these APDU commands to the connected security key/eID, however we didn't test this as we don't have eIDs or 3SKeys to hand. We could however demonstrate downloading and loading the DLLs - these used the same function to validate the DLL signatures.

Loading a DLL through SConnect

Getting our DLL loaded took two more forged signatures, each utilising the same uninitialised-buffer bug. First, InstallAddOns checks the package manifest's signature which is an RSA signature over the hash of the inner addon.zip. We set it to 257 bytes of 0xFF and sprayed the matching block; the host declared it Thales-signed and unzipped our package to disk.

After, we can run CreateAddOnInstance which checks the inner signature.json, which actually does re-hash every file in the package and compare, but it verifies that list of hashes with the same broken RSA routine. So we just listed the real SHA-1 of our own DLL (the file hashes are honest) and forged only the signature over that list. When the second spray lands, the "valid" signature, and the host calls LoadLibrary on our unsigned addon.dll and invokes its onCreate export and our exploit dll code runs inside the Authenticode-signed Thales host process!

In our example to prove the RCE we popped a message box and opened a video in Edge.

This whole process took ~6-10s and the exploit was invisible to the user. As SConnect's content script was injected into all_frames this meant any iframe which was loaded onto a page could have also executed this full RCE flow.

Timeline

This was initially reported to Thales PSIRT on the 29th of June, 2026.

As follows:

  • Reported on the 29th of June
  • Thales confirmed vuln on 3rd of July & CVE reserved
  • Sconnect on the Apple App store is patched on the 7th of August
  • SConnect on the Chrome Web store is patched on the 7th of August
  • Sconnect on Edge gets removed on the 13th of September
  • CVE-2026-18397 published on the 1st of October

At the time of removal, SConnect had 89K users on Edge and we cannot confirm these edge users would have had this automatically uninstalled. We only explicitly tested the Chrome Web Store version however the others are very likely to have included the same vulnerabilities.

Thales pushed an update which referenced a new native host, so all automatically updated extensions no longer were able to communicate with the old native host. It also appears they took extra steps to harden security, beyond what was required to fix this vulnerability.

Conclusion

SConnect wasn't terribly designed, but it had a risky architecture which widened the attack surface exponentially, which in the age of attackers using AI agents to find exploits like this, makes exploits like this cheaper and easier to find & exploit. This was one of the most complicated exploits we've found, however the actual attack complexity was still low. The extension attack surface cannot be overlooked.

It is also used in some of the highest security environments given its sole purpose is to interact with hardware security tokens, namely the 3SKey from SWIFT.

And finally, keep an eye out for our upcoming blog posts where we show how every major signing extension in Brazil/South America is vulnerable to similar drive-by RCEs affecting ~10M endpoints in very sensitive areas.


Here's the full flow:

Security reports for extensions in this post