Beispiel #1
0
    def _calc_checksum(self, secret):
        if isinstance(secret, unicode):
            # XXX: no idea what unicode policy is, but all examples are
            # 7-bit ascii compatible, so using UTF-8
            secret = secret.encode("utf-8")

        user = self.user
        if user:
            # NOTE: not *positive* about this, but it looks like per-user
            # accounts use first 4 chars of user as salt, whereas global
            # "enable" passwords don't have any salt at all.
            if isinstance(user, unicode):
                user = user.encode("utf-8")
            secret += user[:4]

        # pad/truncate to 16
        secret = right_pad_string(secret, 16)

        # md5 digest
        hash = md5(secret).digest()

        # drop every 4th byte
        hash = join_byte_elems(c for i,c in enumerate(hash) if i & 3 < 3)

        # encode using Hash64
        return h64.encode_bytes(hash).decode("ascii")
Beispiel #2
0
    def _calc_checksum(self, secret):
        if isinstance(secret, unicode):
            # XXX: no idea what unicode policy is, but all examples are
            # 7-bit ascii compatible, so using UTF-8
            secret = secret.encode("utf-8")

        user = self.user
        if user:
            # not positive about this, but it looks like per-user
            # accounts use the first 4 chars of the username as the salt,
            # whereas global "enable" passwords don't have any salt at all.
            if isinstance(user, unicode):
                user = user.encode("utf-8")
            secret += user[:4]

        # null-pad or truncate to 16 bytes
        secret = right_pad_string(secret, 16)

        # md5 digest
        hash = md5(secret).digest()

        # drop every 4th byte
        hash = join_byte_elems(c for i, c in enumerate(hash) if i & 3 < 3)

        # encode using Hash64
        return h64.encode_bytes(hash).decode("ascii")
Beispiel #3
0
    def _calc_checksum(self, secret):

        # This function handles both the cisco_pix & cisco_asa formats:
        #   * PIX had a limit of 16 character passwords, and always appended the username.
        #   * ASA 7.0 (2005) increases this limit to 32, and conditionally appends the username.
        # The two behaviors are controlled based on the _is_asa class-level flag.
        asa = self._is_asa

        # XXX: No idea what unicode policy is, but all examples are
        #      7-bit ascii compatible, so using UTF-8.
        if isinstance(secret, unicode):
            secret = secret.encode("utf-8")
        seclen = len(secret)

        # check for truncation (during .hash() calls only)
        if self.use_defaults:
            self._check_truncate_policy(secret)

        # PIX/ASA: Per-user accounts use the first 4 chars of the username as the salt,
        #          whereas global "enable" passwords don't have any salt at all.
        # ASA only: Don't append user if password is 28 or more characters.
        user = self.user
        if user and not (asa and seclen > 27):
            if isinstance(user, unicode):
                user = user.encode("utf-8")
            secret += user[:4]

        # PIX: null-pad or truncate to 16 bytes.
        # ASA: increase to 32 bytes if password is 13 or more characters.
        if asa and seclen > 12:
            padsize = 32
        else:
            padsize = 16
        secret = right_pad_string(secret, padsize)

        # md5 digest
        hash = md5(secret).digest()

        # drop every 4th byte
        hash = join_byte_elems(c for i,c in enumerate(hash) if i & 3 < 3)

        # encode using Hash64
        return h64.encode_bytes(hash).decode("ascii")
    def raw(cls, secret, encoding=None):
        """encode password using LANMAN hash algorithm.

        :type secret: unicode or utf-8 encoded bytes
        :arg secret: secret to hash
        :type encoding: str
        :arg encoding:
            optional encoding to use for unicode inputs.
            this defaults to ``cp437``, which is the
            common case for most situations.

        :returns: returns string of raw bytes
        """
        if not encoding:
            encoding = cls.default_encoding
        # some nice empircal data re: different encodings is at...
        # http://www.openwall.com/lists/john-dev/2011/08/01/2
        # http://www.freerainbowtables.com/phpBB3/viewtopic.php?t=387&p=12163
        from passlib.crypto.des import des_encrypt_block

        MAGIC = cls._magic
        if isinstance(secret, unicode):
            # perform uppercasing while we're still unicode,
            # to give a better shot at getting non-ascii chars right.
            # (though some codepages do NOT upper-case the same as unicode).
            secret = secret.upper().encode(encoding)
        elif isinstance(secret, bytes):
            # FIXME: just trusting ascii upper will work?
            # and if not, how to do codepage specific case conversion?
            # we could decode first using <encoding>,
            # but *that* might not always be right.
            secret = secret.upper()
        else:
            raise TypeError("secret must be unicode or bytes")
        secret = right_pad_string(secret, 14)
        return des_encrypt_block(secret[0:7], MAGIC) + des_encrypt_block(
            secret[7:14], MAGIC)
