How I Hacked Hacker News (with arc security advisory)
Hacker News login cookies are random eight-character strings, stored server-side in a hash table mapping them to user names. I discovered a few weeks ago that these strings were rather less random than they were meant to be, and, through a delightful combination of exploits, could be predicted, enabling an attacker to steal accounts.
Here's the rand-string function from arc.arc, version 2. It gets called with n=8 to generate login cookies, and n=10 for the "fnids" that get used all over the site as hash keys identifying closures.
(def rand-string (n)
(with (cap (fn () (+ 65 (rand 26)))
sm (fn () (+ 97 (rand 26)))
dig (fn () (+ 48 (rand 10))))
(coerce (map [coerce _ 'char]
(cons (rand-choice (cap) (sm))
(n-of (- n 1) (rand-choice (cap) (sm) (dig)))))
'string)))
The first thing you might notice about this function is that not all
characters are equally probable. Each digit has a 1/30 chance of
occuring, while each letter has a 1/78 chance. This alone is no big
deal: this distribution means that each character carries 5.826 bits
of entropy, versus the 5.954 that a uniform distribution would
provide. So for an eight-character string, this bug reduces the
effective keyspace by just over a factor of two -- not enough to have
any practical implications.The 'rand' function is an arc primitive, bound directly to mzscheme's 'random':
; need to use a better seed
(xdef 'rand random)
The comment seen here is prescient, as we'll see.This is the C function which implements mzscheme's 'random' function:
static long sch_int_rand(long n, Scheme_Random_State *rs)
{
double x, q, qn, xq;
/* generate result in {0..n-1} using the rejection method */
q = (double)( (unsigned long)(m1 / (double)n) );
qn = q * n;
do {
x = mrg32k3a(rs);
} while (x >= qn);
xq = x / q;
/* return result */
return (long)xq;
}
Where mrg32k3a() is: static double mrg32k3a(Scheme_Random_State *s) { /*(double), in {0..m1-1}*/
double x10, x20, y;
long k10, k20;
/* component 1 */
x10 = a12*(s->x11) - a13n*(s->x12);
k10 = (long)(x10 / m1);
x10 -= k10 * m1;
if (x10 < 0.0)
x10 += m1;
s->x12 = s->x11;
s->x11 = s->x10;
s->x10 = x10;
/* component 2 */
x20 = a21*(s->x20) - a23n*(s->x22);
k20 = (long)(x20 / m2);
x20 -= k20 * m2;
if (x20 < 0.0)
x20 += m2;
s->x22 = s->x21;
s->x21 = s->x20;
s->x20 = x20;
/* combination of component */
y = x10 - x20;
if (y < 0.0)
y += m1;
return y;
}
This, obviously, is not a cryptographically strong PRNG. Is it possible
that we could break it, computing its internal state by seeing a few
consecutively-generated rand-strings? Probably: it looks as though it
could be represented as the solution to a manageable system of diophantine
equations. That, though, was more math than I felt like doing, so I went
looking for an easier approach.Where does the RNG seed come from? Ah ha:
rs = scheme_make_random_state(scheme_get_milliseconds());
Where scheme_get_milliseconds is defined, after eliding some
preprocessor cruft, as: long scheme_get_milliseconds(void)
{
struct timeb now;
ftime(&now);
return now.time * 1000 + now.millitm;
}
In other words, the random seed is merely the number of milliseconds
since epoch at the time the seed function was called.The part of mzscheme that calls the seed function is a bit daunting: it appears that in some cases, the PRNG state can be thread-loca...
83 comments
[ 4.1 ms ] story [ 132 ms ] threadBoth involve taking hardware, a programming language, an API, an application or something else that someone in the computing industry had one purpose for, and bending it in a way to produce a rather unexpected, very unique result.
A great developer builds Web 2.0 with something as novel and limited as JavaScript and HTML.
A great security researcher made your toaster catch fire from a different continent (didn't they always say our toasters would be internet connected?).
I, personally, think the latter is more fun provided it doesn't cause any actual damage (and I think that was demonstrated ... a whitehat makes his own toaster catch fire and sometimes tells everyone who has that model of toaster how to fix it).
Nicely done.
Security guys think of ways to break things.
That's the main difference.
http://news.ycombinator.com/item?id=563492
You would have a fnid that is in the cookie hash table, yet you still would not know to which username it is mapped to, correct?
That hack was damned impressive for breadth and depth of knowledge, pretty rare in my experience.
This should be exhibit A, B, and C in the death of the idea of the "rockstar programmer".
Like Pandora, he is so curious, he has to check this out.
Like a rat in a maze, he keeps going looking for the clear path.
Like Sherlock Holmes, he applies logic to determine the next step.
Like General Sherman, he keeps marching, building tools along the way as he needs them.
Like William Randolph Hearst, he defines the landscape. ("You provide the pictures, I'll provide the war.") "so I decided on a more proactive approach: crash it!" (hilarious)
And like any parent, he didn't quit until his baby walked.
Thank you, Daniel. I sure hope you've found a way to channel that talent in your day job.
Slight error, Pandora was a female in greek mythology (well, the first female to be more precise)
Pandora was the wife of Prometheus (the Titan of Knowledge) who made all the animals and humanity (last of all and out of the only material he had left - clay). In order to give them life, he stole fire from the gods and gave it to his creations, for which he was bitterly punised. In one telling he was tied to a mountain and his heart/liver was eaten by an eagle/vulture and grew back every day. HOWEVER, in another telling, his punishment was PANDORA and her famous box.... I just always found that interesting, vivisection every day for eternity, or a curious wife... fine lines =P
EDIT - just looked it up, it was incorrectly translated by Erasmus of Rotterdam when he first translated Hesiod's tale into Latin from Greek.
I knew from previous discussions with you that you did work at this level, but this is the first time I had actually seen for myself. It read almost like a mystery and I found myself guessing what you'd do next. So I thought I'd pay a quick tribute with what came into my mind first.
AFAIC, you don't need a proposal or resume any more. Just email any prospect or prospective employer this link. If they don't see what you can do from this, you probably don't want to work for them anyway.
(plus having the ethics to actually do no evil - great combination)
That's the difference between a true hacker at heart and someone who just stumbles across something. Tenacity.
I wonder what set you off.
I do like the fact that here on HN there were at least two of us who thought of this way of hacking the site, one actually made it happen. It's a high quality community.
The point is that people and the ideas they have are influenced by external factors, many of them unconscious. But we will pick up on the same clues, think about the same problems, and maybe come up with the same solutions based on external factors. Nothing can stop an idea whise time has come.
(see The Telephone Gambit: Chasing Alexander Graham Bell's Secret, http://www.amazon.com/gp/product/0393062066?ie=UTF8&tag=... )
(I've recently read The Baroque Cycle again, does it show?)
Probably fine for generating session IDs in most circumstances, but if you're using it in another situation (e.g. shuffling something), you might find the performance is terrible.
http://dfranke.us/cfod.html
Why is this obvious?
The generator used is L'Ecuyer's MRG32k3a : http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00s...
Is there a known weakness?
And, oh look, he's also a fantastic hacker.
Thanks for taking the time to write this out. This is the kind of hack I enjoy most. Watching the combination of obscure facts and astute observations come together into a coherent and powerful whole is a pleasure.
http://news.ycombinator.com/item?id=604323
Previous record holder with 530 points:
http://news.ycombinator.com/item?id=459289
what a job, what a feedback, what a community!!!! no awesomenes but more warm warm warm!
Thanks for sharing it with everyone!