Featured image of post When Giants Use Toothpicks

When Giants Use Toothpicks

How Two Rounds of MD5 Were Used to Protect 4096-bit RSA Keys in an Enterprise Recovery System.

Fireblocks is a major digital asset custody platform, trusted by institutions to secure trillions of dollars. Its security model is built on Multi-Party Computation (MPC), a cryptographic technique that avoids the risk of a single point of failure by splitting a private key into multiple shares. This advanced approach makes it all the more surprising that during a security review for a client, we discovered a significant weakness in a critical component: their open-source Recovery Tool. A robust, 4096-bit RSA private key, a foundational piece of this recovery process, was being protected by a key derivation function (KDF) that would be more at home in the 1990s than in a modern, high-stakes financial system.

The Discovery

While evaluating the Fireblocks Recovery Tool, our security team reviewed the method used for encrypting the disaster recovery RSA private key. The tool generated a strong keypair, but then used a “legacy mode” to encrypt the private key with a user’s passphrase:


const privateKeyPem = pki.encryptRsaPrivateKey(privateKey, passphrase, {
  legacy: true, // 🚨 This flag enables an insecure, outdated method
  algorithm: 'aes128',
  prfAlgorithm: 'sha256',
});

Source: apps/recovery-utility/renderer/pages/setup.tsx

The legacy: true option leads to a function within the forge library called pbe.opensslDeriveBytes:

// encrypt private key using OpenSSL legacy key derivation
var dk = forge.pbe.opensslDeriveBytes(password, iv.substr(0, 8), dkLen);

Source: forge/lib/pbe.js

This function’s job is to turn a human-memorable password into a strong cryptographic key. A secure KDF makes this process slow and computationally expensive to frustrate brute-force attacks. This legacy function, however, does the opposite:

pki.pbe.opensslDeriveBytes = function(password, salt, dkLen, md) {
  if (typeof md === 'undefined' || md === null) {
    md = forge.md.md5.create();
  }
  if (salt === null) {
    salt = '';
  }
  var digests = [hash(md, password + salt)]; // First MD5 hash
  for (var length = 16, i = 1; length < dkLen; ++i, length += 16) {
    // Second MD5 hash uses the result of the first
    digests.push(hash(md, digests[i - 1] + password + salt));
  }
  return digests.join('').substr(0, dkLen);
};

Source: forge/lib/pbe.js

In essence, this code takes a user’s password, combines it with a salt, and runs it through the MD5 hashing algorithm just twice to generate the final encryption key. MD5, cryptographically broken since 2004, is extremely fast to compute. This speed, combined with a mere two iterations, makes a password-protected key vulnerable to offline brute-force attacks, where an attacker can guess billions of passwords per second with modern hardware.

Understanding the Risk

To be fair, exploiting this vulnerability is not a simple one-step process. Fireblocks’ architecture relies on defense-in-depth, which prevented the weak KDF from becoming a direct, critical threat. Here’s a simplified breakdown of how it works and where the vulnerability fits in:

Imagine a digital safe that requires two separate keys to open and sign a transaction:

  • The Client Key: You, the user, hold this key on your device.
  • The Fireblocks Key: Fireblocks holds this key in their secure environment.

These two shares work together through multi-party computation (MPC) to authorize transactions without ever creating a complete private key that could be stolen.

The Recovery Tool is designed for a disaster scenario, for example, if Fireblocks were to go offline permanently. It allows you to create a backup of The Fireblocks Key. To do this, you use the tool to generate your own “backup safe”, a 4096-bit RSA key pair.

  • The RSA public key (the open slot of your backup safe) is given to Fireblocks. They use it to encrypt their key share and create a Recovery Packet.
  • The RSA private key (the key to your backup safe) is kept by you. To protect it, you encrypt it with a passphrase.

This is where the vulnerability lay: the lock on your RSA private key was incredibly weak.

An attacker would need to execute a complex, multi-stage heist to exploit this:

  1. Obtain the Recovery Packet: Steal the encrypted Fireblocks Key from the hard-to-guess URL.
  2. Obtain the Encrypted RSA Private Key: Steal the file containing your RSA private key, which is stored separately by you.
  3. Brute-Force the Passphrase: With both pieces, an attacker can now exploit the weak MD5-based KDF to crack your passphrase and decrypt the RSA key. This is the step that was made dangerously feasible by the vulnerability.
  4. Obtain the Client Key: In a completely separate attack, compromise your device to steal your primary key share.

While this layered security is commendable, the weak KDF dramatically lowered the bar for a critical step in the attack chain. In the high-stakes world of digital assets, “difficult” should not be confused with “secure.”

Responsible Disclosure

We believe in coordinated disclosure to protect users. Our process with Fireblocks unfolded as follows:

  • March 25, 2025: Initial vulnerability report sent to multiple Fireblocks security contacts.
  • April 11, 2025: Follow-up reminder sent after receiving no response.
  • May 26, 2025: Final reminder sent with a reminder on the public disclosure deadline.
  • May 26, 2025: Fireblocks acknowledged the report and requested time to investigate.
  • June 15, 2025: Fireblocks confirmed the fix was live in production.

To their credit, once engaged, Fireblocks fixed the issue by disabling the legacy mode for their key encryption here. This replaces the outdated KDF with a modern, standardized key derivation function designed to resist brute-force attacks.

Guidance for Affected Customers

Despite implementing a crucial cryptographic fix, Fireblocks has, as of this writing, remained silent on the issue. To our knowledge no security advisory has been issued, no blog post has been published, and no direct notification has been sent to customers who used the vulnerable versions of the Recovery Tool. This silence creates a dangerous knowledge gap. All private keys encrypted with the Recovery Tool before the patch (version v1.7.0) are still protected by the weak, two-round MD5 KDF. They remain vulnerable.

The vulnerability is not remediated for existing keys. Therefore, any organization that generated a recovery package before the v1.7.0 update must review their security posture. Your course of action depends almost entirely on the strength of the passphrase you originally used to encrypt the RSA private key.

The Gold Standard: Rotate Your Key

The only way to completely eliminate this specific risk is to generate a new RSA key pair using the updated Recovery Tool (v1.7.0 or later) and use it to create a new recovery package. We recognize this is operationally complex and potentially disruptive.

Pragmatic Mitigation: Enforce Extreme Segregation

If key rotation is not immediately feasible, you must treat the two components of your recovery kit as existentially critical secrets. An attacker needs both the encrypted RSA private key file and the Fireblocks recovery packet to attempt a brute-force attack.

  • Ensure these two files are stored in completely separate, air-gapped, and geographically distinct locations.
  • Re-evaluate the strength of the passphrase used. If it was not exceptionally strong (e.g., generated by a password manager with 100+ bits of entropy), the risk of compromise is significantly higher.

Conclusion

Fireblocks deserves credit for acting swiftly to patch this vulnerability once our report was acknowledged. The move away from a legacy KDF was the correct and responsible technical decision.

However, security responsibility extends beyond a code commit. A technical fix without transparent communication is an incomplete solution. The core lesson of this discovery is twofold. First, it demonstrates how even in a sophisticated security architecture, a single line of code can make it such that a Giant is only held up by a toothpick. Second, it highlights that true partnership in security requires transparency. By not informing customers of the flaw and the residual risk to their existing keys, the burden has been shifted from the provider to the unknowing user.