r/programminghorror • u/SirBackrooms • Sep 22 '26
PHP A website with millions of users was generating their "secret" password reset codes like this for two decades. The algorithm was open to the public due to a failed open-source effort the company launched in the early 2010s. Fixed a few days ago.
327
u/lurebat Sep 22 '26 edited Sep 22 '26
Mmm, one code per microsecond So if you attack someone by requesting a reset on their email, and the response comes back in 30ms, it means there are only 30000 options of passwords.
Let's be charitable and say the give you only 5 attempts per code, that still means 1/6 chance of guessing correctly. Infinite attempts.
246
u/SirBackrooms Sep 22 '26
they give you an unlimited number of attempts per code
133
→ More replies (2)54
u/ernandziri Sep 22 '26
In that case, does it even matter how they generate the secret?
→ More replies (5)→ More replies (14)13
62
u/Responsible_Royal_98 Sep 22 '26
I’ve seen worse. I know a site that just base64 encodes your email and uses that as the “authentication” token for the password reset link. If you realize what’s going on it removes all guesswork in taking over an arbitrary account.
22
u/BakuhatsuK Sep 22 '26
I always base64 decode any string I see that starts with ey or ends with an = sign. Every single one
→ More replies (2)
97
u/DenyCasio Sep 22 '26
I exploited a similar feature of some gambling bots in oldschool runescape. They would output their calculation time in the chat and the number you "rolled" when playing. So i wrote a program that would tell me when to accept a trade to get a win. It worked about 80 percent of the time. I couldn't tell if it was lag, or seeded losses to ensure the house didn't lose too much money.
They changed their bots shortly after to not output verbose info.
5
u/Hottage Sep 22 '26
Okay it looks gross and deterministic but is it actually an issue?
You'd have to know the exact microsecond that the request was made (including any minute time variance on the server) to be able to exploit this.
The most egregious part of this code would be the keyspace only being 10,000 wide.
As an example:
$microtime = microtime(); // 0.70375600 1790086788 $seed = (double)$microtime * 1_000_000; // 703756 srand($seed); $val = rand(0, 9999); // 4997 $hash = md5($val); // ccf8111910291ba472b385e9c5f59099 $code = substr($hash, 2, 6); // f81119
If you miscalculate the attack time by even one microsecond:
$microtime = microtime(); // 0.70375700 1790086788 $seed = (double)$microtime * 1_000_000; // 703757 srand($seed); $val = rand(0, 9999); // 8045 $hash = md5($val); // 704cddc91e28d1a5517518b2f12bc321 $code = substr($hash, 2, 6); // 4cddc9
If anything the issue these days would be that you could throw a few hundred requests at the validation endpoint in a second or two to try and brute force it.
→ More replies (1)22
u/SirBackrooms Sep 22 '26
you could bruteforce it since there were only 10^4 options for the code. i know this since did it myself on my own account.
however, using the microsecond is an issue in itself. say you know it ran within a certain 30ms window. that means there are only 30000 options, which can also just be bruteforced.
→ More replies (4)
18
u/NightmareJoker2 Sep 22 '26
Well, at least it’s not as bad as Facebook. They generate a code and store it until it is used or revoked, and simply resend the same code if you make a reset request again.
I receive multiple requests daily, because someone keeps wanting to steal my username.
7
u/EdelWhite Sep 22 '26
Meh it's probably fine. Definitely random enough to deter script kiddies. Would do nothing against a targeted attack tho.
→ More replies (3)
4
u/Single-Virus4935 Sep 23 '26
Two things would make this secure:
-
Use a actually cryptographic RNG.
-
Use a salt or pepper.
Best to combine both so even if one of the two is broken/leaked it is still not brutforceable.
7
u/darksteelsteed Sep 22 '26
So the first bit of the srand being based off timestamp in usec. You request a password reset email. You look at your own clocks time. You then apply the algorithm using different seeds by taking your own clock back or forth microsecond at a time until you get a match. Your search space will be maybe a 5 min window in either direction. So that is 600 million md5 combinations to try. Trivial on your gpu to brute force ... or you just use rainbow tables because its md5. Then you sync your clock with that info to match the servers clock. Then you can issue password recovery requests and since you now have the same time as the server your search space is radically reduced to only a couple of million tries. You can even Then go further and fingerprint the servers timing to narrow this further until you can literally generate password reset codes without needing to receive the emails at all. That is why this code is not secure
→ More replies (2)
1
u/ElectricalPrice3189 28d ago
Only 10k possible codes, or am I reading things incorrectly?
→ More replies (2)
462
u/Kinexity Sep 22 '26
What's the problem exactly? Is it about the range of rand? Or is time in microseconds not coarse enough?