Five vulnerabilities in CipherMail Gateway: EFAIL revisited, a bounce oracle, and trust-tag spoofing

/ Martijn Brinkers

EFAIL

We recently received a detailed report from the Applied Cryptography Research Group at ETH Zurich describing several ways the CipherMail Gateway could be made to leak information about, or misrepresent the trust status of, the encrypted email it processes. While investigating their findings we identified a couple of closely related issues of our own. In total this resulted in five vulnerabilities, all of which are fixed in CipherMail Gateway 6.3.3.

None of these are remote code execution or key-recovery bugs — the attacker never gets your private key. Instead they belong to a family of attacks that has haunted email encryption for years: the attacker abuses the gateway's behaviour around decryption to either exfiltrate the plaintext indirectly, or to make an unprotected message look protected.

It is also worth noting that most of these attacks require the attacker to already possess an encrypted message addressed to the victim — for example one intercepted in transit, or recovered from a mailbox or archive — which they then manipulate and resend to the gateway.

This post explains each vulnerability, gives some real-world numbers for the most interesting one (a decryption oracle built entirely out of bounce messages), and ends with concrete steps you should take.

Note. CVE identifiers have been requested for all five issues and are pending assignment. This post will be updated with the CVE numbers once they have been assigned.

A quick EFAIL refresher

A quick recap, for anyone who wasn’t reading vulnerability blogs in 2018: the EFAIL attacks paper showed that S/MIME and OpenPGP messages can be turned into exfiltration vehicles — not by breaking the cryptography, but by reshaping the decrypted message so it tricks the recipient’s mail client (or a downstream URL-scanning security product) into making an outbound HTTP request that carries the decrypted plaintext along.

This is the part of EFAIL that often gets misunderstood, so it’s worth stating clearly:

EFAIL is not exploitable by the gateway on its own. Decrypting a message is not in itself a disclosure. The decrypted message only becomes an exfiltration channel when an external component acts on it — typically an HTML-rendering mail client that auto-loads remote images, or a security product that follows or rewrites URLs (Microsoft Defender for Office 365 Safe Links, Proofpoint URL Defense, and similar).

So the gateway sits in the middle. It can’t fix recipients' mail clients, and it can’t stop downstream URL scanners from doing what they do. But it can refuse to deliver a decrypted message that has the structural signs of having been reshaped by an EFAIL aacker. That is what the EFAIL filter shipped in 6.3.3 does.

If you have been with CipherMail for a while, you may remember an earlier EFAIL detection we added back in 2018, controlled by the Check for invalid 7bit chars and Abort decrypt on invalid 7bit chars options. Those options were opt-in — they were off by default — and they only covered the byte-level "non-readable characters in the decrypted output" signal. The new EFAIL filter supersedes them. It has stronger detection, an additional sanitization tier, and it is enabled by default. The two old properties have been removed

Three of the five issues below are EFAIL variants. The other two are about trust: making an unprotected message appear signed or validated.

S/MIME CBC EFAIL

S/MIME messages encrypted with a CBC-mode cipher (AES-CBC, 3DES-CBC) carry no cryptographic integrity check. An attacker who obtains an encrypted S/MIME message (intercepted in transit, pulled from a mailbox or an archive) can tamper with the ciphertext so that, after the gateway decrypts it, the decrypted message has been structurally reshaped — the victim's real plaintext ends up inside an attacker-defined HTML context. The gateway previously emitted the decrypted message as-is, with no defence against this manipulation.

The fix. CipherMail 6.3.3 introduces a two-tier EFAILFilter that runs on every decrypted S/MIME message:

Detection combines a structural 7-bit check (non-7-bit bytes where CBC-gadget garbage lands), a "first-block density" check (a body part whose first bytes are dominated by random-looking non-7-bit bytes), a MIME nesting-depth limit, and a missing-Content-Type check. When sanitisation runs, the gateway records which checks fired in an audit header:

X-CipherMail-EFAIL-Filter: sanitized; checks="structural-7bit,first-block-density"

The legacy, opt-in smime-check-invalid-7bit-chars / smime-abort-decryption-on-invalid-7bit-chars properties — which were off by default, so out-of-the-box installations had no EFAIL defence at all — have been removed and superseded by smime-efail-filter-enabled, which is enabled by default.

OpenPGP EFAIL through spliced packet streams

A legitimate encrypted OpenPGP message is exactly one message: a single literal data packet, reached through an optional encryption layer and an optional compression layer. But OpenPGP packet streams can be concatenated, and that is the opening. An attacker can build a stream that decrypts to more than one message, in two relevant shapes:

  1. Multiple literal data packets — several OpenPGP messages glued together, so decryption yields the attacker's plaintext directly concatenated with the victim's.
  2. A cleartext packet alongside an encrypted packet in the same layer. The cleartext packet is attacker-controlled and is not encrypted, so it carries no MDC. Modification Detection Code enforcement therefore cannot catch it — the integrity check only covers the genuine encrypted packet — yet its output is still concatenated with the decrypted plaintext.

