COMMAND
pw suite
SYTEM AFFECTED
FreeBSD
PROBLEM
The FreeBSD account administration pw suite is able to produce
"random" passwords for new accounts. Due to the simplicity of the
password generation algorithm involved, the passwords are easily
predictable amid a particular range of possibilities. This range
may be very narrow, depending on what sort of information is
available to the attacker.
The pid and the current time in seconds are ored (NOT xored)
together and used as the seed. Because of this, the probability
of a lower bit in the seed of being on is 3/4. The msb's of
the seed change very slowly and predictably over time and in
effect contain no entropy.
If the attacker has access to the file system then the time the
account was created on can by guessed very accurately by
examining the creation date of the new account's directory, or
the creation date of the new account's mail spool file.
With the time part of the seed known it is then possible to
generate an a mask to eliminate 50% of the remaining possible
pid values. The pid can be further approximated retrospectively
by correlation between time stamps and pid values from a wide
number of file system sources such as the pid of the sendmail
task used to deliver the new-user mail, to names of /tmp and
queue files. An active attack (looking for the "pw" process or
other signs of account creation) will of course locate the
pid very closely, if not exactly.
SOLUTION
Patch addresses the problem is given by Julian A.
(proff@suburbia.net) who originally described this vulnerability.
--- /usr/src/usr.sbin/pw/pw_user.c.orig Thu Dec 12 02:10:47 1996
+++ /usr/src/usr.sbin/pw/pw_user.c Sat Dec 14 11:37:50 1996
@@ -33,6 +33,10 @@
#include <sys/param.h>
#include <dirent.h>
#include <termios.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <md5.h>
#include "pw.h"
#include "bitmap.h"
#include "pwupd.h"
@@ -738,19 +742,61 @@
return strcpy(buf, crypt(password, salt));
}
+u_char *
+pw_genmd5rand (u_char *d) /* cryptographically secure rng */
+{
+ MD5_CTX md5_ctx;
+ struct timeval tv, tvo;
+ struct rusage ru;
+ int n=0;
+ int t;
+ MD5Init (&md5_ctx);
+ t=getpid();
+ MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
+ t=getppid();
+ MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
+ gettimeofday (&tvo, NULL);
+ do {
+ getrusage (RUSAGE_SELF, &ru);
+ MD5Update (&md5_ctx, (u_char*)&ru, sizeof ru);
+ gettimeofday (&tv, NULL);
+ MD5Update (&md5_ctx, (u_char*)&tv, sizeof tv);
+ } while (n++<20 || tv.tv_usec-tvo.tv_usec< 100*1000);
+ MD5Final (d, &md5_ctx);
+ return d;
+}
+
+static u_char *
+pw_getrand(u_char *buf, int len)
+{
+ int fd;
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1 || read(fd, buf, len)!=len) {
+ int n;
+ for (n=0;n<len;n+=16) {
+ u_char ubuf[16];
+ pw_genmd5rand(ubuf);
+ memcpy(buf+n, ubuf, MIN(16, len-n));
+ }
+ }
+ close(fd);
+ return buf;
+}
static char *
pw_password(struct userconf * cnf, struct cargs * args, char const * user)
{
int i, l;
char pwbuf[32];
+ u_char rndbuf[sizeof pwbuf];
switch (cnf->default_password) {
case -1: /* Random password */
srandom((unsigned) (time(NULL) | getpid()));
l = (random() % 8 + 8); /* 8 - 16 chars */
+ pw_getrand(rndbuf, l);
for (i = 0; i<l; i++)
- pwbuf[i] = chars[random() % sizeof(chars)];
+ pwbuf[i] = chars[rndbuf[i] % sizeof(chars)];
pwbuf[i] = '\0';
/*