This article will describe PGP Certificate Flooding attacks as well as inform the reader

How to detect if you have a poisoned certificate in your keyring, How to identify & clean the poisoned cert, and How to update the configuration to prevent it from importing poisoned certs in the future

Last month, an attacker spammed several high-profile PGP certificates with tens of thousands (or hundreds of thousands) of signatures (CVE-2019-13050) and uploaded these signatures to the SKS keyservers.

Without looking very deep, I quickly stumbled on 4 keys that were attacked last month:

Date(s) Total Signatures File Size (ASC) Key UID Key Fingerprint 2019-06-17, 06-18 54,600 22 MB Daniel Kahn Gillmor 0xC4BC2DDB38CCE96485EBE9C2F20691179038E5C6 2019-06-19 149,100 60 MB Robert J. Hansen 0xCC11BE7CBBED77B120F37B011DCBDC01B44427C7 2019-06-26, 06-27 84,366 34 MB Micah Lee 0x927F419D7EC82C2F149C1BD1403C2657CD994F73 2019-06-30 121,000 25 MB Tor Browser Developers (signing key) 0xEF6E286DDA85EA2A4BA7DE684E2C6E8793298290

The Problem

If your GnuPG client or thunderbird's enigmail is configured to automatically ` --refresh-keys `, then you could also automatically break your GnuPG install if it downloads one of these poisoned certificates into your keyring.

Note that this problem is further exacerbated by the fact that keyservers by design do not delete keys. Though this introduces obvious vulnerabilities, it was a decision made to make the network resilient to government tampering & censorship.

Personally, I noticed this issue when my email stopped working. Debugging thunderbird & enigmail pointed me to issues with PeP, but--as I was debugging--I noticed that ` gpg ` was hanging on ` removing stale lockfile `. This was because thunderbird was already running, and it was hanging on the poisoned certificate.

user@host:~$ gpg -vvvv --list-keys gpg: using character set 'utf-8' gpg: using pgp trust model ... gpg: checking the trustdb gpg: removing stale lockfile (created by 7055)

The Solution

This section will describe how to fix your GnuPG keyring and configuration to clean out poisoned public keys and prevent it from automatically importing them in the future.

1. Identifying the poisoned key

First, we can list the size the public keys in our keyring (in bytes) using the following command (as reported on the GnuPG issue tracker):

user@disp1754:~$ gpg --export | gpg --list-packets | awk -F= -v oldoff=-1 -v keyid=unset ' /^# off=/{ off = $2 + 0 } /^:public key/{ if (oldoff>-1) { print (off - oldoff) " " keyid }; oldoff = off; keyid = "unset"; } /keyid:/ {if (keyid == "unset") { keyid = $1; } } END { print (off - oldoff) " " keyid ; };' | sort -n 7284 keyid: 1DCBDC01B44427C7 119748 keyid: 4E2C6E8793298290 124557 keyid: 403C2657CD994F73 16934647 keyid: F20691179038E5C6 user@disp1754:~$

If the above command takes longer than a few seconds to run, then you have a problem. Give it 20 minutes or so, and you'll see problematic keys at the bottom. Anything with 8 digits (>10 MB) is a red flag.

