6

我们有一个当前应用程序,其中用户登录凭据存储在 SQL Server 数据库中。这些基本上存储为纯文本用户名、密码散列和此散列的关联盐。

这些都是由 ASP.NET 的成员/角色系统中的内置函数创建的。这是名为“joe”的用户和密码“password”的一行:

乔,kDP0Py2QwEdJYtUX9cJABg==,OJF6H4KdxFLgLu+oTDNFodCEfMA=

我已将这些内容转储到 CSV 文件中,并试图将其转换为 Django 的可用格式,该格式以这种格式存储其密码:

[算法]$[盐]$[哈希]

其中盐是纯字符串,散列是 SHA1 散列的十六进制摘要。

到目前为止,我已经能够确定 ASP 正在以 base64 格式存储这些哈希和盐。上面的那些值解码成二进制字符串。

我们使用反射器来收集 ASP 如何根据这些值进行身份验证:

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);
}

本质上,从数据库中提取盐,然后 b64 将其解码为二进制表示。它对原始密码执行“GetBytes”,然后将它们连接起来,首先是盐。

然后它在这个新字符串上运行 SHA1 算法,base64 对其进行编码,并将其与存储在数据库中的值进行比较。

我试图编写一些代码来尝试在 Python 中重现这些哈希值,但我失败了。在我弄清楚它是如何翻译的之前,我将无法在 Django 中使用它们。这是我的测试方式:

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())

我想知道是否有人可以看到我的方法中的任何缺陷或可以提出替代方法。也许您可以使用上面的算法以及上面的已知密码和盐并在您的系统上生成哈希?

4

2 回答 2

8

当您将 UTF16 字符串转换为二进制时,python 似乎正在插入一个字节顺序标记。.NET 字节数组不包含 BOM,所以我做了一些 ghetto python,将 UTF16 转换为十六进制,删除前 4 个字符,然后将其解码为二进制。

可能有更好的方法来删除 BOM,但这对我有用!

这是一个通过:

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())
于 2008-11-06T22:57:12.407 回答
0

关于可能出了什么问题的两个想法。

首先,来自反射的代码具有三个路径:

  • 如果 passwordFormat 为 0,则按原样返回密码。
  • 如果 passwordFormat 为 1,它会像您的 python 代码一样创建哈希。
  • 如果 passwordFormat 不是 0 或 1 它调用 this.EncryptPassword()

你怎么知道你正在散列密码,而不是用 this.EncryptPassword() 加密密码?您可能需要反转 EncryptPassword() 成员函数并复制它。那是除非您有一些信息可以确保您对密码进行哈希处理而不是对其进行加密。

其次,如果它确实在对密码进行哈希处理,您可能希望查看 Encoding.Unicode.GetBytes() 函数为字符串“password”返回的内容,因为您可能会得到类似以下内容:

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

代替:

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

我希望这有帮助。

于 2008-11-06T19:06:58.817 回答