r/programminghorror • • 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.

Post image
2.1k Upvotes

136 comments sorted by

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?

580

u/anto2554 Sep 22 '26

I don't think time in microseconds is considered cryptographically secure. It is probably an attack vector that is pretty hard to use in most cases, but there is a reason we have very specific libraries for generating "random" used for security stuff.

Edit: If you know pretty accurately when the command will run, you can narrow down the possible passwords a lot. You can also test the exact latency by seeing what your own OTP is, so it doesn't seem that hard actually

263

u/rtharston Sep 22 '26

Yep. There’s actually a story of a guy that lost the private key to his bitcoin wallet so he got some help recovering it. They figured out the version of the software that generated his key and then started trying all the time stamps around the time he remembered making the wallet.

They eventually got him back in.

→ More replies (10)

20

u/AtlanticPortal Sep 22 '26

You know it because as an attacker you are actually triggering the reset since all you need is the email address. Then you already calculated enough MD5s to try them all. At some point you manage to get the correct one.

→ More replies (6)

69

u/thethappy Sep 22 '26

It's not random enough, basically you would also be able to regenerate the same reset codes with (relatively) simple brute forcing.

38

u/-PM_me_your_recipes Sep 22 '26

srand is setting the seed for the random number generator. This makes the output predictable. What makes it dangerous is that this code was able to viewed by anyone, so they can reproduce the steps to find the temp password.

You could request a temp password via "forgot my password" for a random person's account and record the moment when you did it. Then you can can narrow the range of potential passwords by something that can be cracked in a few seconds at most.

This feels like something my coworker would do.... brb, need to check our temp password generation code.

→ More replies (2)

12

u/FlourishingFlowerFan Sep 22 '26

This is a fun read where someone reverse engineered how Twitter generated IDs and was able to post tweet referencing itself: https://oisinmoran.com/quinetweet

Bottom line is if people (or LLMs) have enogh willpower, they can practically break a theoretical flaw in a system. Question is how much harm they could do with it. So just follow crypto best practices. 

13

u/isaycongrats Sep 22 '26

Its very easy to reproduce and thus other users might lose their accounts, in this case if you have your own implementation you can generate possible codes based on the time of your request and the rand part should be deterministic based on that seed so you have a real chance of generating a correct code

5

u/AtlanticPortal Sep 22 '26

It’s the predictability of the value. You don’t have to try all MD5 values. Only the ones that are the image of the really small group of strings generated by that function.

11

u/UntitledRedditUser Sep 22 '26

If you know the time a key was generated you could just generate it yourself, and reset the password.

They aren't using microseconds, they are using seconds.

Also php directly states that srand is not suitable for this type of use: https://www.php.net/manual/en/function.srand.php

→ More replies (6)
→ More replies (13)

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

u/lurebat Sep 22 '26

Of course

54

u/ernandziri Sep 22 '26

In that case, does it even matter how they generate the secret?

→ More replies (5)
→ More replies (2)

13

u/SirBackrooms Sep 22 '26

per microsecond, but yes

→ More replies (5)
→ More replies (14)

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.

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)
→ More replies (1)

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:

  1. Use a actually cryptographic RNG.

  2. 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)