node-rsa icon indicating copy to clipboard operation
node-rsa copied to clipboard

node-rsa vulnerable to the Marvin Attack

Open tomato42 opened this issue 2 years ago • 18 comments

(since I have haven't found a security policy or an email to report security vulnerabilities I'm filing it directly here)

I've verified that the node-rsa 1.1.1 package running on node 21.1.0 is vulnerable to the Marvin Attack (a timing variant of the well-known Bleichenbacher attack).

I've executed the script on Ryzen 5 5600X on an isolated cpu core.

The summary of the test:

Sign test mean p-value: 0.3691, median p-value: 0.2482, min p-value: 0.0
Friedman test (chisquare approximation) for all samples
p-value: 0.0
Worst pair: 3(no_structure), 5(valid_0)
Mean of differences: -3.17599e-05s, 95% CI: -3.24520e-05s, -3.114535e-05s (±6.533e-07s)
Median of differences: -3.20305e-05s, 95% CI: -3.22410e-05s, -3.179000e-05s (±2.255e-07s)
Trimmed mean (5%) of differences: -3.17426e-05s, 95% CI: -3.19244e-05s, -3.155255e-05s (±1.859e-07s)
Trimmed mean (25%) of differences: -3.18371e-05s, 95% CI: -3.20280e-05s, -3.163763e-05s (±1.952e-07s)
Trimmed mean (45%) of differences: -3.19491e-05s, 95% CI: -3.21577e-05s, -3.173588e-05s (±2.109e-07s)
Trimean of differences: -3.18982e-05s, 95% CI: -3.20940e-05s, -3.168987e-05s (±2.021e-07s)
Layperson explanation: Definite side-channel detected, implementation is VULNERABLE

conf_interval_plot_trim_mean_05 the description of the probes:

ID,Name
0,header_only
1,no_header_with_payload_48
2,no_padding_48
3,no_structure
4,signature_padding_8
5,valid_0
6,valid_48
7,valid_192
8,valid_246
9,valid_repeated_byte_payload_246_1
10,valid_repeated_byte_payload_246_255
11,zero_byte_in_padding_48_4

explanation of the probes is in the step2.py script.

Given the large size of the timing signal, the attack will be easy to perform remotely.

tomato42 avatar Nov 25 '23 16:11 tomato42

Did I understand correctly that the vulnerability essentially necessitated access to the machine running node-rsa code?

rzcoder avatar Nov 25 '23 16:11 rzcoder

Did I understand correctly that the vulnerability essentially necessitated access to the machine running node-rsa code?

No, absolutely no. It's measurable over the network, see the linked papers from the vulnerability page.

I haven't made an experiment with a side-channel this huge, but I'd say even attacking a server good few hundred kilometers away, with a RTT of few milliseconds should be entirely doable with reasonable attack time. Attack from within the same data centre will be a manner of few hours per decryption.

tomato42 avatar Nov 25 '23 16:11 tomato42

Is it same result with default pkcs1_oaep scheme?

Also, can you pls confirm same result with keyObj.setOptions({environment: "browser"})? Probably yes, just wondering.

rzcoder avatar Nov 25 '23 17:11 rzcoder

with keyObj.setOptions({environment: "browser"}) set (jut added it after keyObj.setOptions({ encryptionScheme: {scheme: algName}});) it's even worse:

conf_interval_plot_trim_mean_25

  1. because the side-channel is much larger (100µs vs 32µs)
  2. it looks like the length of the padding is not checked: the valid_246 has a PS of 7 bytes, when the standard specifies a minimum of 8

I don't see anything similar when running OAEP (without "browser" set), but then I haven't ran a large sample size, so it may still show up. But the speed of decryption would suggest that in nodejs environment it's just delegating to node? If that's the case, then there's no need to check OAEP: learning about error there doesn't provide information to the attacker

tomato42 avatar Nov 25 '23 19:11 tomato42

@rzcoder any progress on this?

tomato42 avatar Jan 29 '24 00:01 tomato42

@tomato42 It doesn't seem like anything can be fixed here. The engine on pure JS is used only in client-side code, and shouldn't be vulnerable to such an exploit through many requests. And the nodejs methods relate to nodejs/OpenSSL projects.

rzcoder avatar Feb 02 '24 15:02 rzcoder

It doesn't seem like anything can be fixed here.

Dropping support for PKCS#1v1.5 encryption would fix the issue... That's what jsrsasign did for CVE-2024-21484

and shouldn't be vulnerable to such an exploit through many requests

How so?

tomato42 avatar Feb 02 '24 16:02 tomato42

It doesn't seem like anything can be fixed here

as I said, "it looks like the length of the padding is not checked:", that's an outright bug in the implementation...

tomato42 avatar Feb 02 '24 16:02 tomato42