F20691179038E5C6 ` has a size of ` 16934647 ` bytes = 16M. This is our poisoned key. ⓘ Note: In the example above, we see that the public key with id = `` has a size of `` bytes = 16M. This is our poisoned key. The commands that follow in this article use this ` keyid ` (` F20691179038E5C6 `) to manipulate the keyring. You should replace this string in the commands below with the corresponding ` keyid ` found in the command above on your machine.

2. Exporting the poisoned key

Now that we've identified the poisoned key, let's export it for safe keeping before we delete it.

user@disp1754:~$ time gpg -a --export 'F20691179038E5C6' > pubkey.asc real 3m30.950s user 3m24.430s sys 0m0.322s user@disp1754:~$ du -sh pubkey.asc 22M pubkey.asc user@disp1754:~$

After a few minutes, the command will finish, and you should have a file named ` pubkey.asc ` with the contents of the poisoned public key in it. Note that this ASCII armored file containing exactly one public key is 22M!

3. Deleting the poisoned key

Now that we have a safe backup of the poisoned key on disk, let's delete it from our keyring.

user@disp1754:~$ time gpg --delete-key 'F20691179038E5C6' gpg (GnuPG) 2.1.18; Copyright (C) 2017 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. pub ed25519/F20691179038E5C6 2019-01-19 Daniel Kahn Gillmor <dkg@fifthhorseman.net> Delete this key from the keyring? (y/N) y real 12m15.265s user 11m54.242s sys 0m0.715s user@disp1754:~$

4. Re-importing the cleaned key

To re-import a clean copy of the public key, we'll use the ` gpg ` argument ` --import-filters ` to drop all signatures (` drop-sig `) made on the date when the certificate was flooded with signatures.

Given a public key file (like the backup we just exported above), we can list a count of the number of signatures that certificate received for each day with the following command:

user@disp1754:~$ time gpg --list-packets pubkey.asc | grep -i 'sig created ' | sort -n | uniq -c 11 hashed subpkt 2 len 4 (sig created 2019-01-19) 2 hashed subpkt 2 len 4 (sig created 2019-01-20) 4 hashed subpkt 2 len 4 (sig created 2019-01-21) 2 hashed subpkt 2 len 4 (sig created 2019-01-28) 14400 hashed subpkt 2 len 4 (sig created 2019-06-17) 40200 hashed subpkt 2 len 4 (sig created 2019-06-18) real 0m23.061s user 0m17.803s sys 0m0.150s user@disp1754:~$

The above output shows that

14,400 signatures were made to the key on 2019-06-17 and 40,200 signatures were made to the key on 2019-06-18

We can import the key while omitting these spammed signatures on these two days as follows (be sure to replace the dates with the corresponding days printed from the above command on your machine):

user@disp1754:~$ time gpg --import-filter drop-sig="sig_created_d=2019-06-17 || sig_created_d=2019-06-18" --import pubkey.asc gpg: key F20691179038E5C6: 54614 signatures not checked due to missing keys gpg: key F20691179038E5C6: public key "Daniel Kahn Gillmor <dkg@fifthhorseman.net>" imported gpg: Total number processed: 1 gpg: imported: 1 gpg: no ultimately trusted keys found real 3m12.091s user 3m6.991s sys 0m0.284s user@disp1754:~$

And now things should be much more sane:

user@disp1754:~$ time gpg --export | gpg --list-packets | awk -F= -v oldoff=-1 -v keyid=unset ' > /^# off=/{ off = $2 + 0 } > /^:public key/{ if (oldoff>-1) { print (off - oldoff) " " keyid }; oldoff = off; keyid = "unset"; } > /keyid:/ {if (keyid == "unset") { keyid = $1; } } > END { print (off - oldoff) " " keyid ; };' | sort -n 7284 keyid: 1DCBDC01B44427C7 8930 keyid: F20691179038E5C6 119748 keyid: 4E2C6E8793298290 124557 keyid: 403C2657CD994F73 real 0m0.063s user 0m0.059s sys 0m0.016s user@disp1754:~$ gpg -a --export '403C2657CD994F73' > pubkey2.asc user@disp1754:~$ du -sh pubkey2.asc 168K pubkey2.asc user@disp1754:~$

5. Updating your GnuPG config

As Robert J. Hansen (whoose pgp key was spammed with 149,100 signatures on 2019-06-19) pointed out in their excellent comprehensive gist about this issue, you can prevent your gpg client from breaking itself by:

Removing any lines that start with ` keyserver ` in your ` gpg.conf ` file and Updating ` dirmngr.conf ` so that it has only one keyserver: ` keyserver hkps://keys.openpgp.org `

That ` keys.openpgp.org ` keyserver is a new experimental server (interestingly, it went live just weeks before these poisoned certificates were uploaded) that is more resistant to these attacks. Note that the certificates it serves entirely lack third party signatures, and it also strips the UID packets from the key unless a user explicitly opts-in.

6. Updating your MUA config

Update 2019-07-16: Enigmail changed their default keyserver to ` keys.openpgp.org ` in v2.0.12, so updating engimail >= this version is preferred to disabling ` keyRefershOn ` as described below, if possible. (ty Wiktor)

You may also need to update your MUA. For example, enigmail in thunderbird may also be configured to update the keys in your keyring.

To prevent enigmail from refreshing your keys from a keyserver, go to your thunderbird preferences -> Advanced -> Config Editor... -> I accept the risk!

And set ` extensions.enigmail.keyRefreshOn ` to ` false `





Addendum

Note that the keybox keyring format will refuse to import posioned keys as it has a max key size of 5 MiB, and that users with old installs should consider migrating their keyring to keybox format.

This can be done easily in debian-based systems using the migrate-pubring-from-classic-gpg command

Further Reading