php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #78605 Mitigate EFAIL attack on openssl_pkcs7_encrypt() output
Submitted: 2019-09-29 00:12 UTC Modified: 2019-11-10 19:01 UTC
Votes:1
Avg. Score:3.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: perske at uni-muenster dot de Assigned: bukka (profile)
Status: Not a bug Package: OpenSSL related
PHP Version: 7.2.24 OS: all
Private report: No CVE-ID: None
 [2019-09-29 00:12 UTC] perske at uni-muenster dot de
Description:
------------
Though OpenSSL implements much more ciphers, the 6th parameter to openssl_pkcs7_encrypt() ($cryptid) does only allow for very few values:

OPENSSL_CIPHER_RC2_40
OPENSSL_CIPHER_RC2_128
OPENSSL_CIPHER_RC2_64
OPENSSL_CIPHER_DES
OPENSSL_CIPHER_3DES
OPENSSL_CIPHER_AES_128_CBC
OPENSSL_CIPHER_AES_192_CBC
OPENSSL_CIPHER_AES_256_CBC

*All* these values produce weak ciphertexts, either because the cipher itself is weak (RC2) or because the block cipher mode of operation (CBC) allows to attack the ciphertext with the EFAIL CBC/CFB Gadget Attack, see "efail dot de".

OpenSSL already offers AES GCM ciphers that are not susceptible to the attack, so please implement:

OPENSSL_CIPHER_AES_128_GCM
OPENSSL_CIPHER_AES_192_GCM
OPENSSL_CIPHER_AES_256_GCM

(AES GCM is the cipher recommended in the Security Considerations in the new S/MIME RFC 8551 that was written after EFAIL became known.)

Thank you!




Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-10-27 20:12 UTC] bukka@php.net
-Assigned To: +Assigned To: bukka
 [2019-10-27 20:12 UTC] bukka@php.net
I don't think those are available for PKCS7 envelope. In fact they are currently not even implemented for CMS. I have got actually PR in progress to add that support for CMS to OpenSSL: https://github.com/openssl/openssl/pull/8024 . How would you add such support for PKCS7 if it's not even defined in any RFC? Or am I missing something?

Also missing support for ciphers does not really qualify as a security issue. We just expose what OpenSSL gives us. Or do I miss something in here too? :)
 [2019-10-27 22:07 UTC] perske at uni-muenster dot de
-PHP Version: 7.2.23 +PHP Version: 7.2.24
 [2019-10-27 22:07 UTC] perske at uni-muenster dot de
Thank you for you check-back. I hope this long comment can give more explanations. :-)

Regarding the questions whether this qualifies as a security issue:

I think it does because openssl_pkcs7_encrypt() is currently producing encrypted data that is no longer secure since EFail.