In both shapes the attacker ends up with a prefix or wrapper of their choosing fused to the victim's plaintext: an EFAIL exfiltration vehicle. Importantly, MDC does not save you against shape 2.

The fix. After decryption, the gateway now walks the OpenPGP layer tree and refuses to emit a spliced result. A legitimate layer holds exactly one content-producing packet (signatures are not counted, so ordinary signed-and-encrypted mail is unaffected). On a spliced shape the original ciphertext is delivered unchanged instead of the decrypted output, and the refusal is logged as the pgp-spliced-packets check. The shared EFAILFilter (HTML/URL sanitisation) is applied to decrypted PGP mail as well.

A decryption oracle built from bounce messages

This is the most interesting of the five, because it does not require the victim's mail client to do anything at all.

When the gateway decrypts an incoming message and that message later bounces, the bounce is addressed to the envelope sender (MAIL FROM) — which, for attacker-supplied mail, is the attacker. Two distinct leaks follow:

  1. Plaintext disclosure. A bounce traditionally quotes the failed message. After decryption, that quoted content is the decrypted plaintext. A single bounce to an attacker-controlled sender can hand over the plaintext directly.
  2. A decryption / padding oracle. While processing an encrypted message the gateway adds headers such as X-CipherMail-Info-SMIME-Encrypted: True. Whether (and how) these appear in a bounce tells the attacker whether a chosen-ciphertext message decrypted successfully. That single bit of feedback is exactly what a CBC padding-oracle attack needs.

What makes this hard to contain is that the bounce does not have to come from the gateway. A next-hop mail server — the internal mail store, a downstream relay, a mailbox provider — can independently bounce the message on any delivery failure (unknown user, mailbox full, quota, policy rejection), and those bounces are outside the gateway's control. Limiting bounce size on the gateway's own Postfix instance therefore helps, but cannot be the whole fix.

This is not a CipherMail-specific problem. The bouncing of decrypted content is inherent to how email encryption gateways work: any gateway that decrypts inbound mail and then hands the plaintext onward sits in front of mail servers that can generate non-delivery reports back to the envelope sender. Unless the gateway takes explicit steps to control where those bounces go, the same plaintext-disclosure and decryption-oracle exposure applies to email encryption gateways in general — not just to CipherMail. We describe how we address it below, but operators of any encryption gateway should consider the same class of mitigation.

Real-world numbers: how many bounces to decrypt a 1 MB message?

A CBC padding oracle recovers plaintext one byte at a time. For each byte the attacker brute-forces the value that produces valid padding, which takes on average about 128 oracle queries per byte (2⁸ ⁄ 2), and at most 256. Here, each "oracle query" is one crafted message sent to the gateway that produces one bounce the attacker can observe.

So the cost to recover plaintext scales linearly with its size:

Target Plaintext bytes Bounce messages (average)
One AES block 16 ~2,000
A short secret (e.g. a 64-byte line / password) 64 ~8,000
A full 1 MB message 1,048,576 ~134,000,000

For a full 1 MB message that is roughly 134 million bounce messages on average (up to ~268 million worst case). Translated into wall-clock time, assuming the attacker can sustain a given injection rate:

Injection rate Time to recover 1 MB
10 messages/second ~155 days
100 messages/second ~15.5 days
1,000 messages/second ~1.6 days


That sounds like a lot — and it is — but the attack is non-interactive and time-flexible. Spread over several months at, say, 50 bounces per second sustained, it finishes in about a month. The gateway operator would, of course, see an enormous volume of bounces, so the attack is loud. But "loud" is not the same as "infeasible", and against a target worth months of attacker effort it is well within reach.

The fix. CipherMail 6.3.3 closes the channel rather than trying to shrink it:

The envelope-sender rewriting is the primary control, because it is the only one that also neutralises bounces produced by next-hop servers.

PGP/INLINE mixed-content spoof via duplicate detached signatures

This one is about trust, not confidentiality. A PGP/INLINE message that combines a clear-signed body with multiple detached-signature attachments stacked over the same target file could be delivered with the Signed-Valid status set but the X-CipherMail-Info-PGP-Mixed-Content marker — and its subject tag — omitted, even though the message also carried sibling unsigned, attacker-controlled parts.

The cause was a non-idempotent integer counter: the mixed-content decision incremented for each unsigned handler and decremented for each detached signature whose target filename matched a sibling part. Two detached signatures resolving to the same visible file decremented the counter twice for one part's worth of content, allowing an attacker to cancel out the contribution of a separate unsigned part such as evil.txt. The attack requires the attacker's key to be in the gateway's trust store (i.e. they are a known correspondent) so that their signatures verify — but no private-key compromise.

The danger is that a recipient, or a downstream rule/archive/DLP pipeline, that treats "signed-valid and not mixed-content" as "the whole message is signed by a trusted sender" would be misled into trusting attacker-controlled content.