Beispiel #5
0
    def raw(cls, secret, encoding=None):
        """encode password using LANMAN hash algorithm.

        :type secret: unicode or utf-8 encoded bytes
        :arg secret: secret to hash
        :type encoding: str
        :arg encoding:
            optional encoding to use for unicode inputs.
            this defaults to ``cp437``, which is the
            common case for most situations.

        :returns: returns string of raw bytes
        """
        if not encoding:
            encoding = cls.default_encoding
        # some nice empircal data re: different encodings is at...
        # http://www.openwall.com/lists/john-dev/2011/08/01/2
        # http://www.freerainbowtables.com/phpBB3/viewtopic.php?t=387&p=12163
        from passlib.crypto.des import des_encrypt_block
        MAGIC = cls._magic
        if isinstance(secret, unicode):
            # perform uppercasing while we're still unicode,
            # to give a better shot at getting non-ascii chars right.
            # (though some codepages do NOT upper-case the same as unicode).
            secret = secret.upper().encode(encoding)
        elif isinstance(secret, bytes):
            # FIXME: just trusting ascii upper will work?
            # and if not, how to do codepage specific case conversion?
            # we could decode first using <encoding>,
            # but *that* might not always be right.
            secret = secret.upper()
        else:
            raise TypeError("secret must be unicode or bytes")
        secret = right_pad_string(secret, 14)
        return des_encrypt_block(secret[0:7], MAGIC) + \
               des_encrypt_block(secret[7:14], MAGIC)
Beispiel #6
0
    def _calc_checksum(self, secret):
        """
        This function implements the "encrypted" hash format used by Cisco
        PIX & ASA. It's behavior has been confirmed for ASA 9.6,
        but is presumed correct for PIX & other ASA releases,
        as it fits with known test vectors, and existing literature.

        While nearly the same, the PIX & ASA hashes have slight differences,
        so this function performs differently based on the _is_asa class flag.
        Noteable changes from PIX to ASA include password size limit
        increased from 16 -> 32, and other internal changes.
        """
        # select PIX vs or ASA mode
        asa = self._is_asa

        #
        # encode secret
        #
        # per ASA 8.4 documentation,
        # http://www.cisco.com/c/en/us/td/docs/security/asa/asa84/configuration/guide/asa_84_cli_config/ref_cli.html#Supported_Character_Sets,
        # it supposedly uses UTF-8 -- though some double-encoding issues have
        # been observed when trying to actually *set* a non-ascii password
        # via ASDM, and access via SSH seems to strip 8-bit chars.
        #
        if isinstance(secret, unicode):
            secret = secret.encode("utf-8")

        #
        # check if password too large
        #
        # Per ASA 9.6 changes listed in
        # http://www.cisco.com/c/en/us/td/docs/security/asa/roadmap/asa_new_features.html,
        # prior releases had a maximum limit of 32 characters.
        # Testing with an ASA 9.6 system bears this out --
        # setting 32-char password for a user account,
        # and logins will fail if any chars are appended.
        # (ASA 9.6 added new PBKDF2-based hash algorithm,
        #  which supports larger passwords).
        #
        # Per PIX documentation
        # http://www.cisco.com/en/US/docs/security/pix/pix50/configuration/guide/commands.html,
        # it would not allow passwords > 16 chars.
        #
        # Thus, we unconditionally throw a password size error here,
        # as nothing valid can come from a larger password.
        # NOTE: assuming PIX has same behavior, but at 16 char limit.
        #
        spoil_digest = None
        if len(secret) > self.truncate_size:
            if self.use_defaults:
                # called from hash()
                msg = "Password too long (%s allows at most %d bytes)" % (
                    self.name,
                    self.truncate_size,
                )
                raise uh.exc.PasswordSizeError(self.truncate_size, msg=msg)
            else:
                # called from verify() --
                # We don't want to throw error, or return early,
                # as that would let attacker know too much.  Instead, we set a
                # flag to add some dummy data into the md5 digest, so that
                # output won't match truncated version of secret, or anything
                # else that's fixed and predictable.
                spoil_digest = secret + _DUMMY_BYTES

        #
        # append user to secret
        #
        # Policy appears to be:
        #
        # * Nothing appended for enable password (user = "")
        #
        # * ASA: If user present, but secret is >= 28 chars, nothing appended.
        #
        # * 1-2 byte users not allowed.
        #   DEVIATION: we're letting them through, and repeating their
        #   chars ala 3-char user, to simplify testing.
        #   Could issue warning in the future though.
        #
        # * 3 byte user has first char repeated, to pad to 4.
        #   (observed under ASA 9.6, assuming true elsewhere)
        #
        # * 4 byte users are used directly.
        #
        # * 5+ byte users are truncated to 4 bytes.
        #
        user = self.user
        if user:
            if isinstance(user, unicode):
                user = user.encode("utf-8")
            if not asa or len(secret) < 28:
                secret += repeat_string(user, 4)

        #
        # pad / truncate result to limit
        #
        # While PIX always pads to 16 bytes, ASA increases to 32 bytes IFF
        # secret+user > 16 bytes.  This makes PIX & ASA have different results
        # where secret size in range(13,16), and user is present --
        # PIX will truncate to 16, ASA will truncate to 32.
        #
        if asa and len(secret) > 16:
            pad_size = 32
        else:
            pad_size = 16
        secret = right_pad_string(secret, pad_size)

        #
        # md5 digest
        #
        if spoil_digest:
            # make sure digest won't match truncated version of secret
            secret += spoil_digest
        digest = md5(secret).digest()

        #
        # drop every 4th byte
        # NOTE: guessing this was done because it makes output exactly
        #       16 bytes, which may have been a general 'char password[]'
        #       size limit under PIX
        #
        digest = join_byte_elems(c for i, c in enumerate(digest)
                                 if (i + 1) & 3)

        #
        # encode using Hash64
        #
        return h64.encode_bytes(digest).decode("ascii")
