Using Python to authenticate against raw username, hash, salt in an ASP.NET role / membership database

We have a current application in which user login credentials are stored in a SQL Server database. They are stored primarily as a text username, password hash and associated salt for this hash.

All of them were created by built-in functions in the ASP.NET membership / role system. Here is the line for the user named "joe" and the password is "password":

Joe, kDP0Py2QwEdJYtUX9cJABg ==, OJF6H4KdxFLgLu + oTDNFodCEfMA =

I threw this stuff into a CSV file, and I'm trying to load it into a Django-friendly format that stores its passwords in this format:

[algo] $ [salt] $ [hash]

If the salt is a simple string and the hash is the SHA1 hexadecimal digest.

Until now, I could verify that ASP stores these hashes and salts in base64 format. These values ​​above are decoded into binary strings.

We used a reflector to find out how ASP authenticates against these values:

internal string EncodePassword(string pass, int passwordFormat, string salt) { if (passwordFormat == 0) { return pass; } byte[] bytes = Encoding.Unicode.GetBytes(pass); byte[] src = Convert.FromBase64String(salt); byte[] dst = new byte[src.Length + bytes.Length]; byte[] inArray = null; Buffer.BlockCopy(src, 0, dst, 0, src.Length); Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length); if (passwordFormat == 1) { HashAlgorithm algorithm = HashAlgorithm.Create(Membership.HashAlgorithmType); if ((algorithm == null) && Membership.IsHashAlgorithmFromMembershipConfig) { RuntimeConfig.GetAppConfig().Membership.ThrowHashAlgorithmException(); } inArray = algorithm.ComputeHash(dst); } else { inArray = this.EncryptPassword(dst); } return Convert.ToBase64String(inArray); } 

Eseentially, extracts salt from the database and b64 decodes it into binary representation. He makes "GetBytes" on a raw password, and then he combines them, first with salt.

Then it runs the SHA1 algorithm in this new line, base64 encodes it and compares it with the value stored in the database.

I tried to write some code to try to reproduce these hashes in Python, and I am failing. I cannot use them in Django until I can understand how this translates. This is how I test:

 import hashlib from base64 import b64decode, b64encode b64salt = "kDP0Py2QwEdJYtUX9cJABg==" b64hash = "OJF6H4KdxFLgLu+oTDNFodCEfMA=" binsalt = b64decode(b64salt) password_string = 'password' m1 = hashlib.sha1() # Pass in salt m1.update(binsalt) # Pass in password m1.update(password_string) # B64 encode the binary digest if b64encode(m1.digest()) == b64hash: print "Logged in!" else: print "Didn't match" print b64hash print b64encode(m1.digest()) 

I am wondering if anyone can see any flaws in my approach or suggest an alternative method. Perhaps you can take the above algorithms and a well-known password and salt and create a hash in your system?

+6
python passwords hash
source share
2 answers

It seems like python inserts a byte order marker when converting a UTF16 string to binary. The .NET byte array does not contain a specification, so I made some python ghetto python that turns UTF16 into hexadecimal, removes the first 4 characters, and then decodes it into a binary file.

There may be a better way to snatch a spec, but it works for me!

Here is one that goes by:

 import hashlib from base64 import b64decode, b64encode def utf16tobin(s): return s.encode('hex')[4:].decode('hex') b64salt = "kDP0Py2QwEdJYtUX9cJABg==" b64hash = "OJF6H4KdxFLgLu+oTDNFodCEfMA=" binsalt = b64decode(b64salt) password_string = 'password'.encode("utf16") password_string = utf16tobin(password_string) m1 = hashlib.sha1() # Pass in salt m1.update(binsalt + password_string) # Pass in password # B64 encode the binary digest if b64encode(m1.digest()) == b64hash: print "Logged in!" else: print "Didn't match" print b64hash print b64encode(m1.digest()) 
+8
source share

Two thoughts on what might go wrong.

First, the code from reflection has three paths:

  • If passwordFormat is 0, it returns the password as is.
  • If passwordFormat is 1, it creates a hash like your Python code.
  • If passwordFormat is something other than 0 or 1, it calls this.EncryptPassword ()

How do you know that you haveh the password and not encrypt the password with this .EncryptPassword ()? You may need to modify the member function of EncryptPassword () and copy it. That is, if you do not have any information that guarantees that you have the password hashed, and not encrypt it.

Secondly, if this is really a password hash, you can see that the Encoding.Unicode.GetBytes () function returns a string for the password, since you can get something like:

 0x00 0x70 0x00 0x61 0x00 0x73 0x00 0x73 0x00 0x77 0x00 0x6F 0x00 0x72 0x00 0x64 

instead:

 0x70 0x61 0x73 0x73 0x77 0x6F 0x72 0x64 

Hope this helps.

0
source share

All Articles