(The security issue is not that ciphers are missing but that the output is vulnerable with every currently available cipher; you cannot even choose a cipher that produces non-vulnerable output. This makes existing e-mail applications that use this function vulnerable, and I am using such applications, for example www.uni-muenster.de/WWUCA/en/howto-setup-php.html#4 . This is why I detected this weakness.)

Regarding whether there is an RFC that allows to use the AES GCM ciphers in PKCS#7 / S/MIME:

The RFC is RFC8551 chapter 2.7. It does not only allow but even demands ("MUST") that AES GCM must be supported in S/MIME (and so in PKCS#7).

Regarding the implementation:

Maybe I do not understand every detail of the source code (this is why I do not dare to write a patch myself), but if I look into ext/openssl.c of PHP 7.2.24, trying to understand what the source code does, I see this:

How ist the cipher argument evaluated and passed to OpenSSL?

PHP Function openssl_pkcs7_encrypt() starts in line 5168,
reads in line 5188 the cipher argument into "cipherid",
converts it in line 5253 into "cipher" using php_openssl_get_evp_cipher_from_algo(),
and passes it in line 5260 to the OpenSSL function PKCS7_encrypt().

What ciphers are known by php_openssl_get_evp_cipher_from_algo()?

Function php_openssl_get_evp_cipher_from_algo() starts in line 1361,
knows a list of PHP_OPENSSL_CIPHER_XXXXX constants,
returns for each constant the result of a corresponding OpenSSL EVP_xxxxx() function.

What constants are known and what EVP_xxxxx() functions are available()?
This probably depends on the OpenSSL version.
But PHP is already using appropriate feature test macros.
PHP already knows (see line 1361 ff.):
#ifndef OPENSSL_NO_RC2
  PHP_OPENSSL_CIPHER_RC2_40 - EVP_rc2_40_cbc()
  PHP_OPENSSL_CIPHER_RC2_64 - EVP_rc2_64_cbc()
  PHP_OPENSSL_CIPHER_RC2_128 - EVP_rc2_cbc()
#ifndef OPENSSL_NO_DES
  PHP_OPENSSL_CIPHER_DES - EVP_des_cbc()
  PHP_OPENSSL_CIPHER_3DES - EVP_des_ede3_cbc()
#ifndef OPENSSL_NO_AES
  PHP_OPENSSL_CIPHER_AES_128_CBC - EVP_aes_128_cbc()
  PHP_OPENSSL_CIPHER_AES_192_CBC - EVP_aes_192_cbc()
  PHP_OPENSSL_CIPHER_AES_256_CBC - EVP_aes_256_cbc()

If I look into /usr/include/openssl/evp.h (RedHat Enterprise Linux 7, openssl-1.0.2k with fixed backported by RedHat, so your own findings may differ), I see:

[...]
# ifndef OPENSSL_NO_RC2
const EVP_CIPHER *EVP_rc2_ecb(void);
const EVP_CIPHER *EVP_rc2_cbc(void);
const EVP_CIPHER *EVP_rc2_40_cbc(void);
const EVP_CIPHER *EVP_rc2_64_cbc(void);
[...]
# ifndef OPENSSL_NO_AES
const EVP_CIPHER *EVP_aes_128_ecb(void);
const EVP_CIPHER *EVP_aes_128_cbc(void);
const EVP_CIPHER *EVP_aes_128_cfb1(void);
const EVP_CIPHER *EVP_aes_128_cfb8(void);
const EVP_CIPHER *EVP_aes_128_cfb128(void);
#  define EVP_aes_128_cfb EVP_aes_128_cfb128
const EVP_CIPHER *EVP_aes_128_ofb(void);
const EVP_CIPHER *EVP_aes_128_ctr(void);
const EVP_CIPHER *EVP_aes_128_ccm(void);
const EVP_CIPHER *EVP_aes_128_gcm(void);
const EVP_CIPHER *EVP_aes_128_xts(void);
const EVP_CIPHER *EVP_aes_128_wrap(void);
const EVP_CIPHER *EVP_aes_128_wrap_pad(void);
const EVP_CIPHER *EVP_aes_192_ecb(void);
const EVP_CIPHER *EVP_aes_192_cbc(void);
const EVP_CIPHER *EVP_aes_192_cfb1(void);
const EVP_CIPHER *EVP_aes_192_cfb8(void);
const EVP_CIPHER *EVP_aes_192_cfb128(void);
#  define EVP_aes_192_cfb EVP_aes_192_cfb128
const EVP_CIPHER *EVP_aes_192_ofb(void);
const EVP_CIPHER *EVP_aes_192_ctr(void);
const EVP_CIPHER *EVP_aes_192_ccm(void);
const EVP_CIPHER *EVP_aes_192_gcm(void);
const EVP_CIPHER *EVP_aes_192_wrap(void);
const EVP_CIPHER *EVP_aes_192_wrap_pad(void);
const EVP_CIPHER *EVP_aes_256_ecb(void);
const EVP_CIPHER *EVP_aes_256_cbc(void);
const EVP_CIPHER *EVP_aes_256_cfb1(void);
const EVP_CIPHER *EVP_aes_256_cfb8(void);
const EVP_CIPHER *EVP_aes_256_cfb128(void);
#  define EVP_aes_256_cfb EVP_aes_256_cfb128
const EVP_CIPHER *EVP_aes_256_ofb(void);
const EVP_CIPHER *EVP_aes_256_ctr(void);
const EVP_CIPHER *EVP_aes_256_ccm(void);
const EVP_CIPHER *EVP_aes_256_gcm(void);
const EVP_CIPHER *EVP_aes_256_xts(void);
const EVP_CIPHER *EVP_aes_256_wrap(void);
const EVP_CIPHER *EVP_aes_256_wrap_pad(void);
[...]

If I interpret my findings correctly, it should be a not too difficult task to extend php_openssl_get_evp_cipher_from_algo() so it knows also the existing OpenSSL functions

EVP_aes_128_gcm()
EVP_aes_192_gcm()
EVP_aes_256_gcm()

(and possibly further ciphers)

You wrote "We just expose what OpenSSL gives us". Nothing more I want. Because of these findings, I dare to ask kindly to expose these AES GCM functions if they are available.

(In my eyes my proposed change cannot break any existing application, so it can be done in a minor release (perhaps already in 7.2.25?). And in my eyes it is worth the effort because then an application can again use openssl_pkcs7_encrypt() to create a secure S/MIME encryted e-mail.)

Thank you very much! :-)
 [2019-11-06 19:56 UTC] bukka@php.net
I think you are confusing ciphers available for encryption (EVP_EncryptInit_ex) and ciphers that work for PKCS7 (PKCS7_encrypt). Those are limited and AES GCM doesn't work. Just try it and you will see. :)

Also we are still talking about PKCS7 and CMS. CMS is currently not supported by openssl extension and even if it was, the support for AES GCM is still not available in any release version.

I'm afraid I will have to close this as there isn't really anything we can do. I also don't think we can consider this as a security issue for the reason that I said before.
 [2019-11-06 19:58 UTC] bukka@php.net
TYPO

... Also we are still talking about PKCS7 and CMS ...

should be

... Also we are still talking about PKCS7 and NOT CMS ...
 [2019-11-07 00:07 UTC] perske at uni-muenster dot de
Thank you very much for your comments and your intense look at the problem!

Just for completeness, if someone later stumbles across this bug report:

Yes, we are talking about PKCS#7. Embedding the output of PKCS#7 encryption into a valid CMS e-mail is done by my calling PHP code and not a feature I request from the PHP function openssl_pkcs7_encrypt().

According to the man pages on openssl.org, PKCS7_encrypt() has this synopsis:

    » PKCS7 *PKCS7_encrypt(STACK_OF(X509) *certs, BIO *in, const EVP_CIPHER *cipher, int flags); «

In the man page of PKCS7_encrypt(), there is only one restriction for the cipher parameter:

    » The algorithm passed in the cipher parameter must support ASN1 encoding of its parameters. «

So according to this man page, passing the the result of EVP_aes_256_gcm() as third argument to PKCS7_encrypt() should be supported.

But I didn't see one remark until now: On the man page »EVP_EncryptInit« for EVP_aes_256_gcm() and many similar functions, in the BUGS section, there is another important remark:

    » The ASN1 code is incomplete (and sometimes inaccurate) it has only been tested for certain common S/MIME ciphers (RC2, DES, triple DES) in CBC mode. «

    :-(

I think it is still a security problem that encrypted S/MIME e-mails created with PHP are no longer secure.

But unfortunately this problem cannot be fixed as long as OpenSSL does not support ASN1 for AES-GCM.

Both OpenSSL and PHP need to be improved, first OpenSSL, then PHP, to fix it and it makes no sense to improve PHP before OpenSSL.

So it is okay to close this bug; maybe it can be reopened when OpenSSL provides the necessary support.

Thank you very much!
 [2019-11-10 19:01 UTC] bukka@php.net
-Status: Assigned +Status: Not a bug
 [2019-11-10 19:01 UTC] bukka@php.net
That missing ASN1 encoding is exactly the reason why GCM is not supported.

I don't think it will be ever added to PKCS7. It could be potentially added in the future once it is supported in CMS which has got RFC 5084 for ASN1 encoding. First PHP will need to get CMS support though.
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Sun Nov 17 12:01:34 2019 UTC