How so?

Not a real world use case.

it looks like the length of the padding is not checked

Ok, if just this. Can you please check version from https://github.com/rzcoder/node-rsa/tree/check_pkcs1v1.5_padding_size ? Is it gets any better?

rzcoder avatar Feb 02 '24 17:02 rzcoder

Not a real world use case.

Sorry, this is a security sensitive API. The package has nearly a thousand dependencies and over half a million weekly downloads. I doubt that it's "not a real use case" for each and every user of this library.

https://github.com/rzcoder/node-rsa/tree/check_pkcs1v1.5_padding_size

sorry, don't have the time to run tests. Having the unittests verifying correct behaviour would make it much easier to review...

tomato42 avatar Feb 02 '24 17:02 tomato42

I doubt that it's "not a real use case"

This particular behaviour is present only in the client-side browser, which is usually does not handle incoming requests that can exploit this vulnerability, especially with a specific scheme that is not set by default. Therefore, I am quite sure that there is no real-world use case for this in the wild. Otherwise, this vulnerability will not be the biggest problem, I think...

rzcoder avatar Feb 02 '24 17:02 rzcoder

I still don't see it: just because the code is running in the browser doesn't mean that the server is trusted. It can be providing malformed messages specifically to decrypt data it has no access to. I mean: why else would anybody run RSA decryption using javascript in the browser otherwise?

tomato42 avatar Feb 02 '24 18:02 tomato42

I agree that this is a possible situation. But in any case, the client needs to start interacting with the malicious server himself; this is not a situation where an attacker can connect on his own initiative.

Any way, if you'll have time, please run tests on https://github.com/rzcoder/node-rsa/tree/check_pkcs1v1.5_padding_size if it any better I'll merge it to master.

rzcoder avatar Feb 03 '24 03:02 rzcoder

There are situations where the server doesn't have to be malicious, just a participant: like capturing keys through sound or leaks that happen through power LEDs.

Sorry, I won't have the time to test this code any time soon.

tomato42 avatar Feb 05 '24 15:02 tomato42

Any update on this? I'm hoping that I don't have to start using the --security-revert=CVE-2023-46809 when I start my project :)

I looked at the script to run the timing attack but I don't even know where to start with that.

jlkalberer avatar Apr 03 '24 02:04 jlkalberer

What do you mean you "don't know where to start"? There are step-by-step instructions linked in the first first post of this issue: https://github.com/tomato42/marvin-toolkit/tree/master/example/node-rsa

Also, you need to use --security-revert=CVE-2023-46809 only if you use OpenSSL without implicit rejection. Fedora, RHEL, and Ubuntu (there may be others, I'm not tracking that) have backported implicit rejection from 3.2.0 to earlier versions.

tomato42 avatar Apr 03 '24 09:04 tomato42

@tomato42 - yes, I looked at those instructions and it's pretty clear that I'd have to spend half a day or longer getting that environment set up. Would it be possible to set all this up in a docker container? (Would that even work for the attack?) I'm sure that would get you a better response from the maintainers as it would be much easier to follow your instructions on a container that already has all the dependencies pulled down.


I'm not a security expert so please excuse my ignorance on this but what you're saying about the --security-revert=CVE-2023-46809 is that various distros are patching the bug. That means that even if this package isn't fixed, it is covered by the distros?

jlkalberer avatar Apr 03 '24 17:04 jlkalberer

Would it be possible to set all this up in a docker container? (Would that even work for the attack?)

is it possible? yes, and to confirm such large side-channels as those one here, it will work fine. Do I recommend it for verification that the issue was fixed? no. The closer to the hardware the test can be executed, the less noise there should be and thus the less data needs to be collected. Containers may be thin enough to not matter, but I haven't verified it.

I'm sure that would get you a better response from the maintainers as it would be much easier to follow your instructions on a container that already has all the dependencies pulled down.

the instructions and scripts don't require installation of any software outside of python virtual environments. As long as you have git, openssl, bash, and Python 3.7 or later, you have everything you need to run the tests... Introducing containers would make it more complex, not less. If you feel more confident in running them in containers, just grab any recent Linux distro and run the scripts in the container.

I'm not a security expert so please excuse my ignorance on this but what you're saying about the --security-revert=CVE-2023-46809 is that various distros are patching the bug. That means that even if this package isn't fixed, it is covered by the distros?

not exactly sure what you're asking...

if it is: "Is it possible to workaround the issue by changing just OpenSSL, without Node.js?", then the answer is Yes. The change in Node.js is just to make sure that an insecure API is not available. If it can be safely used, then the API is re-enabled. If not, then you have to use the command line switch to re-enable it, thus making your application potentially vulnerable.

tomato42 avatar Apr 03 '24 17:04 tomato42