Beispiel #7
0
    def _calc_checksum(self, secret):
        """
        This function implements the "encrypted" hash format used by Cisco
        PIX & ASA. It's behavior has been confirmed for ASA 9.6,
        but is presumed correct for PIX & other ASA releases,
        as it fits with known test vectors, and existing literature.

        While nearly the same, the PIX & ASA hashes have slight differences,
        so this function performs differently based on the _is_asa class flag.
        Noteable changes from PIX to ASA include password size limit
        increased from 16 -> 32, and other internal changes.
        """
        # select PIX vs or ASA mode
        asa = self._is_asa

        #
        # encode secret
        #
        # per ASA 8.4 documentation,
        # http://www.cisco.com/c/en/us/td/docs/security/asa/asa84/configuration/guide/asa_84_cli_config/ref_cli.html#Supported_Character_Sets,
        # it supposedly uses UTF-8 -- though some double-encoding issues have
        # been observed when trying to actually *set* a non-ascii password
        # via ASDM, and access via SSH seems to strip 8-bit chars.
        #
        if isinstance(secret, unicode):
            secret = secret.encode("utf-8")

        #
        # check if password too large
        #
        # Per ASA 9.6 changes listed in
        # http://www.cisco.com/c/en/us/td/docs/security/asa/roadmap/asa_new_features.html,
        # prior releases had a maximum limit of 32 characters.
        # Testing with an ASA 9.6 system bears this out --
        # setting 32-char password for a user account,
        # and logins will fail if any chars are appended.
        # (ASA 9.6 added new PBKDF2-based hash algorithm,
        #  which supports larger passwords).
        #
        # Per PIX documentation
        # http://www.cisco.com/en/US/docs/security/pix/pix50/configuration/guide/commands.html,
        # it would not allow passwords > 16 chars.
        #
        # Thus, we unconditionally throw a password size error here,
        # as nothing valid can come from a larger password.
        # NOTE: assuming PIX has same behavior, but at 16 char limit.
        #
        spoil_digest = None
        if len(secret) > self.truncate_size:
            if self.use_defaults:
                # called from hash()
                msg = "Password too long (%s allows at most %d bytes)" % \
                      (self.name, self.truncate_size)
                raise uh.exc.PasswordSizeError(self.truncate_size, msg=msg)
            else:
                # called from verify() --
                # We don't want to throw error, or return early,
                # as that would let attacker know too much.  Instead, we set a
                # flag to add some dummy data into the md5 digest, so that
                # output won't match truncated version of secret, or anything
                # else that's fixed and predictable.
                spoil_digest = secret + _DUMMY_BYTES

        #
        # append user to secret
        #
        # Policy appears to be:
        #
        # * Nothing appended for enable password (user = "")
        #
        # * ASA: If user present, but secret is >= 28 chars, nothing appended.
        #
        # * 1-2 byte users not allowed.
        #   DEVIATION: we're letting them through, and repeating their
        #   chars ala 3-char user, to simplify testing.
        #   Could issue warning in the future though.
        #
        # * 3 byte user has first char repeated, to pad to 4.
        #   (observed under ASA 9.6, assuming true elsewhere)
        #
        # * 4 byte users are used directly.
        #
        # * 5+ byte users are truncated to 4 bytes.
        #
        user = self.user
        if user:
            if isinstance(user, unicode):
                user = user.encode("utf-8")
            if not asa or len(secret) < 28:
                secret += repeat_string(user, 4)

        #
        # pad / truncate result to limit
        #
        # While PIX always pads to 16 bytes, ASA increases to 32 bytes IFF
        # secret+user > 16 bytes.  This makes PIX & ASA have different results
        # where secret size in range(13,16), and user is present --
        # PIX will truncate to 16, ASA will truncate to 32.
        #
        if asa and len(secret) > 16:
            pad_size = 32
        else:
            pad_size = 16
        secret = right_pad_string(secret, pad_size)

        #
        # md5 digest
        #
        if spoil_digest:
            # make sure digest won't match truncated version of secret
            secret += spoil_digest
        digest = md5(secret).digest()

        #
        # drop every 4th byte
        # NOTE: guessing this was done because it makes output exactly
        #       16 bytes, which may have been a general 'char password[]'
        #       size limit under PIX
        #
        digest = join_byte_elems(c for i, c in enumerate(digest) if (i + 1) & 3)

        #
        # encode using Hash64
        #
        return h64.encode_bytes(digest).decode("ascii")