New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CVE-2020-25658 - Bleichenbacher-style timing oracle in PKCS#1 v1.5 decryption code #165
Comments
|
The code is basically unchanged for at least 10 years: Lines 120 to 135 in 3f8c551
so we can be reasonably sure that all released versions since 2.1 (the first version that included RSA decryption API) are vulnerable |
|
Hey guys, this seems like a critical security issue. We've been working with python-jose which depends on this repo. |
|
|
Thanks for the quick reply. |
|
Hi @tomato42
Snyk apparently calculated the score here, see here: https://app.snyk.io/vuln/SNYK-PYTHON-RSA-1038401 |
|
For one, as it's timing based, so it's harder to exploit than issues like https://nvd.nist.gov/vuln/detail/CVE-2017-13098 (https://snyk.io/vuln/SNYK-JAVA-ORGBOUNCYCASTLE-32031) So I don't think that rating is consistent with other similar issues. |
|
Thanks for the report & the interest, people, it's much appreciated. How about this approach, would that be a proper fix for this issue? def decrypt(crypto: bytes, priv_key: key.PrivateKey) -> bytes:
blocksize = common.byte_size(priv_key.n)
encrypted = transform.bytes2int(crypto)
decrypted = priv_key.blinded_decrypt(encrypted)
cleartext = transform.int2bytes(decrypted, blocksize)
# Detect leading zeroes in the crypto. These are not reflected in the
# encrypted value (as leading zeroes do not influence the value of an
# integer). This fixes CVE-2020-13757.
crypto_len_bad = len(crypto) > blocksize
# If we can't find the cleartext marker, decryption failed.
cleartext_marker_bad = not compare_digest(cleartext[:2], b'\x00\x02')
# Find the 00 separator between the padding and the message
try:
sep_idx = cleartext.index(b'\x00', 2)
except ValueError:
sep_idx = -1
sep_idx_bad = sep_idx < 0
anything_bad = crypto_len_bad | cleartext_marker_bad | sep_idx_bad
if anything_bad:
raise DecryptionError('Decryption failed')
return cleartext[sep_idx + 1:]The weakest spot here I think is the call to |
the fact that second, in case of failure we definitely can't return part of |
|
How about using
Oh geesh, that shouldn't have been in here. The exception was just disabled to make it possible to do a timing test, and certainly won't be in the final code ;-) |
yes, that does make for nice and clean code, but again, I don't expect it to have any better timing characteristic than for example of code that attempts side-channel free behaviour see here: https://github.com/openssl/openssl/blob/d8701e25239dc3d0c9d871e53873f592420f71d0/crypto/rsa/rsa_pk1.c#L170-L278 |
|
Yeah, I made something similar, but looping over each byte and doing some operations there is much slower than what we have now. |
|
from my previous work on this, I've noticed that subscripting strings and arrays is very expensive (like |
|
I'm quite sure that the code in dae8ce0 does not fix it. |
|
Hi, |
|
|
Thanks @tomato42
How did you end up resolving this issue for you? |
I don't see
I would advise against monkey-patching this code, writing side-channel secure cryptographic code is hard, you really should have any changes around it audited by a cryptographer
I'm not using python-rsa, I just noticed that it's vulnerable and widely used |
Yes, by https in Django for the servers
Let me check once again.
Okay. Will do that for sure.
Ah. I see. |
|
@sybrenstuvel Thanks for the fix! Could you please release a new version? |
|
@tomato42 thanks. Then it is quite misleading to have this issue closed. |
|
Hi, Thanks in advance ! |
|
@NicoleG25 This is definitely not addressed. Why: the supposed fix dae8ce0 was committed on Nov 15, 2020, but the latest release of this library is 4.6 released at Jun 12, 2020 (see https://pypi.org/project/rsa/#history). I am not a security expert, but as @tomato42 's #165 (comment) above: "I'm quite sure that the code in dae8ce0 does not fix it." Good news is that at least google-auth does not use this problematic decrypt method (see above), so it can be considered false-positive in my use-case. |
|
Version 4.7 has just been released to pypi. |
|
Issue still being flagged in scanning for version 4.7 |
|
What does that mean @dalesit ? |
|
I can’t say if @dalesit mean the same thing, but here’s what I’m getting from Gitlab Dependency Scanning ( |
|
Info about the fixed version was never added into the gemnasium vulnerability database https://gitlab.com/gitlab-org/security-products/gemnasium-db/-/blob/master/pypi/rsa/CVE-2020-25658.yml#L11 |
- Update from 4.0 to 4.8 - Update of rootfile - Changelog - Switch to [Poetry](https://python-poetry.org/) for dependency and release management. - Compatibility with Python 3.10. - Chain exceptions using `raise new_exception from old_exception` ([#157](sybrenstuvel/python-rsa#157)) - Added marker file for PEP 561. This will allow type checking tools in dependent projects to use type annotations from Python-RSA ([#136](sybrenstuvel/python-rsa#136)). - Use the Chinese Remainder Theorem when decrypting with a private key. This makes decryption 2-4x faster ([#163](sybrenstuvel/python-rsa#163)). - Fix picking/unpickling issue introduced in 4.7 ([#173](sybrenstuvel/python-rsa#173)) - Fix threading issue introduced in 4.7 ([#173](sybrenstuvel/python-rsa#173)) - Fix [#165](sybrenstuvel/python-rsa#165): CVE-2020-25658 - Bleichenbacher-style timing oracle in PKCS#1 v1.5 decryption code - Add padding length check as described by PKCS#1 v1.5 (Fixes [#164](sybrenstuvel/python-rsa#164)) - Reuse of blinding factors to speed up blinding operations. Fixes [#162](sybrenstuvel/python-rsa#162). - Declare & test support for Python 3.9 Version 4.4 and 4.6 are almost a re-tagged release of version 4.2. It requires Python 3.5+. To avoid older Python installations from trying to upgrade to RSA 4.4, this is now made explicit in the `python_requires` argument in `setup.py`. There was a mistake releasing 4.4 as "3.5+ only", which made it necessary to retag 4.4 as 4.6 as well. No functional changes compared to version 4.2. Version 4.3 and 4.5 are almost a re-tagged release of version 4.0. It is the last to support Python 2.7. This is now made explicit in the `python_requires` argument in `setup.py`. Python 3.4 is not supported by this release. There was a mistake releasing 4.4 as "3.5+ only", which made it necessary to retag 4.3 as 4.5 as well. Two security fixes have also been backported, so 4.3 = 4.0 + these two fixes. - Choose blinding factor relatively prime to N. Thanks Christian Heimes for pointing this out. - Reject cyphertexts (when decrypting) and signatures (when verifying) that have been modified by prepending zero bytes. This resolves CVE-2020-13757. Thanks Carnil for pointing this out. - Rolled back the switch to Poetry, and reverted back to using Pipenv + setup.py for dependency management. There apparently is an issue no-binary installs of packages build with Poetry. This fixes [#148](sybrenstuvel/python-rsa#148) - Limited SHA3 support to those Python versions (3.6+) that support it natively. The third-party library that adds support for this to Python 3.5 is a binary package, and thus breaks the pure-Python nature of Python-RSA. This should fix [#147](sybrenstuvel/python-rsa#147). - Added support for Python 3.8. - Dropped support for Python 2 and 3.4. - Added type annotations to the source code. This will make Python-RSA easier to use in your IDE, and allows better type checking. - Added static type checking via [MyPy](http://mypy-lang.org/). - Fix [#129](sybrenstuvel/python-rsa#129) Installing from source gives UnicodeDecodeError. - Switched to using [Poetry](https://poetry.eustace.io/) for package management. - Added support for SHA3 hashing: SHA3-256, SHA3-384, SHA3-512. This is natively supported by Python 3.6+ and supported via a third-party library on Python 3.5. - Choose blinding factor relatively prime to N. Thanks Christian Heimes for pointing this out. - Reject cyphertexts (when decrypting) and signatures (when verifying) that have been modified by prepending zero bytes. This resolves CVE-2020-13757. Thanks Adelapie for pointing this out. Signed-off-by: Adolf Belka <adolf.belka@ipfire.org> Reviewed-by: Peter Müller <peter.mueller@ipfire.org>
|
Morning, just to note that the potential vuln has raised it's head again, with CVE-2020-25658 has been updated with: -
Not sure if there's anything that can be done, apart from to be aware ? |
|
There are two solutions:
|
|
Thanks @tomato42 appreciate the assist - I think Put it this way, it's the inclusion of kubernetes-client/python` from PyPi which is then throwing up the CVE, Sonatype scan etc. Thanks again, my friend, you rule |
This is correct! We have a couple of open PRs with PKCS#1 v2.0+ functionality, including OAEP, but they haven't seen much activity lately. If you're interested in bringing python-rsa up-to-date and increasing it's security, it would be great if you could take a look!
|

Current PKCS#1 v1.5 decryption code:
python-rsa/rsa/pkcs1.py
Lines 246 to 267 in 6f59ff0
performs the checks on the decrypted value in turn, aborting as soon as first error is found, it also raises an exception in case of errors. This likely provides enough of a timing side channel to mount a Bleichenbacher style attack.
While it's unlikely that a completely side-channel free implementation is possible (see https://securitypitfalls.wordpress.com/2018/08/03/constant-time-compare-in-python/ ), it should be possible to minimise the side-channel by making at least the execution path the same irrespective of previous checks and by providing an API that returns a randomly generated secret in case of error (instead of leaking the timing side-channel by rising an exception) for uses that decrypted value directly as an input to a hash or use it as a symmetric key.
The text was updated successfully, but these errors were encountered: