How Rainbow Tables Work
kestas.kuliukas.com
kestas.kuliukas.com
----
The prison warden brings a hundred prisoners to an empty room and hands each of them one of a hundred cards labeled from 1 to 100, and says:
"In the next room there are 100 drawers. Into each drawer has been placed one of a hundred cards labeled from 1 to 100."
"One by one we will let you into the next room. Each of you will be given fifty chances to look inside one of those drawers, and then you will continue into the final waiting room."
"If every single one of you is able to find the card in the drawers that matches the one you have been given, you will all be released from prison today."
"One more rule - if any of you, after entering the room with the drawers, communicates any information to the remaining participants, you all fail."
The prisoners talk among themselves, discussing strategies for opening the drawers that might let them all find their number, and be released today.
They know that the chance for one prisoner to find their card, choosing randomly, is 50/100, or 1/2. The problem is that they all need to find their card, and if they all choose randomly then the chance they all succeed is (1/2)^100 - or (7.9 * 10^-29)%
One of the prisoners, having just read about how rainbow tables work, realised there was a strategy that would give them a greater than 30% chance to all succeed.
What was their strategy?
----
It's trivial to find the solution to this problem if you've never seen it before, search for the 100 prisoners problem. I don't actually know if reading about ranbow tables makes it easier to reason the solution, but there seemed a strong similarity so hopefully someone can figure it out!
Basically they're kinda sorta block-chains. And you throw away most of the middle computations since you can arrive at them again fairly trivially.
> Rainbow tables differ in that they don't use multiple tables with different reduction functions, they only use one table. However in Rainbow Tables a different reduction function is used for each column. This way different tables with different reduction functions aren't needed, because different reduction functions are used within the same table.
What exactly is the structure of the final rainbow table - does it contain a column for each reduction function, or does it still only contain "start text" and "last hash" of each chain? I would loved to have seen a diagram of the table structure here. (From my own reading, I think these "extra columns" are not stored anywhere).
Column 1 Column 2
start-text1 start-text2
<intermediate> <intermediate>
last-hash1 last-hash2
You only store the start text and the last hash for each column. <intermediate> consists of a mix of passwords ("text ") and hashes. To see how they are computed, let us narrow our attention to a single column.
text1
<hash-1> computed using H(text1)
<text-2> computed using R_1(hash-1)
<hash-2> computed using H(text-2)
<text-3> computed using R_2(hash-2)
last-hash computed using H(text-3)
Each column will look like that. I'm using <stuff> to denote things that are not stored. H is the hash function you are creating the rainbow table for. R_1, R_2, etc. are the reduction functions. Each column uses the same hash function and reduction functions but uses a different starting text.
Note that in a rainbow table that consists of k columns, there is no need to recompute hash/reduction functions for each chain. Instead, the attacker computes R_last(target-digest), and checks that against all the endpoints (last hash) of all column. If it matches any endpoint, then that chain likely has the corresponding password. Otherwise, compute R_last(H(R_second_to_last(target-digest))), and compare the result with all endpoints. Rinse and repeat. In the worst case, you have to compute as many hash/reduction functions as there are rows (regardless of the number of columns since all columns use the same hash and reduction functions).
Wouldn’t R_last generate text? Why would you compare the text to last hash? Wouldn’t you just compare the target-digest to the last hash directly? (Nice explanation but I got lost here)
You first compare target-digest with all endpoints. If it's a match with any of them, good. Then you know a pre-image is in that column.
If not, then try H(R_last(target-digest)). Does it match any endpoint?
If not, then try H(R_last(H(R_second-to-last(target-digest))).
Rinse and repeat.
Sharing rainbow tables over the internet is probably dead, but your disks can keep more hashes than you can calculate quickly.
I used MD5 because that's the typical hash you find unsalted on leaks, but if you do the math with others it is almost impossible to find an example where storing beats using a GPU to crack (even an older one) for a couple of hours.
Is the idea that password hashes should be slow relatively new?
It's just that security wasn't as important (limited web attack surface) or generally understood back in the day (so people were even less likely to ask "is this hash suitable for passwords rather than checksums/indexing/etc?" than they are today), or the slow ones from then were fine -then-, but advances in hardware, the availability of the cloud/GPUs (so massive parallelization without a cost of infrastructure only a nation state could afford), etc, means they're easily compromised today.
The idea that password hashes should be slow is indeed relatively new. Also it's contemporary to the idea that algorithms should have salts builtin, so those features usually go together.
Building a rainbow tables is much more expensive (compute time, storage) than just breaking any individual hash. So unless you break hashes all day every day, you probably need to share that expense somehow, but then you can't customize. Maybe a large group of you want all of the old "NT hash" values, that's easy enough, but agreeing to do 5-7 alphanumerics for MD5() means the person attacking a site with an eight character minimum gets nothing out of it.
So aside from things like NT hash it has fallen out of favour.
And I think ISP hand out WiFi routers stopped having daft names like this a decade or more ago. The only AP SSIDs that aren't partly random nonsense I see around here are my own (named "Reformed Distributed Republic") and somebody's FON router, the main ones I've seen elsewhere in my city are "eduroam" and "govroam" which are federated systems and so do not have a PSK - in all four cases you aren't going to help yourself by calculating rainbow tables with those SSIDs.
It looks like this generic, repeated-SSID-naming "vulnerability" might continue into the present day via the use of a default name on hotspot devices, or smartphones serving as hotspots -- notice that on the "top 25 pwned wifi network names in the @pwnagotchi project database"[1], the top 1st and 2nd place respectively go to "AndroidAP" and "iPhone". Looking at my Pixel 3, I notice that the default hotspot name doesn't appear to be randomized (it's "PixNet").
[1]: https://twitter.com/evilsocket/status/1305892201222807552
But this isn't the 1970s suppose you have 32-bit salt, now you need to use the rainbow table in 4 billion attacks to amortize the extra cost. Hey maybe you can attack every adult in the world?
In reality modern hashes often use 128-bit salt. Now you need to do billions of attacks, for each of the billions of people on the planet, just to keep it only billions of billions of times more expensive than brute force per attack. Or to put it more simply: This prevents the use of rainbow tables.
Edit: this article is from 11/12/2006 and the last section from 04/09/2009 according the the website's home page.
guy was on some really nice crack to come up with this
And then I never posted another blog posted again.