summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2012-03-10 17:35:11 -0500
committerEli Collins <elic@assurancetechnologies.com>2012-03-10 17:35:11 -0500
commit50965db6ee2b6ff2c9227ea6c740e9513963c4f6 (patch)
tree6b40f902cd587ac7a431af2bc79778fea808053a
parente84f3f69e55f407a935e9f80c7e41d81b3ea63b7 (diff)
downloadpasslib-50965db6ee2b6ff2c9227ea6c740e9513963c4f6.tar.gz
added Window's DCC hashes (aka mscache / mscash) version 1 & 2
-rw-r--r--CHANGES5
-rw-r--r--docs/lib/passlib.hash.msdcc.rst101
-rw-r--r--docs/lib/passlib.hash.msdcc2.rst91
-rw-r--r--docs/lib/passlib.hash.rst16
-rw-r--r--passlib/handlers/windows.py (renamed from passlib/handlers/nthash.py)97
-rw-r--r--passlib/registry.py8
-rw-r--r--passlib/tests/test_handlers.py93
7 files changed, 405 insertions, 6 deletions
diff --git a/CHANGES b/CHANGES
index 21efacc..1473979 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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):