Page MenuHome GnuPG

GnuPG does not check encrypted messages for well-formed composition
Closed, ResolvedPublic


Even if allow-multiple-messages is not used, GnuPG allows multiple messages in the following case:

  • The first message is encrypted and does not have a plaintext packet.
  • The second message is a plaintext packet.

The result is a message that looks as if it were encrypted on the command line, but the plaintext is not encrypted. This is confusing and potentially hazardous.

The status output shows NODATA, unless there is at least a single packet in the encrypted data. A private packet is sufficient to avoid the NODATA line. The only other way for applications to notice this case is that the PLAINTEXT messages come after END_DECRYPTION, but common mail clients do not check this (I tested Evolution, Thunderbird, GpgOL and Mutt, but others are probably affected as well).

It is easy to avoid this problem, for example by bumping literals_seen after END_DECRYPTION even if no literal is seen. Here is the comparable change in NeoPG:

More info is available at

How to reproduce:

# Create a private packet that is ignored by GnuPG
echo -n -e '\xfc\x03\x50\x47\x50' > 02-private.pkt

# Create an encrypted message.
gpg --no-literal --compress-level 0 -r Nerd --encrypt < 02-private.pkt > 02-encrypted.pkt 2> /dev/null

# Create a literal data packet.
echo 'This is fine!' | gpg --store --compress-level 0 --faked-system-time 0 > 02-literal.pkt 2> /dev/null

# Store the plaintext after the encrypted packet.
cat 02-encrypted.pkt 02-literal.pkt > 02-message.gpg

cat 02-message.gpg | gpg --enarmor | sed -e "s/ARMORED FILE/MESSAGE/" | sed -e '/^Comment\:/d' > 02-pgp-inline.gpg


$ cat 02-pgp-inline.gpg | gpg
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with 2048-bit RSA key, ID 66489556790B2E8E, created 2018-03-25
This is fine!

Status FD output:

GNUPG:] ENC_TO 66489556790B2E8E 1 0
[GNUPG:] DECRYPTION_KEY 9669A61C2F57DEC457976E7B66489556790B2E8E 013072FB93A232E7C9B1DB3F7EBDF89573BAFB58 u
This is fine!

Message composition:

$ cat msg | gpg --list-packet
gpg: encrypted with 2048-bit RSA key, ID 66489556790B2E8E, created 2018-03-25
# off=0 ctb=85 tag=1 hlen=3 plen=268
:pubkey enc packet: version 3, algo 1, keyid 66489556790B2E8E
	data: [2048 bits]
# off=271 ctb=d2 tag=18 hlen=2 plen=33 new-ctb
:encrypted data packet:
	length: 33
	mdc_method: 2
# off=306 ctb=cb tag=11 hlen=2 plen=20 new-ctb
:literal data packet:
	mode b (62), created 0, name="",
	raw data: 14 bytes

Event Timeline

I would also recommend that GPGME does a sanity check on the status fd output for people with new GPGME but old GnuPG binary.

This looks similar to the "multiple plaintext" issue that we had in Feb. / March 2007.

Can you help me understand what the impact of this is? AFAIK Back in 2007 the problem was that it could be faked that data looked like it was signed.

Here I'm not sure that I see the implications. If I wanted to "fake encrypt" something, what is the advantage of your technique over encrypting it? As I see it even for your technique you would need the pubkey of the recipient.

I agree that it's probably a bug in that it is unintended behavior, but I try to understand what the priority is and if I should e.g. add safeguards in Kleo.

Please stop using Outlook 2007 for testing ;-) Ok that would not have made a difference here but I removed the code for Outlook 2007 already in master.

The impact is low to our current understanding, that's why I didn't report it as a security vulnerability. I tried to use this for signatures, but GnuPG has more verification for signatures, so it doesn't work there as far as I can see. So that's good.
If you allow for a BADMDC, you can easily downgrade the content of an encrypted data packet from, for example, compressed to private packet type, and then you don't even need the public key, just an encrypted message. The MDC will notice this, and since Efail the clients should have strict MDC checking, so I didn't include that variation in my report.
By the way, there are other clients I didn't test which are probably affected, such as kmail, claws, gpgtools.
I only have Outlook 2007 and no funds to buy software I don't use, as I am unemployed and using up my savings. So, next time I won't be able to do the testing, sorry!

patrick changed the visibility from "Public (No Login Required)" to "Subscribers".May 30 2018, 5:58 PM

I have changed visibility of the bug, as I think you can do a lot more with this than Marcus imagined.

Correct me if I'm wrong, but I believe that if you manage to place unencrypted literal packets before and after an encrypted message, you can create a variant of the Efail attack. Even more, it would look like a single stream from GnuPG:

  • The literal packet(s) before the encrypted message contain a PGP/MIME message structure that will embed the encrypted message similarly to the Efail scenario (i.e. a 1st HTML part, for example with an open img src=".." tag
  • Then the encrypted message
  • Then another literal packet with some closing stuff to make the MIME parser happy
werner changed the visibility from "Subscribers" to "Public (No Login Required)".May 30 2018, 8:34 PM
werner added a project: gpgme.

[We do things in the public unless explicitly requested by a bug reporter writing to security.]

That whole thing is old and a common thing when decrypting mails. For example many MUAs decrypt any inline PGP parts they see (common annoyance then are samples in mail to a public ML). So nothing new and no need to hide it.

Th actual problem here is that GPGME does not behave correctly. This needs to be fixed.

BTW: The misconception is to trust non-signed data. It can origin from anyone. This is why MUAs explicitly mark the signed part of a message or hide everything else.

I need to revise my statement (partly because fixing gpgme would be quite complicated). Marcus is right in that using the the literals_seen counter is the straightforward way to get this right. And it will fix it also for non-GPGME applications.

werner claimed this task.

Yeah, that's not good enough. You also need to check if literals_seen is 0 before BEGIN_DECRYPTION to catch the case where the plaintext packet comes before the encrypted packet. See

werner changed the task status from Open to Testing.Jun 6 2018, 3:59 PM