Vanilla 1 is no longer supported or maintained. If you need a copy, you can get it here.
HackerOne users: Testing against this community violates our program's Terms of Service and will result in your bounty being denied.
Security Bug - Cookie Authentication
I just found a considerably large Security issue with the Cookie Authentication used in Vanilla, so I thought it would be nice of me to share it.
The current mechanism in place to keep Sessions persistant via Cookies uses the following code:
$EncryptedUserID = ForceIncomingCookieString("pass", "");
$VerificationKey = ForceIncomingCookieString("name", "");
*snip*
$s->SetMainTable("User", "u");
$s->AddSelect("UserID", "u");
$s->AddSelect("UserID", "u", "EncryptedUserID", "md5");
$s->AddWhere("md5(UserID)", $EncryptedUserID, "=");
$s->AddWhere("VerificationKey", $VerificationKey, "=");
From this we can tell that to return a valid cookie we need a UserID and the relevent Verification Key. So where is the flaw? Well it resides in the creation of the Verification Key which is a little more predictable than you might expect. Let us take a looka the code for DefineVerificationKey() to see where the problem is:
function DefineVerificationKey() {
return md5(str_replace(".","",GetRemoteIp()).time());
}
At first this seems reasonable, however take a moment to think about it. From http://us3.php.net/time we find time() returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT). The issue here is the use of seconds. The first challenge is to calculate the current Servers time. We already know our own IP, so how do we find the current Serverside timestamp? Given the Servers time is in sync, lets say your IP is 216.118.161.18, and your current local timestamp is 1149533263 and you just logged in. Your Verification Key would have 60*60*24 = 86,400 possiblities (24 hours). So your own Verification Key is going to be between MD5("21611816118"."1149490063") and MD5("21611816118"."1149576463"). Given that it should only take a few seconds to determine the current server time.
All you need now is the UserID, IP and rough time of login to give a small set of Verification Key's to find a collision in. You could obtain these values as follows:
UserID - Just look at the Users profile, for example mine is http://lussumo.com/community/account/2461/ which tells me my UserID is 2461.
IP Address - Put up a display Icon for your User and log every User who visits a given discussion. Remember, Administrators and Moderators are quite often the most active and easiest to antagonize and make post. Generally speaking someone who glimpes in a Discussion will only appear once in the log, while a User who posts will appear twice.
Login Time - Yet again the profile is too the rescue, at the time of writing my profile at http://lussumo.com/community/account/2461/ clearly states 'Last Active 52 minutes ago' which is a timestamp accururate to 120 seconds of our derived timestamp.
So, for example lets say I monitor when Mark (as he is the Master Administrator) is active, then start a Discussion regarding him to make him respond. I then go through the log for image requests and attempt to identify which IP is Marks. I already know his UserID is 1, so I MD5 hash that, and then check his profile to find the rough time he returned to the site. That gives me a total of 120 possible Verification Keys which are valid till Mark next returns. Even given there are three entries in my log, that is still only a mere 3 * 120 = 360 possibly Verification Keys. If I was to code something up to validate a single Verification Key only once a second, that would still only take me six minutes to find the valid value so I can login to his account without his password.
My appologies if that is a little hard to follow, but feel free to read it again. A change to the Verification Key routine as simple as the following would solve the problem and make an extremely difficult key to bruteforce:
function DefineVerificationKey() {
return md5(sprintf('%04x%04x%04x%03x4%04x%04x%04x%04x',
mt_rand(0, 65535),
mt_rand(0, 65535),
mt_rand(0, 4095),
bindec(substr_replace(sprintf('%016b', mt_rand(0, 65535)), '01', 6, 2)),
mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535))
);
}
0
This discussion has been closed.
Comments
You seem to know your way around security. Right now Vanilla invalidates cookies if you sign in from another browser or location. How would you recommend changing the current authenticator so that you can stay logged in from many different sources, but still remain secure?
*Ehm.. just kidding.. I'm actually Jazzman himself -.^