The fix. The integer counter has been replaced with set-based coverage tracking: a Set of the distinct MIME parts actually covered by a valid signature, and a separate set of the signature attachments themselves. Adding the same part twice is now a no-op, so stacked detached signatures can no longer cancel anything out. The mixed-content decision becomes "are there any visible parts left over that no valid signature covers?"

Subject-filter bypass via nesting or Unicode homoglyphs

The FilterSubject mailet lets administrators strip operator-defined markers (typically gateway-style security tags such as [signed], [signed and valid], [decrypted]) from incoming subject lines, so that only the gateway's own tags — added later, after real cryptographic validation — remain. It could be evaded two ways:

  1. Nesting. A subject like [sign[signed]ed] was filtered in a single regex pass: the inner [signed] was removed, but the surrounding [sign + ed] re-formed a fresh [signed] that the filter never revisited.
  2. Unicode homoglyphs. A subject containing a Cyrillic е (U+0435) instead of Latin e[signеd] — is visually identical but byte-different, so it bypassed the regex entirely.

The consequence is a subject-line spoof: an external attacker could deliver a message whose subject displays a gateway-style trust marker ([signed and valid] Re: Wire transfer) even though the gateway never added it, deceiving recipients and any downstream rule that keys off the subject string. There is no plaintext disclosure here; the impact is on the integrity of the displayed trust status, which is why this is rated Medium.

The fix. FilterSubject now applies two complementary passes:

As always: subject tags are cosmetic. The authoritative signal that a message was really signed or encrypted is the set of X-CipherMail-Info-* headers, and any automated decision should be based on those, not on the subject string.

What you should do

1. Upgrade to CipherMail Gateway 6.3.3 or later. All five issues are fixed there, and the EFAIL filters and splice/coverage checks are enabled by default — no opt-in required.

2. Stop bounces of encrypted mail from reaching external senders. This is the single most important configuration step, and it is what closes the bounce oracle described above. Rewrite the envelope sender of an encrypted message to a controlled internal address.

Enable it per recipient (at the global, domain or user level) by setting.

From the CLI

Use the following properties:


From the UI

See the Rewrite Sender If Encrypted and Sender If Encrypted settings from the General settings tab.

With these in place, when a message that arrived encrypted later fails to deliver — whether the failure happens on the gateway or on a next-hop server beyond it — the bounce goes to your monitored internal mailbox instead of back to the original (possibly attacker-controlled) sender. The pre-rewrite sender is preserved in X-CipherMail-Original-Sender so you can still see who the message claimed to be from. Point sender-if-encrypted at a mailbox somebody actually watches, because that is now where delivery failures for encrypted mail will land.

3. Leave the EFAIL filters enabled. smime-efail-filter-enabled and pgp-efail-filter-enabled are on by default. Only disable a filter for a recipient who you are certain is never exposed to an HTML-rendering client or a URL-following security scanner. If you previously relied on the old smime-check-invalid-7bit-chars / smime-abort-decryption-on-invalid-7bit-chars properties, remove them from your configuration — they have been replaced.

4. Use the subject filter to scrub spoofed trust tags. For recipients where you care about this, set subject-filter-enabledtrue and configure subject-filter-regex to match the gateway-style tags you want removed. The same patterns that previously matched [signed] now also catch [sign[signed]ed] and the homoglyph/invisible-character variants automatically — no change to your existing regex is needed.

5. Do not trust subject tags for automated decisions. Anywhere you have a mail-client rule, archive policy or DLP/SOC pipeline that keys on subject-line trust markers, switch it to consume the X-CipherMail-Info-* headers instead. Subject tags are best-effort cosmetics; the X-CipherMail-Info-* headers are the authoritative record of what the gateway actually verified. Where a downstream rule consumes X-CipherMail-Info-PGP-Signed-Valid, make sure it also requires the absence of X-CipherMail-Info-PGP-Mixed-Content before treating all parts of a PGP/INLINE message as covered.

6. Prefer authenticated encryption where you can. For newly sent S/MIME mail, prefer AuthEnvelopedData (AES-GCM) over CBC modes — authenticated encryption rules out the ciphertext tampering at the heart of the S/MIME CBC EFAIL issue. Note this protects mail going forward only; already-existing CBC ciphertext stays malleable, which is why the receiving-side EFAIL filter remains necessary. For OpenPGP, keep MDC checking enabled — remembering that MDC alone does not stop the cleartext-splice shape, which is why the structural splice detection described above is also required.

Customers with a support contract who would like help upgrading or applying this configuration are welcome to contact us.

Note on CipherMail Gateway version 5. These fixes are not applied to version 5 of the CipherMail Gateway. Customers with a support contract who, for whatever reason, cannot upgrade are welcome to contact us to discuss further options.

Credits

We would like to thank Andris Suter-Dörig, Matteo Scarlata, and Prof. Kenny Paterson from the Applied Cryptography Research Group at ETH Zurich for reporting these issues to us responsibly and for the detailed, constructive disclosure that made the fixes straightforward to design.

References