COMMAND

    Encryption of passwords

SYSTEMS AFFECTED

    Cisco

PROBLEM

    When  "service  password-encryption"  is  configured  into a cisco
    router and  the configuration  subsequently viewed,  the passwords
    are  no   longer  printed   as  plaintext   but  as   strings   of
    randomish-looking garbage.   Analysis of  several samples  reveals
    the scrambling algorithm to be trivially weak.

    Passwords  can  be  up  to  eleven  mixed-case characters.  In the
    "encrypted" representation, the first two bytes of the long string
    are a random decimal offset between 0 and 15 into a magic block of
    characters, and the remaining bytes are ascii-hex  representations
    of the password bytes xored against the character-block bytes from
    the   given   offset   on   down.     The   character   block   is
    "dsfd;kfoA,.iyewrkldJKDHSUB", which is enough for a maximum-length
    password at the maximum offset.

    Another        character        block        consisting         of
    "sgvca69834ncxv9873254k;fg87" is  located after  the first  one in
    the IOS  image, which  may be  relevant to  something else  and is
    simply mentioned here  for posterity.   It is also  interesting to
    note  that  the  strings  "%02d"  and  "%02x"  occur   immediately
    afterward, which in light of the above is another clue.

    For those who want to save the time here's what cisco is doing  in
    words of .mudge.  Assume the following:

	Password 7 08204E

    The  encrypted  string  is  08204E.  It  must be an even length of
    digits and the first two digits  are used as a base 10  index into
    the  XOR  string.  The  length   of  the  plaintext  password   is
    strlen(enc_pw) -2 / 2. In this case 2 chars.

    08 is the index into the xor string.

    2 is  multiplied by  16 (or  left shifted  4 times)  then the next
    digit (0) is added to it. [ == 32]

    32 XOR xorstring[08] = 'a'

    Move to the next two digits and repeat -

	4 * 16 = 64
	64 + 14 (E) = 78
	increment the index into the xor string
	78 XOR xorstring[08] = 'b'

    In a couple  of days you  should be able  to download a  PalmPilot
    version of this  and a FORTH  interpretation with instructions  to
    put it into your OpenBoot prom on SPARCs from the l0pht.

    John Bashinski from  CISCO posted more  information.  A  non-Cisco
    source  has  recently  released  a  new  program  to  decrypt user
    passwords (and other passwords) in Cisco configuration files.  The
    program will not  decrypt passwords set  with the "enable  secret"
    command.

    User passwords and most other passwords (*not* enable secrets)  in
    Cisco IOS configuration files are encrypted using a scheme  that's
    very weak by modern cryptographic standards.  Although Cisco  does
    not  distribute  a  decryption  program,  at  least  two different
    decryption programs for Cisco  IOS passwords are available  to the
    public on the Internet.  It is expected any amateur  cryptographer
    to be able to create a new program with no more than a few  hours'
    work.

    The scheme used  by IOS for  user passwords was  never intended to
    resist a determined, intelligent attack; it was designed to  avoid
    casual "over-the-shoulder"  password theft.  The threat  model was
    someone reading  a password  from an  administrator's screen.  The
    scheme was never supposed to protect against someone conducting  a
    determined analysis of the configuration file.

    Enable  secrets  are  hashed  using  the  MD5 algorithm. As far as
    anyone  at  Cisco  knows,  it  is  impossible to recover an enable
    secret based on the contents  of a configuration file (other  than
    by obvious dictionary  attacks).  Note  that this applies  only to
    passwords set with  "enable secret", *not*  to passwords set  with
    "enable password". Indeed, the strength of the encryption used  is
    the only significant difference between the two commands.

    Almost all passwords and other authentication strings in Cisco IOS
    configuration  files  are  encrypted  using  the  weak, reversible
    scheme  used  for  user  passwords.  To determine which scheme has
    been  used  to  encrypt  a  specific  password,  check  the  digit
    preceding the encrypted string in the configuration file. If  that
    digit  is  a  7,  the  password  has been encrypted using the weak
    algorithm. If the digit is a 5, the password has been hashed using
    the stronger MD5 algorithm.

    For example, in the configuration command

	enable secret 5 $1$iUjJ$cDZ03KKGh7mHfX2RSbDqP.

    the enable secret has been hashed with MD5, whereas in the command

	username jbash password 7 07362E590E1B1C041B1E124C0A2F2E206832752E1A01134D

    the  password  has  been  encrypted  using  the  weak   reversible
    algorithm.

    AcidGum posted following  program that decrypts  cisco "encrypted"
    passwords.  Feed this confg files as stdin.   Anything that  looks
    like a  "type 7  encrypted" string  gets decrypted.    This should
    really be a C  program, but is presented  as a script.   The shell
    script that was posted was originally written by Hobbit:

    #! /bin/sh

    while read xx ; do
      case "$xx" in
	*d\ 7\ [01]??* ) ;;
	*) continue ;;
      esac
      DEC=`echo "$xx" | sed -e 's/.* //' -e 's/\(^..\).*/\1/'`
      DP1=`expr $DEC + 1`
      HEX=`echo "$xx" | sed -e 's/.* //' -e 's/^..\(..*\)/\1/'`
      echo 'dsfd;kfoA,.iyewrkldJKDHSUB' | cut -c "${DP1}-30" >
    /tmp/cis$$.pad
      echo '#' > /tmp/cis$$.in
      for xx in 1-2 3-4 5-6 7-8 9-10 11-12 13-14 15-16 17-18 19-20 21-22 ;
    do
	echo "${HEX}" | cut -c $xx | sed -e '/^$/q' -e 's/^/0x/' >>
    /tmp/cis$$.in
      done
      echo -n "${DEC}${HEX}: "
      data -g < /tmp/cis$$.in | xor /tmp/cis$$.pad
      echo ''
    done
    rm -f /tmp/cis$$.pad /tmp/cis$$.in
    exit 0

    Following is C code that originally came from SPHiXe:

    #include <stdio.h>
    #include <ctype.h>

    char xlat[] = {
	0x64, 0x73, 0x66, 0x64, 0x3b, 0x6b, 0x66, 0x6f,
	0x41, 0x2c, 0x2e, 0x69, 0x79, 0x65, 0x77, 0x72,
	0x6b, 0x6c, 0x64, 0x4a, 0x4b, 0x44, 0x48, 0x53 , 0x55, 0x42
    };

    char pw_str1[] = " password 7 ";
    char pw_str2[] = "enable password 7 ";
    char pw_str3[] = "ip ftp password 7 ";
    char pw_str4[] = " ip ospf message-digest-key 1 md5 7 ";

    char *pname;

    cdecrypt(enc_pw, dec_pw)
    char *enc_pw;
    char *dec_pw;
    {
	    unsigned int seed, i, val = 0;

	    if(strlen(enc_pw) & 1)
		    return(-1);

	    seed = (enc_pw[0] - '0') * 10 + enc_pw[1] - '0';

	    if (seed > 15 || !isdigit(enc_pw[0]) || !isdigit(enc_pw[1]))
		    return(-1);

	    for (i = 2 ; i <= strlen(enc_pw); i++) {
		    if(i !=2 && !(i & 1)) {
			    dec_pw[i / 2 - 2] = val ^ xlat[seed++];
			    val = 0;
		    }

		    val *= 16;

		    if(isdigit(enc_pw[i] = toupper(enc_pw[i]))) {
			    val += enc_pw[i] - '0';
			    continue;
		    }

		    if(enc_pw[i] >= 'A' && enc_pw[i] <= 'F') {
			    val += enc_pw[i] - 'A' + 10;
			    continue;
		    }

		    if(strlen(enc_pw) != i)
			    return(-1);
	    }

	    dec_pw[++i / 2] = 0;

	    return(0);
    }

    usage()
    {
	    fprintf(stdout, "Usage: %s -p <encrypted password>\n", pname);
	    fprintf(stdout, "       %s <router config file> <output file>\n", pname);

	    return(0);
    }

    main(argc,argv)
    int argc;
    char **argv;

    {
	    FILE *in = stdin, *out = stdout;
	    char line[257];
	    char passwd[65];
	    unsigned int i, pw_pos;

	    pname = argv[0];

	    if(argc > 1)
	    {
		    if(argc > 3) {
			    usage();
			    exit(1);
		    }

		    if(argv[1][0] == '-')
		    {
			    switch(argv[1][1]) {
				    case 'h':
				    usage();
				    break;

				    case 'p':
				    bzero(passwd, sizeof(passwd));
				    if(cdecrypt(argv[2], passwd)) {
					    fprintf(stderr, "Error.\n");
					    exit(1);
				    }
				    fprintf(stdout, "password: %s\n", passwd);
				    break;

				    default:
				    fprintf(stderr, "%s: unknow option.", pname);
			    }

			    return(0);
		    }

		    if((in = fopen(argv[1], "rt")) == NULL)
			    exit(1);
		    if(argc > 2)
			    if((out = fopen(argv[2], "wt")) == NULL)
				    exit(1);
	    }

	    while(1) {
		    for(i = 0; i < 256; i++) {
			    if((line[i] = fgetc(in)) == EOF) {
				    if(i)
					    break;

				    fclose(in);
				    fclose(out);
				    return(0);
			    }
			    if(line[i] == '\r')
				    i--;

			    if(line[i] == '\n')
				    break;
		    }
		    pw_pos = 0;
		    line[i] = 0;

		    if(!strncmp(line, pw_str1, strlen(pw_str1)))
			    pw_pos = strlen(pw_str1);

		    if(!strncmp(line, pw_str2, strlen(pw_str2)))
			    pw_pos = strlen(pw_str2);
		    if(!strncmp(line, pw_str3, strlen(pw_str3)))
			    pw_pos = strlen(pw_str3);
		    if(!strncmp(line, pw_str4, strlen(pw_str4)))
			    pw_pos = strlen(pw_str4);

		    if(!pw_pos) {
			    fprintf(stdout, "%s\n", line);
			    continue;
		    }

		    bzero(passwd, sizeof(passwd));
		    if(cdecrypt(&line[pw_pos], passwd)) {
			    fprintf(stderr, "Error.\n");
			    exit(1);
		    }
		    else {
			    if(pw_pos == strlen(pw_str1))
				    fprintf(out, "%s", pw_str1);
			    else if (pw_pos == strlen(pw_str2))
				    fprintf(out, "%s", pw_str2);
			    else if (pw_pos == strlen(pw_str3))
				    fprintf(out, "%s", pw_str3);
			    else if (pw_pos == strlen(pw_str4))
				    fprintf(out, "%s", pw_str4);


			    fprintf(out, "%s\n", passwd);
		    }
	    }
    }

    And following is a perl script:

    #!/usr/bin/perl -w
    # $Id: ios7decrypt.pl,v 1.3 1998/01/11 21:31:12 mesrik Exp $
    #
    # Credits for orginal code and description hobbit@avian.org,
    # SPHiXe, .mudge et al. and for John Bashinski <jbash@CISCO.COM>
    # for Cisco IOS password encryption facts.
    #
    # Use for any malice or illegal purposes strictly prohibited!
    #

    @xlat = ( 0x64, 0x73, 0x66, 0x64, 0x3b, 0x6b, 0x66, 0x6f, 0x41,
	      0x2c, 0x2e, 0x69, 0x79, 0x65, 0x77, 0x72, 0x6b, 0x6c,
	      0x64, 0x4a, 0x4b, 0x44, 0x48, 0x53 , 0x55, 0x42 );

    while (<>) {
	    if (/(password|md5)\s+7\s+([\da-f]+)/io) {
		if (!(length($2) & 1)) {
		    $ep = $2; $dp = "";
		    ($s, $e) = ($2 =~ /^(..)(.+)/o);
		    for ($i = 0; $i < length($e); $i+=2) {
			$dp .= sprintf "%c",hex(substr($e,$i,2))^$xlat[$s++];
		    }
		   s/7\s+$ep/$dp/;
		}
	    }
	    print;
    }
    # eof

SOLUTION

    No solution.  This is how  things stand.  After all, if  you can't
    keep  eye  on  your  router,  someone  else  will...  Cisco has no
    immediate plans to support a stronger encryption algorithm for IOS
    user passwords.

    It is not, in the general case, possible to switch user  passwords
    over to the MD5-based  algorithm used for enable  secrets, because
    MD5 is a  one-way hash, and  the password can't  be recovered from
    the  encrypted  data  at  all.    In  order  to  support   certain
    authentication protocols (notably  CHAP), the system  needs access
    to the  clear text  of user  passwords, and  therefore must  store
    them using a reversible algorithm.

    Key management issues  would make it  a nontrivial task  to switch
    over to a stronger reversible algorithm, such as DES. Although  it
    would  be  easy  to  modify  IOS  to use DES to encrypt passwords,
    there  would  be  no  security  advantage  in  doing so if all IOS
    systems used the  same DES key.   If different keys  were used  by
    different systems,  an administrative  burden would  be introduced
    for   all   IOS   network   administrators,   and  portability  of
    configuration files between systems would be damaged.