Cryptography-Digest Digest #244, Volume #9 Tue, 16 Mar 99 19:13:03 EST
Contents:
Something, perhaps fun, to decrypt... Give it a try. (Ash)
----------------------------------------------------------------------------
From: [EMAIL PROTECTED] (Ash)
Crossposted-To: rec.puzzles
Subject: Something, perhaps fun, to decrypt... Give it a try.
Date: 16 Mar 1999 23:21:08 GMT
The following is a brute-force client for a cute little encryption
engine that was found in a game on the net the other day.
This is taken off a web page that is giving away a piece of luggage to
anyone/everyone who can get the correct 16 character username and 4
character password for their little web page. The method below is a
cute little byte swapping, xoring method that does not really qualify
as encryption, but hey, it's a cute little bag you get for breaking it.
There are two possible solutions and we have yet to find either, but
the cyphertext for both of them are below (Code1 & Code2) and when
decrypted properly form URLs starting with http://www. So, if you find
the answer you can grab one for yourself, but I would appreciate also
getting told what the answer is so I can claim one as well.
I am figuring there has to be a better way then brute force for this
as it seems a very simplistic design and, in my limited knowledge,
looks like it should be easily reversible.
Please write me directly at [EMAIL PROTECTED] with any questions, or
perhaps the solution. I can also provide the original javascript if
that is helpful.
// //////////////////////////////////////////////////////////////////////////
// Defines (Conditional compilation)
// --------------------------------------------------------------------------
// #define __BYTE_ORDER __LLITTLE_ENDIAN
// 0 = Orginal code
// 1 = Optimizations
#define OPTIMIZATIONS 1
int Num_Iterations = 500000;
// int Num_Iterations = 1;
#define VER "2.1"
// Includes
// --------------------------------------------------------------------------
// Standard includes i.e. stdio.h
#include <iostream.h>
#include <stdio.h>
#include <math.h>
#include <time.h> // clock()
#include <string.h> // strlen()
//#include <fstream.h> //save log file - to be implemented.
// Defines (Macros)
// --------------------------------------------------------------------------
// Stupid MS compilers STILL dont define this !
// And, yes I'm using MSVC C/C++ IDE to write this ;-)
#ifndef M_PI
const double M_PI = 3.141592653589793;
#endif
// Consts (Public)
// --------------------------------------------------------------------------
const int FALSE = 0;
const int TRUE = 1;
// Types (Private)
// --------------------------------------------------------------------------
// If you're running this in a 32-bit environment,
// align single-chars on this boundary since odd memory accesses are slow.
// Use 4 bytes/char:
typedef long keytype;
// Use normal one byte/char:
// typedef unsigned short keytype;
// Use normal one byte/char:
// typedef unsigned char keytype;
// Globals (Public)
// --------------------------------------------------------------------------
// Consts (Private)
// --------------------------------------------------------------------------
// Must be a power of 2 !!!
const int KEY_LENGTH = 64;
const int LEN_LOGIN = 16;
const int LEN_PASSWORD = 4;
const int DICT_SIZE = 73;
const char HEX_STRING[ ] = "0123456789ABCDEF";
// The dictionary of allowed characters in the login/password.
const keytype Convert[ DICT_SIZE ] =
{
// 1..26 = a..z
// 27..36 = 0..9
// 37..43 = misc symbols
// 44..69 = A..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'
, '.','/',':',';','=','?','@'
,
'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'
, '~',' ','>'
};
// Unused ASCII chars:
// 33 !
// 34 "
// 35 #
// 36 $
// 37 %
// 38 &
// 39 '
// 40 (
// 41 )
// 42 *
// 43 +
// 44 ,
// 45 -
// 60 <
// 62 >
// 91 [
// 92 \
// 93 ]
// 94 ^
// 96 `
// 123 {
// 124 |
// 125 }
// Starting tables used to build the 2 keytables.
const keytype Code1[ KEY_LENGTH ] =
{
101, 107, 173, 67, 68, 0, 40, 150, 97, 103, 0, 0, 109, 114,
0, 111
, 16, 245, 108, 47, 116, 186, 213, 141, 46, 0, 32, 27, 0, 140,
104, 159
, 0, 62, 109, 46, 83, 0, 84, 108, 40, 128, 172, 7, 4, 0,
36, 207
, 18, 44, 222, 137, 116, 124, 208, 74, 153, 38, 149, 208, 0, 86,
67, 113
};
const keytype Code2[ KEY_LENGTH ] =
{
101, 108, 173, 205, 68, 0, 32, 71, 97, 103, 108, 97, 239,
0, 99, 0
, 102, 46, 0, 116, 20, 107, 103, 111, 46, 0, 0, 27, 12,
140, 104, 194
, 94, 90, 109, 46, 100, 0, 0, 108, 40, 0, 27, 103, 108,
30, 238, 190
, 18, 55, 176, 73, 74, 145, 208, 30, 153, 51, 36, 8, 83,
86, 56, 241
};
// Tests to make sure optimisizations dont break anything ;-)
// login: aaaaaaaaaaaaaaaa
// password: aaaa
const keytype Test_Output_1[ KEY_LENGTH ] =
{
0x0E, 0xD4, 0xAF, 0x7E, 0x77, 0x14, 0xA0, 0xCB
, 0xA9, 0x85, 0xBE, 0x20, 0x05, 0xBE, 0x74, 0x3B
, 0x61, 0x65, 0x29, 0x73, 0xBA, 0xDC, 0x49, 0xED
, 0x74, 0x8D, 0xCA, 0x56, 0x64, 0x8B, 0x40, 0xA1
, 0x71, 0x96, 0xB6, 0x6C, 0x20, 0xEE, 0x54, 0xD5
, 0x74, 0x61, 0x12, 0x00, 0x31, 0x8C, 0x00, 0xA0
, 0x36, 0xAD, 0x7B, 0x3F, 0x00, 0x7D, 0x5F, 0x7C
, 0x2F, 0xBA, 0xAC, 0xAC, 0x95, 0x6D, 0x00, 0x6E
};
// login: aaaaaaaaaaaaaaaa
// password: aaaa
const keytype Test_Output_2[ KEY_LENGTH ] =
{
0xCE, 0xE3, 0xC1, 0x60, 0x1F, 0x9A, 0xA0, 0xCC
, 0xA9, 0xFE, 0xBE, 0x00, 0x05, 0xCF, 0x29, 0x6F
, 0x6D, 0x65, 0xA9, 0x73, 0xEE, 0xDC, 0x49, 0xF6
, 0x14, 0x6F, 0xA6, 0x56, 0xBF, 0xEA, 0x36, 0x23
, 0xF1, 0x47, 0xB6, 0x00, 0x35, 0xE6, 0x54, 0x67
, 0x4A, 0x61, 0x12, 0x00, 0x31, 0x8C, 0x5E, 0xCF
, 0x36, 0xAD, 0xA3, 0x4D, 0x63, 0xB7, 0x3B, 0x91
, 0x74, 0x6B, 0x1B, 0xCC, 0x24, 0x6D, 0x53, 0x6E
};
// Globals (Private)
// --------------------------------------------------------------------------
// Globals for faster performance (avoids stack setup overhead)
keytype Key [ KEY_LENGTH ];
keytype Decrypt_1[ KEY_LENGTH ];
keytype Decrypt_2[ KEY_LENGTH ];
keytype Login [ KEY_LENGTH ];
keytype Password[ KEY_LENGTH ];
int Starting_Login [ LEN_LOGIN ];
int Starting_Password[ LEN_PASSWORD ];
// Prototypes (Private)
// --------------------------------------------------------------------------
// ==========================================================================
void Build_KeyTable( keytype *login, keytype *password, keytype *key );
// ==========================================================================
void DecodeURL( keytype *login, keytype *password );
// ==========================================================================
void Do_BruteForceCrack( void );
// ==========================================================================
void Do_DictionaryCrack( char *dict_filename );
// ==========================================================================
int GetDictCharOffset( keytype symbol );
// ==========================================================================
void GetStartingPosition( char *arg, int *Starting_Array, const int MAX_LENGTH
);
// ==========================================================================
void HexDumpDecrypt( keytype *decrypt );
// ==========================================================================
void Initialize( void );
// ==========================================================================
void Print_Login( void );
// ==========================================================================
void Print_Password( void );
// ==========================================================================
void Test_Decryption( void );
// Implementation
// --------------------------------------------------------------------------
// Builds the Key[0..63] table.
// ==========================================================================
void Build_KeyTable( keytype *login, keytype *password, keytype *key )
{
int key_index;
#if OPTIMIZATIONS
double temp = M_PI;
for (key_index = 0; key_index < KEY_LENGTH; key_index++)
{
temp = (double)((temp * login[key_index & 0xF]) + password[key_index &
0x3]);
if (temp >= (double)256.)
{
long div256 = (long)(temp / (double)256.);
temp = temp - ((double)256. * (double)div256);
}
key[key_index] = (keytype)temp;
}
#else
double temp = M_PI;
for (key_index = 0; key_index < KEY_LENGTH; key_index++)
{
temp = fmod( (double)((temp * login[key_index & 0xF]) +
password[key_index & 0x3]) , (double)256. );
key[key_index] = (keytype)temp;
}
#endif // OPTIMIZATIONS
}
// ==========================================================================
void DecodeURL (keytype *login, keytype *password)
{
register keytype swap;
int swap_index, index;
// Initialize the decrpyt tables
index = KEY_LENGTH;
while (--index >= 0)
{
Decrypt_1[ index ] = Code1[ index ];
Decrypt_2[ index ] = Code2[ index ];
}
Build_KeyTable( login, password, Key );
index = KEY_LENGTH;
while ( --index >= 0 )
{
swap_index = Key[ index ] & (KEY_LENGTH - 1);
if (swap_index == index)
{
swap_index = (swap_index + 1) & (KEY_LENGTH - 1);
}
swap = Decrypt_1[ swap_index ] ^ Key[index];
Decrypt_1[ swap_index ] = Decrypt_1[ index ];
Decrypt_1[ index ] = swap;
swap = Decrypt_2[ swap_index ] ^ Key[ index ];
Decrypt_2[ swap_index ] = Decrypt_2[ index ];
Decrypt_2[ index ] = swap;
}
}
// ==========================================================================
void Do_BruceForceCrack( void )
{
// All the following for loops where put verticly jsut so it wouldn't
// be anoying and only the inner loop has any code.
for (int a = Starting_Login[0]; a < DICT_SIZE;a++) //start of login
{
Starting_Login[0] = 1; // HACK!: a
Login[0] = Convert[ a ];
for (int b = Starting_Login[1]; b < DICT_SIZE;b++)
{
Starting_Login[1] = 1; // HACK!: a
Login[1] = Convert[ b ];
for (int c = Starting_Login[2]; c < DICT_SIZE;c++)
{
Starting_Login[2] = 1; // HACK!: a
Login[2] = Convert[ c ];
for (int d = Starting_Login[3];d < DICT_SIZE;d++)
{
Starting_Login[3] = 1; // HACK!: a
Login[3] = Convert[ d ];
for (int e = Starting_Login[4];e < DICT_SIZE;e++) //5
{
Starting_Login[4] = 1; // HACK!: a
Login[4] = Convert[ e ];
for (int f = Starting_Login[5];f < DICT_SIZE;f++)
{
Starting_Login[5] = 1; // HACK!: a
Login[5] = Convert[ f ];
for (int g = Starting_Login[6];g < DICT_SIZE;g++)
{
Starting_Login[6] = 1; // HACK!: a
Login[6] = Convert[ g ];
for (int h = Starting_Login[7];h < DICT_SIZE;h++)
{
Starting_Login[7] = 1; // HACK!: a
Login[7] = Convert[ h ];
for (int i = Starting_Login[8];i < DICT_SIZE;i++)
{
Starting_Login[8] = 1; // HACK!: a
Login[8] = Convert[ i ];
for (int j = Starting_Login[ 9 ]; j < DICT_SIZE;j++) //10
{
Starting_Login[9] = 1; // HACK!: a
Login[9] = Convert[ j ];
for (int k = Starting_Login[10]; k < DICT_SIZE;k++)
{
Starting_Login[10] = 1; // HACK!: a
Login[10] = Convert[ k ];
for (int l = Starting_Login[11]; l < DICT_SIZE;l++)
{
Starting_Login[11] = 1; // HACK!: a
Login[11] = Convert[ l ];
for (int m = Starting_Login[12]; m < DICT_SIZE;m++)
{
Starting_Login[12] = 1; // HACK!: a
Login[12] = Convert[ m ];
for (int n = Starting_Login[ 13 ];n < DICT_SIZE;n++)
{
Starting_Login[13] = 1; // HACK!: a
Login[13] = Convert[ n ];
for (int o = Starting_Login[ 14 ]; o < DICT_SIZE;o++)
{
Starting_Login[14] = 1; // HACK!: a
Login[14] = Convert[ o ];
for (int p = Starting_Login[ 15 ]; p < DICT_SIZE;p++)
//16 end of login
{
Starting_Login[15] = 1; // HACK!: a
Login[15] = Convert[ p ];
for (int q = Starting_Password[0]; q < DICT_SIZE;q++) //start of password
{
Starting_Password[0] = 1; // a
Password[0] = Convert[ q ];
for (int r = Starting_Password[1]; r < DICT_SIZE;r++)
{
Password[1] = Convert[ r ];
for (int s = Starting_Password[2]; s < DICT_SIZE;s++)
{
Password[2] = Convert[ s ];
for (int t = Starting_Password[3]; t < DICT_SIZE;t++)
//20 end of password
{
Password[3] = Convert[ t ];
//runs decode just like Real program
DecodeURL( Login, Password );
//Checks to see if we have any winners
if (
(Decrypt_1[0]=='h') &&
(Decrypt_1[1]=='t') && (Decrypt_1[2]=='t') &&
(Decrypt_1[3]=='p') &&
(Decrypt_1[4]==':') && (Decrypt_1[5]=='/') &&
(Decrypt_1[6]=='/'))
{
cout << "WINNER: ";
Print_Login();
cout << endl;
return;
}
if (
(Decrypt_2[0]=='h') &&
(Decrypt_2[1]=='t') && (Decrypt_2[2]=='t') &&
(Decrypt_2[3]=='p') &&
(Decrypt_2[4]==':') && (Decrypt_2[5]=='/') &&
(Decrypt_2[6]=='/'))
{
cout << "WINNER: ";
Print_Password();
cout << endl;
return;
}
}
Starting_Password[3] = 1; // HACK!: a
}
Starting_Password[2] = 1; // HACK!: a
}
Starting_Password[1] = 1; // HACK!: a
// Everytime the first char of the password changes,
// Spit out login/pasword (since using arrays now)
Print_Login();
cout << " ";
//cout << "*";
Print_Password();
//cout << "*";
cout << endl;
}
Starting_Password[0] = 1; // HACK!: a
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
// ==========================================================================
void Do_DictionaryCrack( char *dict_filename )
{
FILE *dict_file = NULL;
int clear;
int q = 0;
int r = 0;
int s = 0;
int t = 0;
dict_file = fopen( dict_filename, "rt" );
if (dict_file == NULL)
{
cout << "Couldnt open dictionary file: " << dict_filename << endl;
goto bail;
}
char line[ 128 ];
while( !feof( dict_file ) )
{
// Dont forget to read the '\n' !
fgets( line, 128, dict_file );
// Move the word into the login field.
int word_len = strlen( line );
if (word_len > LEN_LOGIN)
word_len = LEN_LOGIN;
for( clear = 0; clear < word_len; clear++ )
{
Login[ clear ] = line[ clear ];
}
// If word is too short, pad it with spaces
if (word_len < LEN_LOGIN)
{
for( clear = word_len; clear < LEN_LOGIN; clear++ )
{
Login[ clear ] = ' ';
}
}
// And do one round of passwords...
for ( q = Starting_Password[0]; q < DICT_SIZE;q++) //start of password
{
Starting_Password[0] = 1; // a
Password[0] = Convert[ q ];
for ( r = Starting_Password[1]; r < DICT_SIZE;r++)
{
Password[1] = Convert[ r ];
for ( s = Starting_Password[2]; s < DICT_SIZE;s++)
{
Password[2] = Convert[ s ];
for ( t = Starting_Password[3]; t <
DICT_SIZE;t++) //20 end of password
{
Password[3] = Convert[ t ];
//runs decode just like Real program
DecodeURL( Login, Password );
//Checks to see if we have any winners
if (
(Decrypt_1[0]=='h') &&
(Decrypt_1[1]=='t') && (Decrypt_1[2]=='t') &&
(Decrypt_1[3]=='p') &&
(Decrypt_1[4]==':') && (Decrypt_1[5]=='/') &&
(Decrypt_1[6]=='/'))
{
cout << "WINNER: ";
Print_Login();
cout << endl;
return;
}
if (
(Decrypt_2[0]=='h') &&
(Decrypt_2[1]=='t') && (Decrypt_2[2]=='t') &&
(Decrypt_2[3]=='p') &&
(Decrypt_2[4]==':') && (Decrypt_2[5]=='/') &&
(Decrypt_2[6]=='/'))
{
cout << "WINNER: ";
Print_Password();
cout << endl;
return;
}
}
Starting_Password[3] = 1; // HACK!: a
}
Starting_Password[2] = 1; // HACK!: a
}
Starting_Password[1] = 1; // HACK!: a
// Everytime the first char of the password changes,
// Spit out login/pasword (since using arrays now)
Print_Login();
cout << " ";
//cout << "*";
Print_Password();
cout << endl;
}
Starting_Password[0] = 1; // HACK!: a
}
bail:
if (dict_file != NULL)
{
fclose( dict_file );
dict_file = NULL;
}
}
// Returns the position of symble in table Convert, else 0 if not found.
// ==========================================================================
int GetDictCharOffset( keytype symbol )
{
int i = 0;
while (i++ < DICT_SIZE)
{
if (Convert[ i ] == symbol)
{
return i;
}
}
return 0;
}
// ==========================================================================
void GetStartingPosition( char *arg, int *Starting_Array, const int MAX_LENGTH )
{
int clear;
int arg_len = strlen( arg );
// Convert '_' to spaces
for( clear = 0; clear < arg_len; clear++ )
{
if (arg[ clear ] == '_')
arg[ clear ] = ' ';
}
if (arg_len > MAX_LENGTH )
arg_len = MAX_LENGTH;
// Convert char in ascii to offset in Dict table.
for( clear = 0; clear < arg_len; clear++ )
{
int position = GetDictCharOffset( arg[ clear ] );
if (!position)
position = GetDictCharOffset( ' ' );
Starting_Array[ clear ] = position;
}
// If starting login word is too short, pad it with spaces
if (arg_len < MAX_LENGTH)
{
for( clear = arg_len; clear < MAX_LENGTH; clear++ )
{
Starting_Array[ clear ] = GetDictCharOffset( ' ' );
}
}
}
// ==========================================================================
void HexDumpDecrypt( keytype *decrypt )
{
for( int i = 0; i < KEY_LENGTH; i++ )
{
if (!(i & 0xF))
cout << endl;
cout
<< (char)HEX_STRING[ (decrypt[ i ] >> 4) & 0xF ]
<< (char)HEX_STRING[ (decrypt[ i ] >> 0) & 0xF ]
<< " ";
}
}
// ==========================================================================
void Initialize( void )
{
int clear = KEY_LENGTH;
// Clear the tables
while ( --clear >= 0 )
{
Decrypt_1[ clear ] = 0;
Decrypt_2[ clear ] = 0;
Login [ clear ] = 0;
Password[ clear ] = 0;
}
// Default to:
// login: aaaaaaaaaaaaaaaa
// password: aaaa
for( clear = 0; clear < LEN_LOGIN; clear++ )
{
Starting_Login[ clear ] = GetDictCharOffset( 'a' );
}
for( clear = 0; clear < LEN_PASSWORD; clear++ )
{
Starting_Password[ clear ] = GetDictCharOffset( 'a' );
}
}
// ==========================================================================
void Print_Login( void )
{
int clear;
for (clear = 0; clear < LEN_LOGIN; clear++ )
{
cout << (char)Login[ clear ];
}
}
// ==========================================================================
void Print_Password( void )
{
int clear;
for (clear = 0; clear < LEN_PASSWORD; clear++ )
{
cout << (char)Password[ clear ];
}
}
// ==========================================================================
void Test_Decryption( void )
{
cout << "Testing/Benchmarking" << endl;
int i;
int ok = TRUE;
// Default to:
// login: aaaaaaaaaaaaaaaa
// password: aaaa
for( i = 0; i < LEN_LOGIN; i++ )
{
Login[ i ] = Convert[ Starting_Login[ i ] ];
}
for( i = 0; i < LEN_PASSWORD; i++ )
{
Password[ i ] = Convert[ Starting_Password[ i ] ];
}
// And benchmark the puppy...
clock_t start = clock();
for( i = 0; i < Num_Iterations; i++ )
{
DecodeURL( Login, Password );
}
clock_t end = clock();
double elapsed = (float)difftime( end, start );
double secs = elapsed / (float)CLOCKS_PER_SEC;
cout << "Time elapsed: " << secs << " seconds." << endl;
cout << "Decrypts / second: " << ((double)Num_Iterations) / secs << endl;
cout << endl;
ok = true;
// Test to make sure optimisizations dont break anything ;-)
ok = true;
cout << "Decrypt_1: ";
HexDumpDecrypt( Decrypt_1 );
cout << endl;
for( i = 0; i < KEY_LENGTH; i++ )
{
if (Decrypt_1[ i ] != Test_Output_1[ i ])
{
ok = false;
break;
}
}
if ( ok )
cout << "Decryption passed." << endl;
else
cout << "Decryption FAILED!" << endl;
cout << endl;
// Test to make sure optimisizations dont break anything ;-)
ok = true;
cout << "Decrypt_2: ";
HexDumpDecrypt( Decrypt_2 );
cout << endl;
for( i = 0; i < KEY_LENGTH; i++ )
{
if (Decrypt_2[ i ] != Test_Output_2[ i ])
{
ok = false;
break;
}
}
if ( ok )
cout << "Decryption passed." << endl;
else
cout << "Decryption FAILED!" << endl;
getchar();
}
// ==========================================================================
void main( int num_args, char *arg[] )
{
cout << "Kipling crack program, " __DATE__ ", Verison: " VER << endl;
Initialize();
int i = 1;
while ( i < num_args )
{
if (!strcmp( arg[ i ], "-bench" ))
{
Test_Decryption();
return;
}
else
if (!strcmp( arg[ i ], "-help" ))
{
cout <<
"progname [starting_login [starting_password]]
[-bench] [-dict dictionary_fileneme]\n"
"\n"
"-bench Displays decrypts/sec\n"
"-dict filename Uses the specified filename as a
dictionary of words\n"
"\n"
"Default is to brute force crack with\n"
"starting_login defaulting to 'aaaaaaaaaaaaaaaa'\n"
"starting_password defaulting to 'aaaa'\n"
"\n"
<< endl;
return;
}
else
if (!strcmp( arg[ i ], "-dict" ))
{
i++;
if (i < num_args)
{
cout << "Using specified dictionary file: " << arg[ i
] << endl;
Do_DictionaryCrack( arg[ i ] );
}
else
cout << "No dictionary file given !" << endl;
return;
}
i++;
}
// Do we have a starting point specified on the command-line?
if (num_args > 1)
{
cout << "Using specified starting login: " << arg[ 1 ] << endl;
GetStartingPosition( arg[ 1 ], Starting_Login, LEN_LOGIN );
if (num_args > 2)
{
cout << "Using specified starting password: " << arg[ 2 ] <<
endl;
GetStartingPosition( arg[ 2 ], Starting_Password, LEN_PASSWORD
);
}
}
Do_BruceForceCrack();
}
------------------------------
** FOR YOUR REFERENCE **
The service address, to which questions about the list itself and requests
to be added to or deleted from it should be directed, is:
Internet: [EMAIL PROTECTED]
You can send mail to the entire list (and sci.crypt) via:
Internet: [EMAIL PROTECTED]
End of Cryptography-Digest Digest
******************************