What's the deal with Randstorm vulnerability?

10 replies 149 views
the_foxFull Member
Posts: 36 · Reputation: 308
#1Dec 4, 2021, 07:09 AM
I've been going through this Unciphered article on Randstorm and honestly, I'm still pretty lost. So from what I gather, wallets made with the BitcoinJs library between 2011 and 2015 have some serious issues because of how they handled randomness. The worst of it seems to be with wallets created between May 4, 2011, and March 2012. But the article doesn’t really clarify what the actual exploit is. It basically boils down to the fact that they used Math.random() for generating random values before March 2014, and we all know that's not a good function. If we check out the initial commit from March 4, 2011, eckey.js is what generates the private key, while rng.js and prng4.js in the jsbn folder are used to gather randomness. In rng.js, if rng_pool isn’t initialized, it gets filled with random values from Math.random(). Now, Math.random() has a cycle of 2^60 before it repeats, which is mentioned in the article. They also say it doesn't pass modern benchmarks, but I'm a bit unclear on what those benchmarks are. So is Math.random() the main issue here? What’s the real weakness? Later on, they seed the pool with the current time in milliseconds, and then they initialize the state and use the pool as the key for the RC4 cipher from prng4.js. That script creates a 256-value array and fills it through a loop. eckey.js then uses SecureRandom() to create our private key. But again, this doesn’t really shed much light on the actual vulnerability or what potential attacks could look like. The article hints that if we have a GUID or IV (which I assume is a public key?), then we might only need to deal with 2^32 to 2^64 values (2^48 seems to be the most common). What does all this really mean?
5 Reply Quote Share
fox100Senior Member
Posts: 165 · Reputation: 1050
#2Dec 4, 2021, 08:59 AM
Weird post. This isn't a new issue, here is a talk I gave in 2015 describing exactly this vulnerability https://youtu.be/TYQ-3VvNCHE?t=3072 (including that it never uses window.crypto in that code).  At the time of my presentation the code in question was already widely known to be insecure and deprecated. So it's pretty weird that they're attributing the discovery to 2018, years later. As far as what attacks might be used-- the state space is simply too small: an attacker can throw computing power at it to search the possible random numbers, generate the resulting keys, and check the blockchain for them. But I think the specific issue is a distraction. It's simple:  DO NOT USE JAVASCRIPT FOR CRYPTOGRAPHY.   If you've used JS with any private key, assume it compromised. No programmer, no matter how skilled can write JS code that doesn't leave secrets laying around in memory. No programmer, no matter how skilled can write JS code that is sure to not leak secrets via side channels. Protecting against memory leaks and timing sidechannels are basic steps that any competent author will take. Thus, by definition, no JS cryptography software is competently written. You shouldn't use crypto software that wasn't competently written even if at the moment no one knows anything specifically wrong with it. With this handy rule of thumb at hand you don't have to worry about the RNG embarrassment discussed in that post.
4 Reply Quote Share
the_foxFull Member
Posts: 36 · Reputation: 308
#3Dec 4, 2021, 10:22 AM
Let me get this straight. In rng.js , our pool rng_pool is seeded first with the time the browser starts in the line 37 like with 48 bits of entropy and then it is seeded in with the time in milliseconds whenever the rng_seed_time() is called (does it bring any new bits of entropy?) The rng_pool is an array that is used to initialize our state rng_state in the line 52 and rng_pool is the input for that initialization. It is being inputted into Arcfour() from prng4.js which in line 32 returns   and this is what is being assigned to rng_state at the end of it Then in ecdsa.js it becomes just rng in the line 128 like: and in the line 180 we construct an integer value from that array with BigInteger(...). After this, the integer is passed to eckey.js for generating the private key? (i.e. after BigInteger(...) we already got our private key. Its output is essentially our private key) On the development side, on cryptography in JavaScript, yeah. Your point is understandable. Even Mozilla developer doc recommends instead to use Web crypto API
6 Reply Quote Share
ninj42016Full Member
Posts: 45 · Reputation: 251
#4Dec 4, 2021, 10:34 AM
I spent some time on it and here is what I understand. Indeed Math. Random is called when a private key is generated, however, it's called many times in a loop. The result of math.random() varies because of the state variable used in Math.Random varies every time it's called. (The implementation of math.random depends on the browser)  while(rng_pptr < rng_psize) {  // extract some randomness from Math.random()     t = Math.floor(65536 * Math.random());     rng_pool[rng_pptr++] = t >>> 8;     rng_pool[rng_pptr++] = t & 255; This in turn is mixed with the time millisecond when the key is generated, I think there could be a small space to search when we know the exact time of key generation otherwise, I am assuming that the keyspace is large to search.
4 Reply Quote Share
fox100Senior Member
Posts: 165 · Reputation: 1050
#5Dec 4, 2021, 11:24 AM
Yes, it is in the browser. But you cannot just ignore it.  Classically-- as in when this code was actively used--  math.random was a 48bit lcg seeded by the browser time at start.  It doesn't matter how many times you call it because math random itself only had 48-bits of state.
2 Reply Quote Share
LuckyCoinLegendary
Posts: 832 · Reputation: 4795
#6Dec 4, 2021, 04:23 PM
It's not only because of that, but also because (node)JS likes to create oodles of files when it installs dependencies, and any one of these packages can be bugged, even if you pin all your dependencies to a specific version. Because there might be a dep of a dep of your dep that is not pinned, and someone takes control of the project and publishes a malicious code to steal keys in your code, and hence everybody else's code. Plain JS also has this problem, because most of the time you are relying on minified bundles that are hard to read and might be hiding a malicious code.
0 Reply Quote Share
fox100Senior Member
Posts: 165 · Reputation: 1050
#7Dec 6, 2021, 07:30 AM
Yes, JS has a dependency culture which is pretty much incompatible with security, though it's not alone-- Rust is also in that boat  (and so I find it a bit alarming to see rust promoted as a solution to security problems.)
3 Reply Quote Share
HyperCipherFull Member
Posts: 220 · Reputation: 780
#8Dec 6, 2021, 09:58 AM
👀 Ser, I merely want to ask a simple question. You said that, and that Rust is also "in that boat", does that actually mean that all of those new, "more modern" blockchains with clients that were written wholly and/or party in Rust are also vulnerable to such leaks, and that users should avoid holding any assets in those blockchains?
4 Reply Quote Share
coin777Senior Member
Posts: 143 · Reputation: 970
#9Dec 6, 2021, 04:00 PM
Of course. If you use unsafe cryptography, then it will bite you. For example: Does it mean that C++ is unsafe? No. It means, that if you use "std::rand()" from the standard library, then it will be unsafe, because then your seed is only some 32-bit timestamp, and all keys, generated in this way, can be sweeped in a moment. And the same is true for other languages and tools: if you use built-in randomness from many languages, and it uses small seeds, then it will hurt you. That's why Satoshi included OpenSSL, instead of generating private keys in some unsafe way, as shown above. Edit: example straight from Rust: in a standard library, you have this function: https://docs.rs/rand/latest/rand/trait.SeedableRng.html#method.seed_from_u64 Guess what: if you use it, then your private key will be as weak, as the puzzle #64, if not weaker. And guess what people might do: they can fill this value with the current timestamp, and boom, everyone using it to create private keys is doomed.
6 Reply Quote Share
fox100Senior Member
Posts: 165 · Reputation: 1050
#10Dec 6, 2021, 07:01 PM
You can write rust code that zeros memory that contained secrets and has constant time behavior wrt secrets.  It's in the same boat as C/C++ which is in practice it works with some care and review but compilers may from time to time undermine you which is why review effort to check that they're not is required. Of course, if you use not fit for purpose (or backdoored) dependencies you're screwed... which I think is a lot more of a risk in rust due to the extreme overuse of dependencies.  That's not a problem with the language itself though, it's a problem with the culture that comes along with the language.
4 Reply Quote Share
LuckyCoinLegendary
Posts: 832 · Reputation: 4795
#11Dec 6, 2021, 10:17 PM
In Rust at least you *might* be able to get away with this by implementing as many things as possible in your own library, and then you don't have to worry about any dependencies at all. Being bare-bones like C/C++ certainly gives it those freedoms. Whereas in a language like JS it is practically impossible to implement everything yourself and still be secure since chances are you are going to be downloading some packages just to make the user's runtime work anyway. Not to mention the giant attack surface that browsers and anything resembling a browser like Electron brings.
3 Reply Quote Share

Related topics