diff options
| author | Eli Collins <elic@assurancetechnologies.com> | 2012-03-10 17:35:11 -0500 |
|---|---|---|
| committer | Eli Collins <elic@assurancetechnologies.com> | 2012-03-10 17:35:11 -0500 |
| commit | 50965db6ee2b6ff2c9227ea6c740e9513963c4f6 (patch) | |
| tree | 6b40f902cd587ac7a431af2bc79778fea808053a | |
| parent | e84f3f69e55f407a935e9f80c7e41d81b3ea63b7 (diff) | |
| download | passlib-50965db6ee2b6ff2c9227ea6c740e9513963c4f6.tar.gz | |
added Window's DCC hashes (aka mscache / mscash) version 1 & 2
| -rw-r--r-- | CHANGES | 5 | ||||
| -rw-r--r-- | docs/lib/passlib.hash.msdcc.rst | 101 | ||||
| -rw-r--r-- | docs/lib/passlib.hash.msdcc2.rst | 91 | ||||
| -rw-r--r-- | docs/lib/passlib.hash.rst | 16 | ||||
| -rw-r--r-- | passlib/handlers/windows.py (renamed from passlib/handlers/nthash.py) | 97 | ||||
| -rw-r--r-- | passlib/registry.py | 8 | ||||
| -rw-r--r-- | passlib/tests/test_handlers.py | 93 |
7 files changed, 405 insertions, 6 deletions
@@ -8,6 +8,11 @@ Release History Hashes + * Added support for Window's Domain Cached Credentials (aka + "dcc", "mscache", "mscash"), versions 1 and 2: + :doc:`msdcc <lib/passlib.hash.msdcc>` and + :doc:`msdcc2 <lib/passlib.hash.msdcc2>`. + * The rarely-used hash previously known as ``nthash`` has been renamed to :class:`~passlib.hash.bsd_nthash`, to better indicate it's lineage; a new :class:`~passlib.hash.nthash` class now directly implements diff --git a/docs/lib/passlib.hash.msdcc.rst b/docs/lib/passlib.hash.msdcc.rst new file mode 100644 index 0000000..737952d --- /dev/null +++ b/docs/lib/passlib.hash.msdcc.rst @@ -0,0 +1,101 @@ +.. index:: msdcc; Windows; Domain Cached Credentials + +====================================================================== +:class:`passlib.hash.msdcc` - Windows' Domain Cached Credentials +====================================================================== + +.. currentmodule:: passlib.hash + +This class implements the DCC (Domain Cached Credentials) hash, used +by Windows to cache and verify remote credentials when the relevant +server is unavailable. It is known by a number of other names, +including "mscache" and "mscash" (Microsoft CAched haSH). Security wise +it is not particularly strong, as it's little more than :doc:`nthash <passlib.hash.nthash>` +salted with a username. It was replaced by :doc:`msdcc2 <passlib.hash.msdcc2>` +in Windows Vista. + +.. warning:: + + This hash is not very secure, and should mainly be used to verify + existing cached credentials. + +.. seealso:: + + :doc:`passlib.hash.msdcc2` + +Usage +===== +This class can be used directly as follows:: + + >>> from passlib.hash import msdcc + + >>> # encrypt password using specified username + >>> h = msdcc.encrypt("password", "Administrator") + >>> h + '25fd08fa89795ed54207e6e8442a6ca0' + + >>> #verify correct password + >>> msdcc.verify("password", h, "Administrator") + True + >>> #verify correct password w/ wrong username + >>> msdcc.verify("password", h, "User") + False + >>> #verify incorrect password + >>> msdcc.verify("letmein", h, "Administrator") + False + + >>> # check if hash may belong to msdcc + >>> msdcc.identify(h) + True + >>> # check if foreign hash belongs to msdcc + >>> msdcc.identify('$1$3azHgidD$SrJPt7B.9rekpmwJwtON31') + False + +Interface +========= +.. autoclass:: msdcc() + +.. rst-class:: html-toggle + +Format & Algorithm +================== +Much like :class:`!lmhash` and :class:`!nthash`, MS DCC hashes +consists of a 16 byte digest, usually encoded as 32 hexidecimal characters. +An example hash (of ``"password"`` with the account ``"Administrator"``) is +``25fd08fa89795ed54207e6e8442a6ca0``. + +The digest is calculated as follows: + +1. The password is encoded using ``UTF-16-LE``. +2. The MD4 digest of step 1 is calculated. + (The result of this step is identical to the :class:`~passlib.hash.nthash` + of the password). +3. The unicode username is converted to lowercase, + and encoded using ``UTF-16-LE``. + This should be just the plain username (e.g. ``User`` + not ``SOMEDOMAIN\\User``) +4. The username from step 3 is appended to the + digest from step 2; and the MD4 digest of the result + is calculated. +5. The result of step 4 is encoded into hexidecimal, + this is the DCC hash. + +Security Issues +=============== +This algorithm is should not be used for any purpose besides +manipulating existing DCC v1 hashes, due to the following flaws: + +* It's use of the username as a salt value (and lower-case at that), + means that common usernames (eg ``Administrator``) will occur + more frequently as salts, weakening the effectiveness of the salt in + foiling pre-computed tables. + +* The MD4 message digest has been severely compromised by collision and + preimage attacks. + +* Efficient brute-force attacks on MD4 exist. + +.. rubric:: Footnotes + +.. [#] Description of DCC v1 algorithm - + `<http://openwall.info/wiki/john/MSCash>`_ diff --git a/docs/lib/passlib.hash.msdcc2.rst b/docs/lib/passlib.hash.msdcc2.rst new file mode 100644 index 0000000..3ff41e6 --- /dev/null +++ b/docs/lib/passlib.hash.msdcc2.rst @@ -0,0 +1,91 @@ +.. index:: msdcc; Windows; Domain Cached Credentials v2 + +====================================================================== +:class:`passlib.hash.msdcc2` - Windows' Domain Cached Credentials v2 +====================================================================== + +.. currentmodule:: passlib.hash + +This class implements the DCC2 (Domain Cached Credentials version 2) hash, used +by Windows Vista and newer to cache and verify remote credentials when the relevant +server is unavailable. It is known by a number of other names, +including "mscache2" and "mscash2" (Microsoft CAched haSH). It replaces +the weaker :doc:`msdcc (v1)<passlib.hash.msdcc>` hash used by previous releases +of Windows. Security wise it is not particularly weak, but due to it's +use of the username as a salt, it should probably not be used for anything +but verifying existing cached credentials. + +Usage +===== +This class can be used directly as follows:: + + >>> from passlib.hash import msdcc2 + + >>> # encrypt password using specified username + >>> h = msdcc2.encrypt("password", "Administrator") + >>> h + '4c253e4b65c007a8cd683ea57bc43c76' + + >>> #verify correct password + >>> msdcc2.verify("password", h, "Administrator") + True + >>> #verify correct password w/ wrong username + >>> msdcc2.verify("password", h, "User") + False + >>> #verify incorrect password + >>> msdcc2.verify("letmein", h, "Administrator") + False + + >>> # check if hash may belong to msdcc + >>> msdcc2.identify(h) + True + >>> # check if foreign hash belongs to msdcc + >>> msdcc2.identify('$1$3azHgidD$SrJPt7B.9rekpmwJwtON31') + False + +Interface +========= +.. autoclass:: msdcc2() + +.. rst-class:: html-toggle + +Format & Algorithm +================== +Much like :class:`!lmhash`, :class:`!nthash`, and :class:`!msdcc`, +MS DCC v2 hashes consists of a 16 byte digest, usually encoded as 32 +hexidecimal characters. An example hash (of ``"password"`` with the +account ``"Administrator"``) is ``4c253e4b65c007a8cd683ea57bc43c76``. + +The digest is calculated as follows: + +1. The password is encoded using ``UTF-16-LE``. +2. The MD4 digest of step 1 is calculated. + (The result of this is identical to the :class:`~passlib.hash.nthash` + digest of the password). +3. The unicode username is converted to lowercase, + and encoded using ``UTF-16-LE``. + This should be just the plain username (e.g. ``User`` + not ``SOMEDOMAIN\\User``) +4. The username from step 3 is appended to the + digest from step 2; and the MD4 digest of the result + is calculated (The result of this is identicial to the + :class:`~passlib.hash.msdcc` digest). +5. :func:`PBKDF2-HMAC-SHA1 <passlib.utils.pbkdf2.pbkdf2>` is then invoked, + using the result of step 4 as the secret, the username from step 3 as + the salt, 10240 rounds, and resulting in a 16 byte digest. +6. The result of step 5 is encoded into hexidecimal; + this is the DCC2 hash. + +Security Issues +=============== +This hash is essentially DCC v1 with a fixed-round PBKDF2 function +wrapped around it. The number of rounds of PBKDF2 is currently +sufficient to make this a semi-reasonable way to store passwords, +but the use of the lowercase username as a salt, and the fact +that the rounds can't be increased, means this hash is not particularly +future-proof, and should not be used for new applications. + +.. rubric:: Footnotes + +.. [#] Description of DCC v2 algorithm - + `<http://openwall.info/wiki/john/MSCash2>`_ diff --git a/docs/lib/passlib.hash.rst b/docs/lib/passlib.hash.rst index fa73060..d06fed1 100644 --- a/docs/lib/passlib.hash.rst +++ b/docs/lib/passlib.hash.rst @@ -183,6 +183,20 @@ not seen outside those specific contexts: .. _other-hashes: +Windows Hashes +============== +The following hashes are used in various places by Microsoft Windows. +As they were designed for "internal" use, they generally contain +no identifying markers, identifying them is pretty much context-dependant. + +.. toctree:: + :maxdepth: 1 + + passlib.hash.lmhash + passlib.hash.nthash + passlib.hash.msdcc + passlib.hash.msdcc2 + Other Hashes ============ The following schemes are used in various contexts, @@ -192,8 +206,6 @@ in one of the above categories: .. toctree:: :maxdepth: 1 - passlib.hash.lmhash - passlib.hash.nthash passlib.hash.cisco_pix * *Cisco "Type 5" hashes* - see :doc:`md5_crypt <passlib.hash.md5_crypt>` diff --git a/passlib/handlers/nthash.py b/passlib/handlers/windows.py index e1ad34f..e4a5199 100644 --- a/passlib/handlers/nthash.py +++ b/passlib/handlers/windows.py @@ -1,4 +1,4 @@ -"""passlib.handlers.nthash - unix-crypt compatible nthash passwords""" +"""passlib.handlers.nthash - Microsoft Windows -related hashes""" #========================================================= #imports #========================================================= @@ -19,6 +19,8 @@ __all__ = [ "lmhash", "nthash", "bsd_nthash", + "msdcc", + "msdcc2", ] #========================================================= @@ -220,5 +222,98 @@ bsd_nthash = uh.PrefixWrapper("bsd_nthash", nthash, prefix="$3$$", ident="$3$$", ## return lmhash.verify(secret, lm) or nthash.verify(secret, nt) #========================================================= +# msdcc v1 +#========================================================= +class msdcc(uh.HasUserContext, uh.StaticHandler): + """This class implements Microsoft's Domain Cached Credentials password hash, + and follows the :ref:`password-hash-api`. + + It has a fixed number of rounds, and uses the associated + username as the salt. + + The :meth:`encrypt()`, :meth:`genhash()`, and :meth:`verify()` methods + have the following optional keywords: + + :param user: + String containing name of user account this password is associated with. + This is required to properly calculate the hash. + + This keyword is case-insensitive, and should contain just the username + (e.g. ``Administrator``, not ``SOMEDOMAIN\\Administrator``). + + Note that while this class outputs lower-case hexidecimal digests, + it will accept upper-case digests as well. + """ + name = "msdcc" + checksum_chars = uh.HEX_CHARS + checksum_size = 32 + + @classmethod + def _norm_hash(cls, hash): + return hash.lower() + + def _calc_checksum(self, secret): + return hexlify(self.raw(secret, self.user)).decode("ascii") + + @classmethod + def raw(cls, secret, user): + """encode password using mscash v1 algorithm + + :arg secret: secret as unicode or utf-8 encoded bytes + :arg user: username to use as salt + + :returns: returns string of raw bytes + """ + secret = to_unicode(secret, "utf-8", errname="secret").encode("utf-16-le") + user = to_unicode(user, "utf-8", errname="user").lower().encode("utf-16-le") + return md4(md4(secret).digest() + user).digest() + +#========================================================= +# msdcc2 aka mscash2 +#========================================================= +class msdcc2(uh.HasUserContext, uh.StaticHandler): + """This class implements version 2 of Microsoft's Domain Cached Credentials + password hash, and follows the :ref:`password-hash-api`. + + It has a fixed number of rounds, and uses the associated + username as the salt. + + The :meth:`encrypt()`, :meth:`genhash()`, and :meth:`verify()` methods + have the following extra keyword: + + :param user: + String containing name of user account this password is associated with. + This is required to properly calculate the hash. + + This keyword is case-insensitive, and should contain just the username + (e.g. ``Administrator``, not ``SOMEDOMAIN\\Administrator``). + """ + name = "msdcc2" + checksum_chars = uh.HEX_CHARS + checksum_size = 32 + + @classmethod + def _norm_hash(cls, hash): + return hash.lower() + + def _calc_checksum(self, secret): + return hexlify(self.raw(secret, self.user)).decode("ascii") + + @classmethod + def raw(cls, secret, user): + """encode password using msdcc v2 algorithm + + :arg secret: secret as unicode or utf-8 encoded bytes + :arg user: username to use as salt + + :returns: returns string of raw bytes + """ + from passlib.utils.pbkdf2 import pbkdf2 + secret = to_unicode(secret, "utf-8", errname="secret").encode("utf-16-le") + user = to_unicode(user, "utf-8", errname="user").lower().encode("utf-16-le") + tmp = md4(md4(secret).digest() + user).digest() + return pbkdf2(tmp, user, 10240, 16, 'hmac-sha1') + +#========================================================= #eof #========================================================= diff --git a/passlib/registry.py b/passlib/registry.py index e39a45d..bf252fd 100644 --- a/passlib/registry.py +++ b/passlib/registry.py @@ -84,7 +84,7 @@ _handler_locations = { ("passlib.handlers.pbkdf2", "atlassian_pbkdf2_sha1"), "bcrypt": ("passlib.handlers.bcrypt", "bcrypt"), "bigcrypt": ("passlib.handlers.des_crypt", "bigcrypt"), - "bsd_nthash": ("passlib.handlers.nthash", "bsd_nthash"), + "bsd_nthash": ("passlib.handlers.windows", "bsd_nthash"), "bsdi_crypt": ("passlib.handlers.des_crypt", "bsdi_crypt"), "cisco_pix": ("passlib.handlers.cisco", "cisco_pix"), "cisco_type7": ("passlib.handlers.cisco", "cisco_type7"), @@ -124,13 +124,15 @@ _handler_locations = { ("passlib.handlers.pbkdf2", "ldap_pbkdf2_sha256"), "ldap_pbkdf2_sha512": ("passlib.handlers.pbkdf2", "ldap_pbkdf2_sha512"), - "lmhash": ("passlib.handlers.nthash", "lmhash"), + "lmhash": ("passlib.handlers.windows", "lmhash"), "md5_crypt": ("passlib.handlers.md5_crypt", "md5_crypt"), + "msdcc": ("passlib.handlers.windows", "msdcc"), + "msdcc2": ("passlib.handlers.windows", "msdcc2"), "mssql2000": ("passlib.handlers.mssql", "mssql2000"), "mssql2005": ("passlib.handlers.mssql", "mssql2005"), "mysql323": ("passlib.handlers.mysql", "mysql323"), "mysql41": ("passlib.handlers.mysql", "mysql41"), - "nthash": ("passlib.handlers.nthash", "nthash"), + "nthash": ("passlib.handlers.windows", "nthash"), "oracle10": ("passlib.handlers.oracle", "oracle10"), "oracle11": ("passlib.handlers.oracle", "oracle11"), "pbkdf2_sha1": ("passlib.handlers.pbkdf2", "pbkdf2_sha1"), diff --git a/passlib/tests/test_handlers.py b/passlib/tests/test_handlers.py index d0555f3..260d011 100644 --- a/passlib/tests/test_handlers.py +++ b/passlib/tests/test_handlers.py @@ -951,6 +951,99 @@ os_crypt_md5_crypt_test = create_backend_case(_md5_crypt_test, "os_crypt") builtin_md5_crypt_test = create_backend_case(_md5_crypt_test, "builtin") #========================================================= +# msdcc 1 & 2 +#========================================================= +class msdcc_test(UserHandlerMixin, HandlerCase): + handler = hash.msdcc + user_case_insensitive = True + + known_correct_hashes = [ + + # + # http://www.jedge.com/wordpress/windows-password-cache/ + # + (("Asdf999", "sevans"), "b1176c2587478785ec1037e5abc916d0"), + + # + # http://infosecisland.com/blogview/12156-Cachedump-for-Meterpreter-in-Action.html + # + (("ASDqwe123", "jdoe"), "592cdfbc3f1ef77ae95c75f851e37166"), + + # + # http://comments.gmane.org/gmane.comp.security.openwall.john.user/1917 + # + (("test1", "test1"), "64cd29e36a8431a2b111378564a10631"), + (("test2", "test2"), "ab60bdb4493822b175486810ac2abe63"), + (("test3", "test3"), "14dd041848e12fc48c0aa7a416a4a00c"), + (("test4", "test4"), "b945d24866af4b01a6d89b9d932a153c"), + + # + # http://ciscoit.wordpress.com/2011/04/13/metasploit-hashdump-vs-cachedump/ + # + (("1234qwer!@#$", "Administrator"), "7b69d06ef494621e3f47b9802fe7776d"), + + # + # http://www.securiteam.com/tools/5JP0I2KFPA.html + # + (("password", "user"), "2d9f0b052932ad18b87f315641921cda"), + + # + # from JTR 1.7.9 + # + (("", "root"), "176a4c2bd45ac73687676c2f09045353"), + (("test1", "TEST1"), "64cd29e36a8431a2b111378564a10631"), + (("okolada", "nineteen_characters"), "290efa10307e36a79b3eebf2a6b29455"), + ((u("\u00FC"), u("\u00FC")), "48f84e6f73d6d5305f6558a33fa2c9bb"), + ((u("\u00FC\u00FC"), u("\u00FC\u00FC")), "593246a8335cf0261799bda2a2a9c623"), + ((u("\u20AC\u20AC"), "user"), "9121790702dda0fa5d353014c334c2ce"), + + # + # custom + # + + # ensures utf-8 used for unicode + ((UPASS_TABLE, 'bob'), 'fcb82eb4212865c7ac3503156ca3f349'), + ] + + known_alternate_hashes = [ + # check uppercase accepted. + ("B1176C2587478785EC1037E5ABC916D0", ("Asdf999", "sevans"), + "b1176c2587478785ec1037e5abc916d0"), + ] + +class msdcc2_test(UserHandlerMixin, HandlerCase): + handler = hash.msdcc2 + user_case_insensitive = True + + known_correct_hashes = [ + # + # from JTR 1.7.9 + # + (("test1", "test1"), "607bbe89611e37446e736f7856515bf8"), + (("qerwt", "Joe"), "e09b38f84ab0be586b730baf61781e30"), + (("12345", "Joe"), "6432f517a900b3fc34ffe57f0f346e16"), + (("", "bin"), "c0cbe0313a861062e29f92ede58f9b36"), + (("w00t", "nineteen_characters"), "87136ae0a18b2dafe4a41d555425b2ed"), + (("w00t", "eighteencharacters"), "fc5df74eca97afd7cd5abb0032496223"), + (("longpassword", "twentyXXX_characters"), "cfc6a1e33eb36c3d4f84e4c2606623d2"), + (("longpassword", "twentyoneX_characters"), "99ff74cea552799da8769d30b2684bee"), + (("longpassword", "twentytwoXX_characters"), "0a721bdc92f27d7fb23b87a445ec562f"), + (("test2", "TEST2"), "c6758e5be7fc943d00b97972a8a97620"), + (("test3", "test3"), "360e51304a2d383ea33467ab0b639cc4"), + (("test4", "test4"), "6f79ee93518306f071c47185998566ae"), + ((u("\u00FC"), "joe"), "bdb80f2c4656a8b8591bd27d39064a54"), + ((u("\u20AC\u20AC"), "joe"), "1e1e20f482ff748038e47d801d0d1bda"), + ((u("\u00FC\u00FC"), "admin"), "0839e4a07c00f18a8c65cf5b985b9e73"), + + # + # custom + # + + # custom unicode test + ((UPASS_TABLE, 'bob'), 'cad511dc9edefcf69201da72efb6bb55'), + ] + +#========================================================= # mssql 2000 & 2005 #========================================================= class mssql2000_test(HandlerCase): |
